diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index ed839d8505e..00000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Debug scenario", - "type": "go", - "request": "launch", - "mode": "test", - "program": "${workspaceFolder}/core/integration/main_test.go", - "_comment": " * * * change to your .feature file * * * ", - "args": [ - "--godog.format", - "pretty", - "${workspaceFolder}/core/integration/features/liquidity-provision/0044-LIME-062.feature" - ] - } - ] -} diff --git a/CHANGELOG.md b/CHANGELOG.md index 7c7ee588a13..7d59825bd9d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,10 @@ # Changelog -## Unreleased 0.78.0 +## Unreleased 0.79.0 ### 🚨 Breaking changes -- [](https://github.com/vegaprotocol/vega/issues/xxx) +- [8777](https://github.com/vegaprotocol/vega/issues/8777) - Stop orders can now be used to create or increase a position. ### 🗑️ Deprecation @@ -19,12 +19,57 @@ - [](https://github.com/vegaprotocol/vega/issues/xxx) +## 0.78.0 + +### 🛠 Improvements + +- [11428](https://github.com/vegaprotocol/vega/issues/11428) - Add buy back and treasury fee and separate discount/reward factors. +- [11468](https://github.com/vegaprotocol/vega/issues/11468) - Added support for volume rebate program. +- [11523](https://github.com/vegaprotocol/vega/issues/11523) - Change method of caching to improve `AMM` snapshot performance. +- [11426](https://github.com/vegaprotocol/vega/issues/11426) - `EstimateAMMBounds` now reports issues with commitment. +- [11459](https://github.com/vegaprotocol/vega/issues/11459) - Deprecate time weight position reward metric and replace it with time weighted notional. +- [11528](https://github.com/vegaprotocol/vega/issues/11528) - Added support for eligible entities reward. +- [11372](https://github.com/vegaprotocol/vega/issues/11372) - Support combined filters for the `AMM` API. +- [11583](https://github.com/vegaprotocol/vega/issues/11583) - Keep `AMM` ephemeral position throughout entire auction uncrossing. +- [11535](https://github.com/vegaprotocol/vega/issues/11535) - Added support for lottery rank distribution strategy. +- [11536](https://github.com/vegaprotocol/vega/issues/11536) - Make the batch market instructions errors programmatically usable. +- [11546](https://github.com/vegaprotocol/vega/issues/11546) - Add validation to market proposals metadata. +- [11562](https://github.com/vegaprotocol/vega/issues/11562) - Update average notional metric with mark price at the end of the epoch and when calculating live score. +- [11570](https://github.com/vegaprotocol/vega/issues/11570) - Include the required set of parties for evaluation for eligible entities reward. +- [11576](https://github.com/vegaprotocol/vega/issues/11576) - Replace additional rebate validation with a cap. +- [11533](https://github.com/vegaprotocol/vega/issues/11533) - Suppose per party fee discounts in fee estimation. +- [11577](https://github.com/vegaprotocol/vega/issues/11577) - Add API for party discounts and rewards. +- [10716](https://github.com/vegaprotocol/vega/issues/10716) - Set Tendermint defaults during init. +- [11624](https://github.com/vegaprotocol/vega/issues/11624) - prevent creation of rewards with no payout, but with high computational cost. + +### 🐛 Fixes + +- [11521](https://github.com/vegaprotocol/vega/issues/11521) - Restore `AMM` position factor when loading from a snapshot. +- [11526](https://github.com/vegaprotocol/vega/issues/11526) - `EstimateAMMBounds` now respects the market's decimal places. +- [11486](https://github.com/vegaprotocol/vega/issues/11486) - `AMMs` can now be submitted on markets with more decimal places than asset decimal places. +- [11561](https://github.com/vegaprotocol/vega/issues/11561) - Failing amends on `AMMs` now restore original properly. +- [11583](https://github.com/vegaprotocol/vega/issues/11583) - Remove `AMMs` entirely from engine when market closes. +- [11568](https://github.com/vegaprotocol/vega/issues/11568) - order book shape on closing `AMM` no longer panics. +- [11540](https://github.com/vegaprotocol/vega/issues/11540) - Fix spam check for spots to use not double count quantum. +- [11542](https://github.com/vegaprotocol/vega/issues/11542) - Fix non determinism in lottery ranking. +- [11616](https://github.com/vegaprotocol/vega/issues/11616) - `AMM` tradable volume now calculated purely in positions to prevent loss of precision. +- [11544](https://github.com/vegaprotocol/vega/issues/11544) - Fix empty candles stream. +- [11619](https://github.com/vegaprotocol/vega/issues/11619) - Fix `EstimatePositions` API for capped futures. +- [11579](https://github.com/vegaprotocol/vega/issues/11579) - Spot calculate fee on amend, use order price if no amended price is provided. +- [11585](https://github.com/vegaprotocol/vega/issues/11585) - Initialise rebate stats service in API. +- [11592](https://github.com/vegaprotocol/vega/issues/11592) - Fix the order of calls at end of epoch between rebate engine and market tracker. +- [11607](https://github.com/vegaprotocol/vega/issues/11607) - Wire rank lottery distribution to team reward payout. +- [959](https://github.com/vegaprotocol/core-test-coverage/issues/959) - Include `ELS` for `AMM` sub keys to the parent key `ELS`. +- [11592](https://github.com/vegaprotocol/vega/issues/11592) - Fix the order of calls at end of epoch between rebate engine and market tracker. +- [10907](https://github.com/vegaprotocol/vega/issues/10907) - Fix position API distressed status not getting updated once the party has been closed out. + + ## 0.77.5 ### 🐛 Fixes - [11513](https://github.com/vegaprotocol/vega/issues/11513) - Rollback CometBFT to version `v0.38.8`. - +- [11516](https://github.com/vegaprotocol/vega/issues/11516) - Fix order spam check for amends. ## 0.77.4 @@ -32,6 +77,7 @@ - [11508](https://github.com/vegaprotocol/vega/issues/11508) - Handle market order for margin spam protection. - [11507](https://github.com/vegaprotocol/vega/issues/11507) - Margin spam check to remove division by asset quantum. +- [11504](https://github.com/vegaprotocol/vega/issues/11504) - Fix bug causing panic when accounting for volume rounding with AMM orders. ## 0.77.3 diff --git a/cmd/data-node/commands/start/node.go b/cmd/data-node/commands/start/node.go index a71d04c0e90..395dcd52d4d 100644 --- a/cmd/data-node/commands/start/node.go +++ b/cmd/data-node/commands/start/node.go @@ -242,6 +242,8 @@ func (l *NodeCommand) createGRPCServer(config api.Config) *api.GRPCServer { l.timeWeightedNotionalPositionService, l.gameScoreService, l.ammPoolsService, + l.volumeRebateStatsService, + l.volumeRebateProgramService, ) return grpcServer } diff --git a/cmd/data-node/commands/start/sqlsubscribers.go b/cmd/data-node/commands/start/sqlsubscribers.go index f381571e78a..87903ce48a6 100644 --- a/cmd/data-node/commands/start/sqlsubscribers.go +++ b/cmd/data-node/commands/start/sqlsubscribers.go @@ -84,6 +84,8 @@ type SQLSubscribers struct { timeWeightedNotionalPositionStore *sqlstore.TimeWeightedNotionalPosition gameScoreStore *sqlstore.GameScores ammPoolsStore *sqlstore.AMMPools + volumeRebateStatsStore *sqlstore.VolumeRebateStats + volumeRebateProgramsStore *sqlstore.VolumeRebatePrograms // Services candleService *candlesv2.Svc @@ -142,6 +144,8 @@ type SQLSubscribers struct { timeWeightedNotionalPositionService *service.TimeWeightedNotionalPosition gameScoreService *service.GameScore ammPoolsService *service.AMMPools + volumeRebateStatsService *service.VolumeRebateStats + volumeRebateProgramService *service.VolumeRebatePrograms // Subscribers accountSub *sqlsubscribers.Account @@ -196,6 +200,8 @@ type SQLSubscribers struct { timeWeightedNotionalPositionSub *sqlsubscribers.TimeWeightedNotionalPosition gameScoreSub *sqlsubscribers.GameScore ammPoolsSub *sqlsubscribers.AMMPools + volumeRebateStatsSub *sqlsubscribers.VolumeRebateStatsUpdated + volumeRebateProgramSub *sqlsubscribers.VolumeRebateProgram } func (s *SQLSubscribers) GetSQLSubscribers() []broker.SQLBrokerSubscriber { @@ -254,6 +260,8 @@ func (s *SQLSubscribers) GetSQLSubscribers() []broker.SQLBrokerSubscriber { s.timeWeightedNotionalPositionSub, s.gameScoreSub, s.ammPoolsSub, + s.volumeRebateProgramSub, + s.volumeRebateStatsSub, } } @@ -317,6 +325,8 @@ func (s *SQLSubscribers) CreateAllStores(ctx context.Context, Log *logging.Logge s.timeWeightedNotionalPositionStore = sqlstore.NewTimeWeightedNotionalPosition(transactionalConnectionSource) s.gameScoreStore = sqlstore.NewGameScores(transactionalConnectionSource) s.ammPoolsStore = sqlstore.NewAMMPools(transactionalConnectionSource) + s.volumeRebateStatsStore = sqlstore.NewVolumeRebateStats(transactionalConnectionSource) + s.volumeRebateProgramsStore = sqlstore.NewVolumeRebatePrograms(transactionalConnectionSource) } func (s *SQLSubscribers) SetupServices(ctx context.Context, log *logging.Logger, cfg service.Config, candlesConfig candlesv2.Config) error { @@ -374,6 +384,8 @@ func (s *SQLSubscribers) SetupServices(ctx context.Context, log *logging.Logger, s.timeWeightedNotionalPositionService = service.NewTimeWeightedNotionalPosition(s.timeWeightedNotionalPositionStore) s.gameScoreService = service.NewGameScore(s.gameScoreStore, log) s.ammPoolsService = service.NewAMMPools(s.ammPoolsStore) + s.volumeRebateStatsService = service.NewVolumeRebateStats(s.volumeRebateStatsStore) + s.volumeRebateProgramService = service.NewVolumeRebatePrograms(s.volumeRebateProgramsStore) s.marketDepthService = service.NewMarketDepth( cfg.MarketDepth, @@ -455,5 +467,7 @@ func (s *SQLSubscribers) SetupSQLSubscribers() { s.marginModesSub = sqlsubscribers.NewMarginModes(s.marginModesService) s.timeWeightedNotionalPositionSub = sqlsubscribers.NewTimeWeightedNotionalPosition(s.timeWeightedNotionalPositionService) s.gameScoreSub = sqlsubscribers.NewGameScore(s.gameScoreStore) + s.volumeRebateStatsSub = sqlsubscribers.NewVolumeRebateStatsUpdated(s.volumeRebateStatsService) + s.volumeRebateProgramSub = sqlsubscribers.NewVolumeRebateProgram(s.volumeRebateProgramService) s.ammPoolsSub = sqlsubscribers.NewAMMPools(s.ammPoolsService, s.marketDepthService) } diff --git a/cmd/vega/commands/init.go b/cmd/vega/commands/init.go index 1e9537ad758..1b15750733b 100644 --- a/cmd/vega/commands/init.go +++ b/cmd/vega/commands/init.go @@ -20,6 +20,7 @@ import ( "errors" "fmt" "os" + "path/filepath" "time" "code.vegaprotocol.io/vega/core/config" @@ -127,7 +128,19 @@ func (opts *InitCmd) Execute(args []string) error { if !initCmd.NoTendermint { tmCfg := tmcfg.DefaultConfig() tmCfg.SetRoot(os.ExpandEnv(initCmd.TendermintHome)) + // add a few defaults + tmCfg.P2P.MaxPacketMsgPayloadSize = 16384 + tmCfg.P2P.SendRate = 20000000 + tmCfg.P2P.RecvRate = 20000000 + tmCfg.Mempool.Size = 10000 + tmCfg.Mempool.CacheSize = 20000 + tmCfg.Consensus.TimeoutCommit = 0 * time.Second + tmCfg.Consensus.SkipTimeoutCommit = true + tmCfg.Consensus.CreateEmptyBlocksInterval = 1 * time.Second + tmCfg.Storage.DiscardABCIResponses = true tmcfg.EnsureRoot(tmCfg.RootDir) + // then rewrite the config to apply the changes, EnsureRoot create the config, but with a default config + tmcfg.WriteConfigFile(filepath.Join(tmCfg.RootDir, tmcfg.DefaultConfigDir, tmcfg.DefaultConfigFileName), tmCfg) if err := initTendermintConfiguration(output, logger, tmCfg); err != nil { return fmt.Errorf("couldn't initialise tendermint %w", err) } diff --git a/cmd/vega/commands/verify/genesis_test.go b/cmd/vega/commands/verify/genesis_test.go index d68e88cbebc..a559def044d 100644 --- a/cmd/vega/commands/verify/genesis_test.go +++ b/cmd/vega/commands/verify/genesis_test.go @@ -24,7 +24,6 @@ import ( "code.vegaprotocol.io/vega/cmd/vega/commands/verify" "code.vegaprotocol.io/vega/core/assets" "code.vegaprotocol.io/vega/core/genesis" - "code.vegaprotocol.io/vega/core/netparams" "code.vegaprotocol.io/vega/core/validators" "github.com/stretchr/testify/assert" @@ -151,13 +150,6 @@ func testVerifyNetworkParams(t *testing.T) { gs = genesis.DefaultState() gs.NetParamsOverwrite = []string{"NOTREAL"} assert.Error(t, cmd.Execute([]string{writeGenesisFileWithState(t, gs)})) - - // Check for deprecated parameter in genesis - gs = genesis.DefaultState() - for k := range netparams.Deprecated { - gs.NetParams[k] = "hello" - } - assert.Error(t, cmd.Execute([]string{writeGenesisFileWithState(t, gs)})) } func TestVerifyValidators(t *testing.T) { diff --git a/commands/create_referral_set.go b/commands/create_referral_set.go index 9e76f2f7033..1467c39939e 100644 --- a/commands/create_referral_set.go +++ b/commands/create_referral_set.go @@ -15,7 +15,11 @@ package commands -import commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" +import ( + "errors" + + commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" +) func CheckCreateReferralSet(cmd *commandspb.CreateReferralSet) error { return checkCreateReferralSet(cmd).ErrorOrNil() @@ -28,6 +32,14 @@ func checkCreateReferralSet(cmd *commandspb.CreateReferralSet) Errors { return errs.FinalAddForProperty("create_referral_set", ErrIsRequired) } + // Basically this command should be rejected if we are not creating a team + // but also not creating a referral set... + // just check if this command is ineffective... + if cmd.DoNotCreateReferralSet && !cmd.IsTeam { + return errs.FinalAddForProperty("create_referral_set", + errors.New("is ineffective")) + } + if cmd.IsTeam { if cmd.Team == nil { return errs.FinalAddForProperty("create_referral_set.team", ErrIsRequired) diff --git a/commands/errors.go b/commands/errors.go index dfdb3fb1174..4ce8df2508d 100644 --- a/commands/errors.go +++ b/commands/errors.go @@ -33,6 +33,7 @@ var ( ErrMustBeLessThan150 = errors.New("must be less than 150") ErrMustBeAtMost1M = errors.New("must be at most 1000000") ErrMustBeAtMost100 = errors.New("must be at most 100") + ErrMustBeAtMost2048 = errors.New("must be at most 2048") ErrMustBeWithinRange7 = errors.New("must be between -7 and 7") ErrIsNotValid = errors.New("is not a valid value") ErrIsNotValidWithOCO = errors.New("is not a valid with one cancel other") diff --git a/commands/proposal_submission.go b/commands/proposal_submission.go index 1e6b2d8c8fa..d6c268ca176 100644 --- a/commands/proposal_submission.go +++ b/commands/proposal_submission.go @@ -48,11 +48,12 @@ var validTransfers = map[protoTypes.AccountType]map[protoTypes.AccountType]struc protoTypes.AccountType_ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS: {}, - protoTypes.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION: {}, + protoTypes.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN: {}, + protoTypes.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES: {}, }, protoTypes.AccountType_ACCOUNT_TYPE_INSURANCE: { protoTypes.AccountType_ACCOUNT_TYPE_GENERAL: {}, @@ -64,11 +65,12 @@ var validTransfers = map[protoTypes.AccountType]map[protoTypes.AccountType]struc protoTypes.AccountType_ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS: {}, - protoTypes.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION: {}, + protoTypes.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN: {}, + protoTypes.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES: {}, }, protoTypes.AccountType_ACCOUNT_TYPE_GLOBAL_INSURANCE: { protoTypes.AccountType_ACCOUNT_TYPE_GENERAL: {}, @@ -79,11 +81,12 @@ var validTransfers = map[protoTypes.AccountType]map[protoTypes.AccountType]struc protoTypes.AccountType_ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS: {}, - protoTypes.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION: {}, + protoTypes.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING: {}, protoTypes.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN: {}, + protoTypes.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES: {}, }, } @@ -211,6 +214,8 @@ func checkProposalChanges(terms *protoTypes.ProposalTerms) Errors { errs.Merge(checkUpdateReferralProgram(terms, c)) case *protoTypes.ProposalTerms_UpdateVolumeDiscountProgram: errs.Merge(checkVolumeDiscountProgram(terms, c)) + case *protoTypes.ProposalTerms_UpdateVolumeRebateProgram: + errs.Merge(checkVolumeRebateProgram(terms, c)) default: return errs.FinalAddForProperty("proposal_submission.terms.change", ErrIsNotValid) } @@ -406,6 +411,19 @@ func checkReferralProgramChanges(changes *vegapb.ReferralProgramChanges, enactme return errs } +func checkVolumeRebateProgram(terms *vegapb.ProposalTerms, change *vegapb.ProposalTerms_UpdateVolumeRebateProgram) Errors { + errs := NewErrors() + if change.UpdateVolumeRebateProgram == nil { + return errs.FinalAddForProperty("proposal_submission.terms.change.update_volume_rebate_program", ErrIsRequired) + } + if change.UpdateVolumeRebateProgram.Changes == nil { + return errs.FinalAddForProperty("proposal_submission.terms.change.update_volume_rebate_program.changes", ErrIsRequired) + } + + return checkVolumeRebateProgramChanges(change.UpdateVolumeRebateProgram.Changes, terms.EnactmentTimestamp). + AddPrefix("proposal_submission.terms.change.") +} + func checkVolumeDiscountProgram(terms *vegapb.ProposalTerms, change *vegapb.ProposalTerms_UpdateVolumeDiscountProgram) Errors { errs := NewErrors() if change.UpdateVolumeDiscountProgram == nil { @@ -419,6 +437,54 @@ func checkVolumeDiscountProgram(terms *vegapb.ProposalTerms, change *vegapb.Prop AddPrefix("proposal_submission.terms.change.") } +func checkVolumeRebateProgramChanges(changes *vegapb.VolumeRebateProgramChanges, enactmentTimestamp int64) Errors { + errs := NewErrors() + + if changes.EndOfProgramTimestamp == 0 { + errs.AddForProperty("update_volume_rebate_program.changes.end_of_program_timestamp", ErrIsRequired) + } else if changes.EndOfProgramTimestamp < 0 { + errs.AddForProperty("update_volume_rebate_program.changes.end_of_program_timestamp", ErrMustBePositive) + } else if changes.EndOfProgramTimestamp < enactmentTimestamp { + errs.AddForProperty("update_volume_rebate_program.changes.end_of_program_timestamp", ErrMustBeGreaterThanEnactmentTimestamp) + } + if changes.WindowLength == 0 { + errs.AddForProperty("update_volume_rebate_program.changes.window_length", ErrIsRequired) + } else if changes.WindowLength > 100 { + errs.AddForProperty("update_volume_rebate_program.changes.window_length", ErrMustBeAtMost100) + } + for i, tier := range changes.BenefitTiers { + errs.Merge(checkVolumeRebateBenefitTier(i, tier)) + } + + return errs +} + +func checkVolumeRebateBenefitTier(index int, tier *vegapb.VolumeRebateBenefitTier) Errors { + errs := NewErrors() + propertyPath := fmt.Sprintf("update_volume_discount_program.changes.benefit_tiers.%d", index) + if len(tier.MinimumPartyMakerVolumeFraction) == 0 { + errs.AddForProperty(propertyPath+".minimum_party_maker_volume_fraction", ErrIsRequired) + } else { + mrtv, err := num.DecimalFromString(tier.MinimumPartyMakerVolumeFraction) + if err != nil { + errs.AddForProperty(propertyPath+".minimum_party_maker_volume_fraction", ErrIsNotValidNumber) + } else if mrtv.IsNegative() || mrtv.IsZero() { + errs.AddForProperty(propertyPath+".minimum_party_maker_volume_fraction", ErrMustBePositive) + } + } + if len(tier.AdditionalMakerRebate) == 0 { + errs.AddForProperty(propertyPath+".additional_maker_rebate", ErrIsRequired) + } else { + rdf, err := num.DecimalFromString(tier.AdditionalMakerRebate) + if err != nil { + errs.AddForProperty(propertyPath+".additional_maker_rebate", ErrIsNotValidNumber) + } else if rdf.IsNegative() { + errs.AddForProperty(propertyPath+".additional_maker_rebate", ErrMustBePositiveOrZero) + } + } + return errs +} + func checkVolumeDiscountProgramChanges(changes *vegapb.VolumeDiscountProgramChanges, enactmentTimestamp int64) Errors { errs := NewErrors() @@ -454,14 +520,26 @@ func checkVolumeBenefitTier(index int, tier *vegapb.VolumeBenefitTier) Errors { errs.AddForProperty(propertyPath+".minimum_running_notional_taker_volume", ErrMustBePositive) } } - if len(tier.VolumeDiscountFactor) == 0 { - errs.AddForProperty(propertyPath+".volume_discount_factor", ErrIsRequired) + if tier.VolumeDiscountFactors == nil { + errs.AddForProperty(propertyPath+".volume_discount_factors", ErrIsRequired) } else { - rdf, err := num.DecimalFromString(tier.VolumeDiscountFactor) + rdf, err := num.DecimalFromString(tier.VolumeDiscountFactors.MakerDiscountFactor) + if err != nil { + errs.AddForProperty(propertyPath+".volume_discount_factors.maker_discount_factor", ErrIsNotValidNumber) + } else if rdf.IsNegative() { + errs.AddForProperty(propertyPath+".volume_discount_factors.maker_discount_factor", ErrMustBePositiveOrZero) + } + rdf, err = num.DecimalFromString(tier.VolumeDiscountFactors.LiquidityDiscountFactor) + if err != nil { + errs.AddForProperty(propertyPath+".volume_discount_factors.liquidity_discount_factor", ErrIsNotValidNumber) + } else if rdf.IsNegative() { + errs.AddForProperty(propertyPath+".volume_discount_factors.liquidity_discount_factor", ErrMustBePositiveOrZero) + } + rdf, err = num.DecimalFromString(tier.VolumeDiscountFactors.InfrastructureDiscountFactor) if err != nil { - errs.AddForProperty(propertyPath+".volume_discount_factor", ErrIsNotValidNumber) + errs.AddForProperty(propertyPath+".volume_discount_factors.infrastructure_discount_factor", ErrIsNotValidNumber) } else if rdf.IsNegative() { - errs.AddForProperty(propertyPath+".volume_discount_factor", ErrMustBePositiveOrZero) + errs.AddForProperty(propertyPath+".volume_discount_factors.infrastructure_discount_factor", ErrMustBePositiveOrZero) } } return errs @@ -494,25 +572,73 @@ func checkBenefitTier(index int, tier *vegapb.BenefitTier) Errors { } } - if len(tier.ReferralRewardFactor) == 0 { - errs.AddForProperty(propertyPath+".referral_reward_factor", ErrIsRequired) + if tier.ReferralRewardFactors == nil { + errs.AddForProperty(propertyPath+".referral_reward_factors", ErrIsRequired) } else { - rrf, err := num.DecimalFromString(tier.ReferralRewardFactor) - if err != nil { - errs.AddForProperty(propertyPath+".referral_reward_factor", ErrIsNotValidNumber) - } else if rrf.IsNegative() { - errs.AddForProperty(propertyPath+".referral_reward_factor", ErrMustBePositiveOrZero) + if len(tier.ReferralRewardFactors.InfrastructureRewardFactor) == 0 { + errs.AddForProperty(propertyPath+".referral_reward_factors.infrastructure_reward_factor", ErrIsRequired) + } else { + rrf, err := num.DecimalFromString(tier.ReferralRewardFactors.InfrastructureRewardFactor) + if err != nil { + errs.AddForProperty(propertyPath+".referral_reward_factors.infrastructure_reward_factor", ErrIsNotValidNumber) + } else if rrf.IsNegative() { + errs.AddForProperty(propertyPath+".referral_reward_factors.infrastructure_reward_factor", ErrMustBePositiveOrZero) + } + } + if len(tier.ReferralRewardFactors.MakerRewardFactor) == 0 { + errs.AddForProperty(propertyPath+".referral_reward_factors.maker_reward_factor", ErrIsRequired) + } else { + rrf, err := num.DecimalFromString(tier.ReferralRewardFactors.MakerRewardFactor) + if err != nil { + errs.AddForProperty(propertyPath+".referral_reward_factors.maker_reward_factor", ErrIsNotValidNumber) + } else if rrf.IsNegative() { + errs.AddForProperty(propertyPath+".referral_reward_factors.maker_reward_factor", ErrMustBePositiveOrZero) + } + } + if len(tier.ReferralRewardFactors.LiquidityRewardFactor) == 0 { + errs.AddForProperty(propertyPath+".referral_reward_factors.liquidity_reward_factor", ErrIsRequired) + } else { + rrf, err := num.DecimalFromString(tier.ReferralRewardFactors.LiquidityRewardFactor) + if err != nil { + errs.AddForProperty(propertyPath+".referral_reward_factors.liquidity_reward_factor", ErrIsNotValidNumber) + } else if rrf.IsNegative() { + errs.AddForProperty(propertyPath+".referral_reward_factors.liquidity_reward_factor", ErrMustBePositiveOrZero) + } } } - if len(tier.ReferralDiscountFactor) == 0 { - errs.AddForProperty(propertyPath+".referral_discount_factor", ErrIsRequired) + if tier.ReferralDiscountFactors == nil { + errs.AddForProperty(propertyPath+".referral_discount_factors", ErrIsRequired) } else { - rdf, err := num.DecimalFromString(tier.ReferralDiscountFactor) - if err != nil { - errs.AddForProperty(propertyPath+".referral_discount_factor", ErrIsNotValidNumber) - } else if rdf.IsNegative() { - errs.AddForProperty(propertyPath+".referral_discount_factor", ErrMustBePositiveOrZero) + if len(tier.ReferralDiscountFactors.InfrastructureDiscountFactor) == 0 { + errs.AddForProperty(propertyPath+".referral_discount_factors.infrastructure_discount_factor", ErrIsRequired) + } else { + rrf, err := num.DecimalFromString(tier.ReferralDiscountFactors.InfrastructureDiscountFactor) + if err != nil { + errs.AddForProperty(propertyPath+".referral_discount_factors.infrastructure_discount_factor", ErrIsNotValidNumber) + } else if rrf.IsNegative() { + errs.AddForProperty(propertyPath+".referral_discount_factors.infrastructure_discount_factor", ErrMustBePositiveOrZero) + } + } + if len(tier.ReferralDiscountFactors.MakerDiscountFactor) == 0 { + errs.AddForProperty(propertyPath+".referral_discount_factors.maker_discount_factor", ErrIsRequired) + } else { + rrf, err := num.DecimalFromString(tier.ReferralDiscountFactors.MakerDiscountFactor) + if err != nil { + errs.AddForProperty(propertyPath+".referral_discount_factors.maker_discount_factor", ErrIsNotValidNumber) + } else if rrf.IsNegative() { + errs.AddForProperty(propertyPath+".referral_discount_factors.maker_discount_factor", ErrMustBePositiveOrZero) + } + } + if len(tier.ReferralDiscountFactors.LiquidityDiscountFactor) == 0 { + errs.AddForProperty(propertyPath+".referral_discount_factors.liquidity_discount_factor", ErrIsRequired) + } else { + rrf, err := num.DecimalFromString(tier.ReferralDiscountFactors.LiquidityDiscountFactor) + if err != nil { + errs.AddForProperty(propertyPath+".referral_discount_factors.liquidity_discount_factor", ErrIsNotValidNumber) + } else if rrf.IsNegative() { + errs.AddForProperty(propertyPath+".referral_discount_factors.liquidity_discount_factor", ErrMustBePositiveOrZero) + } } } @@ -680,11 +806,12 @@ func checkNewTransferConfiguration(changes *vegapb.NewTransferConfiguration) Err changes.DestinationType == vega.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES || changes.DestinationType == vega.AccountType_ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES || changes.DestinationType == vega.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS || - changes.DestinationType == vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION || + changes.DestinationType == vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL || changes.DestinationType == vega.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN || changes.DestinationType == vega.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY || changes.DestinationType == vega.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING || - changes.DestinationType == vega.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN { + changes.DestinationType == vega.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN || + changes.DestinationType == vega.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES { errs.AddForProperty("new_transfer.changes.destination_type", ErrIsNotValid) } if oneoff.DeliverOn < 0 { @@ -886,6 +1013,8 @@ func checkNewSpotMarketConfiguration(changes *vegapb.NewSpotMarketConfiguration) isCorrectProduct := false + errs.Merge(checkMetadata(changes, "new_market.changes")) + if changes.Instrument == nil { return errs.FinalAddForProperty("new_spot_market.changes.instrument", ErrIsRequired) } @@ -936,9 +1065,32 @@ func checkNewMarketChanges(change *protoTypes.ProposalTerms_NewMarket) Errors { return checkNewMarketChangesConfiguration(change.NewMarket.Changes).AddPrefix("proposal_submission.terms.change.") } +func checkMetadata( + m interface{ GetMetadata() []string }, + pre string, +) Errors { + errs := NewErrors() + + meta := m.GetMetadata() + + if len(meta) > 100 { + errs.AddForProperty(fmt.Sprintf("%s.metadata", pre), ErrMustBeAtMost100) + } + + for i, v := range meta { + if len(v) > 2048 { + errs.AddForProperty(fmt.Sprintf("%s.metadata.%d", pre, i), ErrMustBeAtMost2048) + } + } + + return errs +} + func checkNewMarketChangesConfiguration(changes *vegapb.NewMarketConfiguration) Errors { errs := NewErrors() + errs.Merge(checkMetadata(changes, "new_market.changes")) + if changes.DecimalPlaces >= 150 { errs.AddForProperty("new_market.changes.decimal_places", ErrMustBeLessThan150) } diff --git a/commands/proposal_submission_new_transfer_test.go b/commands/proposal_submission_new_transfer_test.go index 9ba54244326..02a026f0737 100644 --- a/commands/proposal_submission_new_transfer_test.go +++ b/commands/proposal_submission_new_transfer_test.go @@ -62,43 +62,47 @@ func testInvalidDestForMetric(t *testing.T) { types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE, - types.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING, types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, + types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES, }, types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES: { types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE, - types.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING, types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, + types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES, }, types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES: { types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE, - types.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING, types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, + types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES, }, types.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS: { types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, - types.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING, types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, + types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES, }, - types.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION: { + types.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL: { types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, @@ -107,46 +111,62 @@ func testInvalidDestForMetric(t *testing.T) { types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING, types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, + types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES, }, types.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN: { types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE, - types.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING, types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, + types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES, }, types.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY: { types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE, - types.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING, types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, + types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES, }, types.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING: { types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE, - types.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, + types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES, }, types.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN: { types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE, - types.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING, + types.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES, + }, + types.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES: { + types.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, + types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, + types.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, + types.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE, + types.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, + types.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, + types.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, + types.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING, + types.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, }, } @@ -187,7 +207,7 @@ func testInvalidAssetForMetric(t *testing.T) { types.AccountType_ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES, types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES, types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES, - types.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + types.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, types.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN, types.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY, types.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN, @@ -263,7 +283,8 @@ func testRecurringWithDispatchInvalidTypes(t *testing.T) { delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY) delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN) delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN) - delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION) + delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL) + delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES) delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_INSURANCE) delete(invalidTypes, types.AccountType_ACCOUNT_TYPE_GENERAL) @@ -671,11 +692,12 @@ func testNewTransferChangeSubmissionInvalidDestinationTypeFails(t *testing.T) { delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES)) delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES)) delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS)) - delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION)) + delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL)) delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN)) delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY)) delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING)) delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN)) + delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES)) delete(allAccountTypes, int32(types.AccountType_ACCOUNT_TYPE_UNSPECIFIED)) for at := range allAccountTypes { diff --git a/commands/proposal_submission_update_referral_program_test.go b/commands/proposal_submission_update_referral_program_test.go index a987c8e5fff..2df7549bfbf 100644 --- a/commands/proposal_submission_update_referral_program_test.go +++ b/commands/proposal_submission_update_referral_program_test.go @@ -183,6 +183,8 @@ func testSubmissionForReferralProgramUpdateWithoutTierMinimumRunningNotionalTake } func testSubmissionForReferralProgramUpdateWithDuplicateBenefitTierEntriesFails(t *testing.T) { + factors := []string{"1.1", "1.2", "1.3", "1.4", "1.5", "1.6"} + err := checkProposalSubmission(&commandspb.ProposalSubmission{ Terms: &types.ProposalTerms{ Change: &types.ProposalTerms_UpdateReferralProgram{ @@ -192,20 +194,44 @@ func testSubmissionForReferralProgramUpdateWithDuplicateBenefitTierEntriesFails( { MinimumRunningNotionalTakerVolume: "100", MinimumEpochs: "10", - ReferralRewardFactor: "1.1", - ReferralDiscountFactor: "1.2", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: factors[0], + MakerRewardFactor: factors[1], + LiquidityRewardFactor: factors[2], + }, + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: factors[1], + MakerDiscountFactor: factors[2], + LiquidityDiscountFactor: factors[3], + }, }, { MinimumRunningNotionalTakerVolume: "100", MinimumEpochs: "10", - ReferralRewardFactor: "1.2", - ReferralDiscountFactor: "1.3", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: factors[1], + MakerRewardFactor: factors[2], + LiquidityRewardFactor: factors[3], + }, + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: factors[2], + MakerDiscountFactor: factors[3], + LiquidityDiscountFactor: factors[4], + }, }, { MinimumRunningNotionalTakerVolume: "100", MinimumEpochs: "20", - ReferralRewardFactor: "1.3", - ReferralDiscountFactor: "1.4", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: factors[2], + MakerRewardFactor: factors[3], + LiquidityRewardFactor: factors[4], + }, + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: factors[3], + MakerDiscountFactor: factors[4], + LiquidityDiscountFactor: factors[5], + }, }, }, }, @@ -376,8 +402,8 @@ func testSubmissionForReferralProgramUpdateWithoutTierReferralRewardFactorFails( }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factor"), commands.ErrIsRequired) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factors"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factors"), commands.ErrIsRequired) } func testSubmissionForReferralProgramUpdateWithBadFormatForTierReferralRewardFactorFails(t *testing.T) { @@ -388,9 +414,17 @@ func testSubmissionForReferralProgramUpdateWithBadFormatForTierReferralRewardFac Changes: &types.ReferralProgramChanges{ BenefitTiers: []*types.BenefitTier{ { - ReferralRewardFactor: "qbc", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: "qbc", + MakerRewardFactor: "qbc", + LiquidityRewardFactor: "qbc", + }, }, { - ReferralRewardFactor: "0x32", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: "0x32", + MakerRewardFactor: "0x32", + LiquidityRewardFactor: "0x32", + }, }, }, }, @@ -399,8 +433,8 @@ func testSubmissionForReferralProgramUpdateWithBadFormatForTierReferralRewardFac }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factor"), commands.ErrIsNotValidNumber) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factors.infrastructure_reward_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factors.infrastructure_reward_factor"), commands.ErrIsNotValidNumber) } func testSubmissionForReferralProgramUpdateWithBadValueForTierReferralRewardFactorFails(t *testing.T) { @@ -411,9 +445,17 @@ func testSubmissionForReferralProgramUpdateWithBadValueForTierReferralRewardFact Changes: &types.ReferralProgramChanges{ BenefitTiers: []*types.BenefitTier{ { - ReferralRewardFactor: "-10", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: "-10", + MakerRewardFactor: "-10", + LiquidityRewardFactor: "-10", + }, }, { - ReferralRewardFactor: "-1", + ReferralRewardFactors: &types.RewardFactors{ + InfrastructureRewardFactor: "-1", + MakerRewardFactor: "-1", + LiquidityRewardFactor: "-1", + }, }, }, }, @@ -422,8 +464,12 @@ func testSubmissionForReferralProgramUpdateWithBadValueForTierReferralRewardFact }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factor"), commands.ErrMustBePositiveOrZero) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factors.infrastructure_reward_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factors.infrastructure_reward_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factors.maker_reward_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factors.maker_reward_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_reward_factors.liquidity_reward_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_reward_factors.liquidity_reward_factor"), commands.ErrMustBePositiveOrZero) } func testSubmissionForReferralProgramUpdateWithoutTierReferralDiscountFactorFails(t *testing.T) { @@ -434,9 +480,9 @@ func testSubmissionForReferralProgramUpdateWithoutTierReferralDiscountFactorFail Changes: &types.ReferralProgramChanges{ BenefitTiers: []*types.BenefitTier{ { - ReferralDiscountFactor: "", + ReferralDiscountFactors: &types.DiscountFactors{}, }, { - ReferralDiscountFactor: "", + ReferralDiscountFactors: &types.DiscountFactors{}, }, }, }, @@ -445,8 +491,12 @@ func testSubmissionForReferralProgramUpdateWithoutTierReferralDiscountFactorFail }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factor"), commands.ErrIsRequired) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.infrastructure_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.infrastructure_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.maker_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.maker_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.liquidity_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.liquidity_discount_factor"), commands.ErrIsRequired) } func testSubmissionForReferralProgramUpdateWithBadFormatForTierReferralDiscountFactorFails(t *testing.T) { @@ -457,9 +507,17 @@ func testSubmissionForReferralProgramUpdateWithBadFormatForTierReferralDiscountF Changes: &types.ReferralProgramChanges{ BenefitTiers: []*types.BenefitTier{ { - ReferralDiscountFactor: "qbc", + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: "qbc", + LiquidityDiscountFactor: "qbc", + MakerDiscountFactor: "qbc", + }, }, { - ReferralDiscountFactor: "0x32", + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: "0x32", + LiquidityDiscountFactor: "0x32", + MakerDiscountFactor: "0x32", + }, }, }, }, @@ -468,8 +526,12 @@ func testSubmissionForReferralProgramUpdateWithBadFormatForTierReferralDiscountF }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factor"), commands.ErrIsNotValidNumber) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.infrastructure_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.infrastructure_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.maker_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.maker_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.liquidity_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.liquidity_discount_factor"), commands.ErrIsNotValidNumber) } func testSubmissionForReferralProgramUpdateWithBadValueForTierReferralDiscountFactorFails(t *testing.T) { @@ -480,9 +542,17 @@ func testSubmissionForReferralProgramUpdateWithBadValueForTierReferralDiscountFa Changes: &types.ReferralProgramChanges{ BenefitTiers: []*types.BenefitTier{ { - ReferralDiscountFactor: "-10", + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: "-10", + MakerDiscountFactor: "-10", + LiquidityDiscountFactor: "-10", + }, }, { - ReferralDiscountFactor: "-1", + ReferralDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: "-1", + MakerDiscountFactor: "-1", + LiquidityDiscountFactor: "-1", + }, }, }, }, @@ -491,8 +561,12 @@ func testSubmissionForReferralProgramUpdateWithBadValueForTierReferralDiscountFa }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factor"), commands.ErrMustBePositiveOrZero) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.infrastructure_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.infrastructure_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.liquidity_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.liquidity_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.0.referral_discount_factors.maker_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_referral_program.changes.benefit_tiers.1.referral_discount_factors.maker_discount_factor"), commands.ErrMustBePositiveOrZero) } func testSubmissionForReferralProgramUpdateWithoutStakingTierMinimumStakedTokensFails(t *testing.T) { diff --git a/commands/proposal_submission_update_volume_discount_program_test.go b/commands/proposal_submission_update_volume_discount_program_test.go index b5107d318a0..9278e3fe0d0 100644 --- a/commands/proposal_submission_update_volume_discount_program_test.go +++ b/commands/proposal_submission_update_volume_discount_program_test.go @@ -220,11 +220,7 @@ func testSubmissionForVolumeDiscountProgramUpdateWithoutTierVolumeDiscountFactor UpdateVolumeDiscountProgram: &types.UpdateVolumeDiscountProgram{ Changes: &types.VolumeDiscountProgramChanges{ BenefitTiers: []*types.VolumeBenefitTier{ - { - VolumeDiscountFactor: "", - }, { - VolumeDiscountFactor: "", - }, + {}, {}, }, }, }, @@ -232,8 +228,8 @@ func testSubmissionForVolumeDiscountProgramUpdateWithoutTierVolumeDiscountFactor }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factor"), commands.ErrIsRequired) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.1.volume_discount_factor"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factors"), commands.ErrIsRequired) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.1.volume_discount_factors"), commands.ErrIsRequired) } func testSubmissionForVolumeDiscountProgramUpdateWithBadFormatForTierVolumeDiscountFactorFails(t *testing.T) { @@ -244,9 +240,17 @@ func testSubmissionForVolumeDiscountProgramUpdateWithBadFormatForTierVolumeDisco Changes: &types.VolumeDiscountProgramChanges{ BenefitTiers: []*types.VolumeBenefitTier{ { - VolumeDiscountFactor: "qbc", + VolumeDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: "qbc", + LiquidityDiscountFactor: "qbc", + MakerDiscountFactor: "qbc", + }, }, { - VolumeDiscountFactor: "0x32", + VolumeDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: "0x32", + LiquidityDiscountFactor: "0x32", + MakerDiscountFactor: "0x32", + }, }, }, }, @@ -255,8 +259,12 @@ func testSubmissionForVolumeDiscountProgramUpdateWithBadFormatForTierVolumeDisco }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factor"), commands.ErrIsNotValidNumber) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.1.volume_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factors.maker_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factors.liquidity_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factors.infrastructure_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.1.volume_discount_factors.maker_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.1.volume_discount_factors.liquidity_discount_factor"), commands.ErrIsNotValidNumber) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.1.volume_discount_factors.infrastructure_discount_factor"), commands.ErrIsNotValidNumber) } func testSubmissionForVolumeDiscountProgramUpdateWithBadValueForTierVolumeDiscountFactorFails(t *testing.T) { @@ -267,9 +275,17 @@ func testSubmissionForVolumeDiscountProgramUpdateWithBadValueForTierVolumeDiscou Changes: &types.VolumeDiscountProgramChanges{ BenefitTiers: []*types.VolumeBenefitTier{ { - VolumeDiscountFactor: "-10", + VolumeDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: "-10", + LiquidityDiscountFactor: "-5", + MakerDiscountFactor: "-7", + }, }, { - VolumeDiscountFactor: "-1", + VolumeDiscountFactors: &types.DiscountFactors{ + InfrastructureDiscountFactor: "-1", + LiquidityDiscountFactor: "-3", + MakerDiscountFactor: "-9", + }, }, }, }, @@ -278,6 +294,10 @@ func testSubmissionForVolumeDiscountProgramUpdateWithBadValueForTierVolumeDiscou }, }) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factor"), commands.ErrMustBePositiveOrZero) - assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.1.volume_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factors.maker_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factors.liquidity_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factors.infrastructure_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factors.maker_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factors.liquidity_discount_factor"), commands.ErrMustBePositiveOrZero) + assert.Contains(t, err.Get("proposal_submission.terms.change.update_volume_discount_program.changes.benefit_tiers.0.volume_discount_factors.infrastructure_discount_factor"), commands.ErrMustBePositiveOrZero) } diff --git a/commands/transfer_funds.go b/commands/transfer_funds.go index 3cd066ec070..f4dc81d8867 100644 --- a/commands/transfer_funds.go +++ b/commands/transfer_funds.go @@ -120,11 +120,12 @@ func checkTransfer(cmd *commandspb.Transfer) (e Errors) { cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES || cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES || cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS || - cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION || + cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL || cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN || cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY || cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN || - cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING { + cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING || + cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES { errs.AddForProperty("transfer.account.to", errors.New("transfers to metric-based reward accounts must be recurring transfers that specify a distribution metric")) } case *commandspb.Transfer_Recurring: @@ -151,10 +152,11 @@ func checkTransfer(cmd *commandspb.Transfer) (e Errors) { cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES || cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES || cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS || - cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION || + cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL || cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN || cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN || cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY || + cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES || cmd.ToAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING { if k.Recurring.DispatchStrategy == nil { errs.AddForProperty("transfer.kind.dispatch_strategy", ErrIsRequired) @@ -183,15 +185,16 @@ func validateDispatchStrategy(toAccountType vega.AccountType, dispatchStrategy * toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_MAKER_RECEIVED_FEES && toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES && toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS && - toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION && + toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL && toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN && toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_REALISED_RETURN && toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY && + toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES && toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING { errs.AddForProperty(destinationPrefixErr, ErrIsNotValid) } // check asset for metric is passed unless it's a market proposer reward - if len(dispatchStrategy.AssetForMetric) <= 0 && toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS && toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING { + if len(dispatchStrategy.AssetForMetric) <= 0 && toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES && toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS && toAccountType != vega.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING { errs.AddForProperty(prefix+".asset_for_metric", ErrIsRequired) } if len(dispatchStrategy.AssetForMetric) > 0 && !IsVegaID(dispatchStrategy.AssetForMetric) { @@ -213,7 +216,10 @@ func validateDispatchStrategy(toAccountType vega.AccountType, dispatchStrategy * if toAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS && dispatchStrategy.Metric != vega.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE { errs.AddForProperty(prefix+".dispatch_metric", mismatchingAccountTypeError(toAccountType, dispatchStrategy.Metric)) } - if toAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION && dispatchStrategy.Metric != vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION { + if toAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL && dispatchStrategy.Metric != vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL { + errs.AddForProperty(prefix+".dispatch_metric", mismatchingAccountTypeError(toAccountType, dispatchStrategy.Metric)) + } + if toAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES && dispatchStrategy.Metric != vega.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES { errs.AddForProperty(prefix+".dispatch_metric", mismatchingAccountTypeError(toAccountType, dispatchStrategy.Metric)) } if toAccountType == vega.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN && dispatchStrategy.Metric != vega.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN { @@ -237,6 +243,28 @@ func validateDispatchStrategy(toAccountType vega.AccountType, dispatchStrategy * if dispatchStrategy.EntityScope == vega.EntityScope_ENTITY_SCOPE_TEAMS && len(dispatchStrategy.NTopPerformers) == 0 { errs.AddForProperty(prefix+".n_top_performers", ErrIsRequired) } + if dispatchStrategy.Metric == vega.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES { + var metricAssetDefined, marketScope, stakingRequirement, positionRequirement bool + if len(dispatchStrategy.AssetForMetric) > 0 { + metricAssetDefined = true + } + if len(dispatchStrategy.Markets) > 0 { + marketScope = true + } + if len(dispatchStrategy.StakingRequirement) > 0 { + stakingRequirement = true + } + if len(dispatchStrategy.NotionalTimeWeightedAveragePositionRequirement) > 0 { + positionRequirement = true + } + if !metricAssetDefined && !marketScope && !stakingRequirement && !positionRequirement { + errs.AddForProperty(prefix+".dispatch_metric", fmt.Errorf("eligible_entities metric requires at least one of (markets, asset_for_metric, staking_requirement, notional_time_weighted_average_position_requirement) to be defined")) + } + } + + if dispatchStrategy.Metric == vega.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES && len(dispatchStrategy.NotionalTimeWeightedAveragePositionRequirement) > 0 && len(dispatchStrategy.AssetForMetric) == 0 { + errs.AddForProperty(prefix+".asset_for_metric", fmt.Errorf("asset for metric must be provided if NotionalTimeWeightedAveragePositionRequirement is specified")) + } if dispatchStrategy.EntityScope != vega.EntityScope_ENTITY_SCOPE_TEAMS && len(dispatchStrategy.NTopPerformers) != 0 { errs.AddForProperty(prefix+".n_top_performers", errors.New("must not be set when entity scope is not "+vega.EntityScope_ENTITY_SCOPE_TEAMS.String())) @@ -296,10 +324,10 @@ func validateDispatchStrategy(toAccountType vega.AccountType, dispatchStrategy * if dispatchStrategy.WindowLength > 100 { errs.AddForProperty(prefix+".window_length", ErrMustBeAtMost100) } - if len(dispatchStrategy.RankTable) == 0 && dispatchStrategy.DistributionStrategy == vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK { + if len(dispatchStrategy.RankTable) == 0 && (dispatchStrategy.DistributionStrategy == vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK || dispatchStrategy.DistributionStrategy == vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK_LOTTERY) { errs.AddForProperty(prefix+".rank_table", ErrMustBePositive) } - if len(dispatchStrategy.RankTable) > 0 && dispatchStrategy.DistributionStrategy != vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK { + if len(dispatchStrategy.RankTable) > 0 && dispatchStrategy.DistributionStrategy != vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK && dispatchStrategy.DistributionStrategy != vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK_LOTTERY { errs.AddForProperty(prefix+".rank_table", errors.New("should not be set for distribution strategy "+dispatchStrategy.DistributionStrategy.String())) } if len(dispatchStrategy.RankTable) > 500 { diff --git a/commands/transfer_funds_test.go b/commands/transfer_funds_test.go index ad3487c6424..6a16deca4eb 100644 --- a/commands/transfer_funds_test.go +++ b/commands/transfer_funds_test.go @@ -791,7 +791,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -799,7 +799,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, }, @@ -840,7 +840,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -848,7 +848,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, StakingRequirement: "banana", @@ -865,7 +865,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -873,7 +873,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, StakingRequirement: "-1", @@ -915,7 +915,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -923,7 +923,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, StakingRequirement: "1", @@ -941,7 +941,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -949,7 +949,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, NotionalTimeWeightedAveragePositionRequirement: "-1", @@ -1016,7 +1016,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1024,7 +1024,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, WindowLength: 101, @@ -1041,7 +1041,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1049,7 +1049,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK, @@ -1066,7 +1066,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1074,7 +1074,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, @@ -1094,7 +1094,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1102,7 +1102,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK, @@ -1120,7 +1120,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1128,7 +1128,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK, @@ -1150,7 +1150,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1158,7 +1158,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK, @@ -1180,7 +1180,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1188,7 +1188,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "080538b7cc2249de568cb4272a17f4d5e0b0a69a1a240acbf5119d816178daff", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, @@ -1208,7 +1208,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1216,7 +1216,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "080538b7cc2249de568cb4272a17f4d5e0b0a69a1a240acbf5119d816178daff", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, @@ -1235,7 +1235,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1243,7 +1243,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "080538b7cc2249de568cb4272a17f4d5e0b0a69a1a240acbf5119d816178daff", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, @@ -1278,7 +1278,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1286,7 +1286,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "080538b7cc2249de568cb4272a17f4d5e0b0a69a1a240acbf5119d816178daff", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, EntityScope: vega.EntityScope_ENTITY_SCOPE_TEAMS, TeamScope: []string{"team1"}, @@ -1306,7 +1306,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1314,7 +1314,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, WindowLength: 100, @@ -1332,7 +1332,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1340,7 +1340,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, WindowLength: 100, @@ -1358,7 +1358,7 @@ func TestTransferFunds(t *testing.T) { { transfer: commandspb.Transfer{ FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, - ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, Kind: &commandspb.Transfer_Recurring{ Recurring: &commandspb.RecurringTransfer{ StartEpoch: 10, @@ -1366,7 +1366,7 @@ func TestTransferFunds(t *testing.T) { Factor: "1", DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, WindowLength: 100, @@ -1441,6 +1441,31 @@ func TestTransferFunds(t *testing.T) { Reference: "testing", }, }, + { + transfer: commandspb.Transfer{ + FromAccountType: vega.AccountType_ACCOUNT_TYPE_GENERAL, + ToAccountType: vega.AccountType_ACCOUNT_TYPE_REWARD_ELIGIBLE_ENTITIES, + Kind: &commandspb.Transfer_Recurring{ + Recurring: &commandspb.RecurringTransfer{ + StartEpoch: 10, + EndEpoch: ptr.From(uint64(11)), + Factor: "1", + DispatchStrategy: &vega.DispatchStrategy{ + AssetForMetric: "", + Metric: vega.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES, + DistributionStrategy: vega.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA, + EntityScope: vega.EntityScope_ENTITY_SCOPE_TEAMS, + // no asset for metric, no markets in scope, no position requirement, no staking requirement + }, + }, + }, + To: "84e2b15102a8d6c1c6b4bdf40af8a0dc21b040eaaa1c94cd10d17604b75fdc35", + Asset: "080538b7cc2249de568cb4272a17f4d5e0b0a69a1a240acbf5119d816178daff", + Amount: "1", + Reference: "testing", + }, + errString: "transfer.kind.dispatch_strategy.dispatch_metric (eligible_entities metric requires at least one of (markets, asset_for_metric, staking_requirement, notional_time_weighted_average_position_requirement) to be defined)", + }, } invalidAccountTypesForOneOff := []vega.AccountType{ @@ -1458,7 +1483,7 @@ func TestTransferFunds(t *testing.T) { vega.AccountType_ACCOUNT_TYPE_REWARD_LP_RECEIVED_FEES, vega.AccountType_ACCOUNT_TYPE_REWARD_MARKET_PROPOSERS, vega.AccountType_ACCOUNT_TYPE_REWARD_MAKER_PAID_FEES, - vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_POSITION, + vega.AccountType_ACCOUNT_TYPE_REWARD_AVERAGE_NOTIONAL, vega.AccountType_ACCOUNT_TYPE_REWARD_RELATIVE_RETURN, vega.AccountType_ACCOUNT_TYPE_REWARD_RETURN_VOLATILITY, vega.AccountType_ACCOUNT_TYPE_REWARD_VALIDATOR_RANKING, diff --git a/core/assets/erc20/erc20.go b/core/assets/erc20/erc20.go index 7377f164189..c0e02483c68 100644 --- a/core/assets/erc20/erc20.go +++ b/core/assets/erc20/erc20.go @@ -145,7 +145,7 @@ func (e *ERC20) SignListAsset() (msg []byte, sig []byte, err error) { } source := e.asset.Details.GetERC20() - bundle, err := bridges.NewERC20Logic(e.wallet, bridgeAddress, e.chainID, e.ethClient.IsEthereum()). + bundle, err := bridges.NewERC20Logic(e.wallet, bridgeAddress, e.chainID, false). ListAsset(e.address, e.asset.ID, source.LifetimeLimit, source.WithdrawThreshold, nonce) if err != nil { return nil, nil, err @@ -156,7 +156,7 @@ func (e *ERC20) SignListAsset() (msg []byte, sig []byte, err error) { func (e *ERC20) SignSetAssetLimits(nonce *num.Uint, lifetimeLimit *num.Uint, withdrawThreshold *num.Uint) (msg []byte, sig []byte, err error) { bridgeAddress := e.ethClient.CollateralBridgeAddress().Hex() - bundle, err := bridges.NewERC20Logic(e.wallet, bridgeAddress, e.chainID, e.ethClient.IsEthereum()). + bundle, err := bridges.NewERC20Logic(e.wallet, bridgeAddress, e.chainID, false). SetAssetLimits(e.address, lifetimeLimit, withdrawThreshold, nonce) if err != nil { return nil, nil, err @@ -173,7 +173,7 @@ func (e *ERC20) SignWithdrawal( ) (msg []byte, sig []byte, err error) { nonce, _ := num.UintFromBig(withdrawRef) bridgeAddress := e.ethClient.CollateralBridgeAddress().Hex() - bundle, err := bridges.NewERC20Logic(e.wallet, bridgeAddress, e.chainID, e.ethClient.IsEthereum()). + bundle, err := bridges.NewERC20Logic(e.wallet, bridgeAddress, e.chainID, false). WithdrawAsset(e.address, amount, ethPartyAddress, now, nonce) if err != nil { return nil, nil, err diff --git a/core/assets/erc20/erc20_test.go b/core/assets/erc20/erc20_test.go index 252dd637af6..129dc4ad689 100644 --- a/core/assets/erc20/erc20_test.go +++ b/core/assets/erc20/erc20_test.go @@ -99,8 +99,7 @@ func testWithdrawAsset(t *testing.T) { assert.NotNil(t, msg) assert.NotNil(t, sig) assert.True(t, verifySignature(msg, sig)) - assert.Equal(t, - "68154aa30a66d8546a338e2f50ac3e0bde710975755562e12c8508c5e4e43aa741b98d1f7384d8cf6a33e86fc1ed6f833ad627a9fb9b5a56aaaf0024511a2402", + assert.Equal(t, "4b7423e2f005a25150f56fdcea72ccabfb466d94a4a3d1aa3541eed2f1292deabd9de643ccc00d64561806c302c8c9773a028fd0e2778e2b2a6f6b3948c95b06", hex.EncodeToString(sig), ) } @@ -113,8 +112,7 @@ func testListAsset(t *testing.T) { assert.NotNil(t, msg) assert.NotNil(t, sig) assert.True(t, verifySignature(msg, sig)) - assert.Equal(t, - "e6048f597145d7d1e1ddfe41abf9ae950e9b6e93598c8b1e4fe2d9af8493b240a4d85322eb40c6bf76b0eac2481fa42014956f10a38675769b0c995e191d650b", + assert.Equal(t, "125419bebd3623144c1da43d9cc32fc7cab4b870ade32eda8e2a3b175c22b9dac556c47c195bf44f7d7114964411235e690294683cf252f005809f49c4536400", hex.EncodeToString(sig), ) } diff --git a/core/banking/engine.go b/core/banking/engine.go index 859724a9574..332d4b552c5 100644 --- a/core/banking/engine.go +++ b/core/banking/engine.go @@ -114,6 +114,7 @@ type MarketActivityTracker interface { MarketTrackedForAsset(market, asset string) bool TeamStatsForMarkets(allMarketsForAssets, onlyTheseMarkets []string) map[string]map[string]*num.Uint PublishGameMetric(ctx context.Context, dispatchStrategy []*vega.DispatchStrategy, now time.Time) + GameFinished(gameID string) } type EthereumEventSource interface { diff --git a/core/banking/engine_test.go b/core/banking/engine_test.go index 78922818012..a2d14c00abd 100644 --- a/core/banking/engine_test.go +++ b/core/banking/engine_test.go @@ -76,6 +76,7 @@ func getTestEngine(t *testing.T) *testEngine { primaryBridgeView := mocks.NewMockERC20BridgeView(ctrl) secondaryBridgeView := mocks.NewMockERC20BridgeView(ctrl) marketActivityTracker := mocks.NewMockMarketActivityTracker(ctrl) + marketActivityTracker.EXPECT().GameFinished(gomock.Any()).AnyTimes() ethSource := mocks.NewMockEthereumEventSource(ctrl) notary.EXPECT().OfferSignatures(gomock.Any(), gomock.Any()).AnyTimes() diff --git a/core/banking/gov_transfers.go b/core/banking/gov_transfers.go index 665bd6e1f52..e2bb5dac37c 100644 --- a/core/banking/gov_transfers.go +++ b/core/banking/gov_transfers.go @@ -48,11 +48,12 @@ var ( types.AccountTypeMakerReceivedFeeReward: {}, types.AccountTypeMarketProposerReward: {}, types.AccountTypeLPFeeReward: {}, - types.AccountTypeAveragePositionReward: {}, + types.AccountTypeAverageNotionalReward: {}, types.AccountTypeRelativeReturnReward: {}, types.AccountTypeReturnVolatilityReward: {}, types.AccountTypeValidatorRankingReward: {}, types.AccountTypeRealisedReturnReward: {}, + types.AccountTypeEligibleEntitiesReward: {}, } ) diff --git a/core/banking/mocks/mocks.go b/core/banking/mocks/mocks.go index 3ac5f286fd4..6017f912187 100644 --- a/core/banking/mocks/mocks.go +++ b/core/banking/mocks/mocks.go @@ -514,6 +514,18 @@ func (mr *MockMarketActivityTrackerMockRecorder) CalculateMetricForTeams(arg0, a return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CalculateMetricForTeams", reflect.TypeOf((*MockMarketActivityTracker)(nil).CalculateMetricForTeams), arg0, arg1) } +// GameFinished mocks base method. +func (m *MockMarketActivityTracker) GameFinished(arg0 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "GameFinished", arg0) +} + +// GameFinished indicates an expected call of GameFinished. +func (mr *MockMarketActivityTrackerMockRecorder) GameFinished(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GameFinished", reflect.TypeOf((*MockMarketActivityTracker)(nil).GameFinished), arg0) +} + // GetMarketsWithEligibleProposer mocks base method. func (m *MockMarketActivityTracker) GetMarketsWithEligibleProposer(arg0 string, arg1 []string, arg2, arg3 string) []*types.MarketContributionScore { m.ctrl.T.Helper() diff --git a/core/banking/recurring_transfers.go b/core/banking/recurring_transfers.go index 76941a5dbbf..7603183ff68 100644 --- a/core/banking/recurring_transfers.go +++ b/core/banking/recurring_transfers.go @@ -152,6 +152,7 @@ func (e *Engine) cleanupStaleDispatchStrategies() { for hash, dsc := range e.hashToStrategy { if dsc.refCount == 0 { delete(e.hashToStrategy, hash) + e.marketActivityTracker.GameFinished(hash) } } } @@ -194,10 +195,11 @@ func (e *Engine) dispatchRequired(ctx context.Context, ds *vegapb.DispatchStrate case vegapb.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_PAID, vegapb.DispatchMetric_DISPATCH_METRIC_MAKER_FEES_RECEIVED, vegapb.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED, - vegapb.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + vegapb.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, vegapb.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, vegapb.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY, - vegapb.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN: + vegapb.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN, + vegapb.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES: if ds.EntityScope == vegapb.EntityScope_ENTITY_SCOPE_INDIVIDUALS { hasNonZeroMetric := false partyMetrics := e.marketActivityTracker.CalculateMetricForIndividuals(ctx, ds) @@ -215,7 +217,7 @@ func (e *Engine) dispatchRequired(ctx context.Context, ds *vegapb.DispatchStrate break } } - required = hasNonZeroMetric || (hasEligibleParties && ds.DistributionStrategy == vegapb.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK) + required = hasNonZeroMetric || (hasEligibleParties && (ds.DistributionStrategy == vegapb.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK || ds.DistributionStrategy == vegapb.DistributionStrategy_DISTRIBUTION_STRATEGY_RANK_LOTTERY)) return required } else { tcs, pcs := e.marketActivityTracker.CalculateMetricForTeams(ctx, ds) diff --git a/core/banking/recurring_transfers_test.go b/core/banking/recurring_transfers_test.go index 80ff3d08da4..7904354bd51 100644 --- a/core/banking/recurring_transfers_test.go +++ b/core/banking/recurring_transfers_test.go @@ -781,7 +781,7 @@ func TestMarketAssetMismatchRejectsTransfer(t *testing.T) { Factor: num.MustDecimalFromString("0.9"), DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "zohar", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, Markets: []string{"mmm"}, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, @@ -802,7 +802,7 @@ func TestDispatchStrategyRemoval(t *testing.T) { dispatchStrat := &vega.DispatchStrategy{ AssetForMetric: "zohar", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, Markets: []string{"mmm"}, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, diff --git a/core/banking/snapshot_test.go b/core/banking/snapshot_test.go index 9faab7daed2..58c832551d2 100644 --- a/core/banking/snapshot_test.go +++ b/core/banking/snapshot_test.go @@ -516,7 +516,7 @@ func TestRecurringTransfersSnapshotRoundTrip(t *testing.T) { Factor: num.MustDecimalFromString("0.9"), DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "zohar", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, Markets: []string{"mmm"}, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, @@ -578,7 +578,7 @@ func TestRecurringGovTransfersSnapshotRoundTrip(t *testing.T) { EndEpoch: &endEpoch, DispatchStrategy: &vega.DispatchStrategy{ AssetForMetric: "zohar", - Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, Markets: []string{"mmm"}, EntityScope: vega.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM, diff --git a/core/bridges/erc20_asset_pool.go b/core/bridges/erc20_asset_pool.go index 90e52207f14..b72a5421ef3 100644 --- a/core/bridges/erc20_asset_pool.go +++ b/core/bridges/erc20_asset_pool.go @@ -74,7 +74,7 @@ func (e ERC20AssetPool) SetBridgeAddress( newAddressEth := ethcmn.HexToAddress(newAddress) buf, err := args.Pack([]interface{}{ - newAddressEth, nonce.BigInt(), "set_bridge_address", + newAddressEth, nonce.BigInt(), "setBridgeAddress", }...) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) @@ -122,7 +122,7 @@ func (e ERC20AssetPool) SetMultiSigControl( newAddressEth := ethcmn.HexToAddress(newAddress) buf, err := args.Pack([]interface{}{ - newAddressEth, nonce.BigInt(), "set_multisig_control", + newAddressEth, nonce.BigInt(), "setMultisigControl", }...) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) diff --git a/core/bridges/erc20_asset_pool_test.go b/core/bridges/erc20_asset_pool_test.go index d93162c5b73..de4394bf740 100644 --- a/core/bridges/erc20_asset_pool_test.go +++ b/core/bridges/erc20_asset_pool_test.go @@ -37,12 +37,12 @@ func TestAssetPoolSetBridgeAddress(t *testing.T) { { name: "v1 scheme", v1: true, - expected: "2488c05dd36a754db037f22a1d649109573e299a3c135efdb81c6f64632b26101c0b4ce19c896d370abae8d457682b21a4a3322f48380f29932b311b6ab47707", + expected: "d0d9cfac8f805bd28a8c534069157d900b8c60d29580ebbee73ad5be71d1d2c1b20d5f10339b0ff570cea9f3422c1c599bd76b99c37cd19c8a3901bd75603404", }, { name: "v2 scheme", v1: false, - expected: "4b01dfa1a3b77ecc624f678805a74418862cbcb1e32b929e7dce7fbbfa73806ec1f5db1d40d28f4ebcb09d83f59815f04438142612ebc1683158a23c9fbf3a0c", + expected: "52e2d9005416e7afe750b4fcf69d9e8e0fe2809127f87c327f10cfa5e76da55069ef723cdc07bafcfb4e44798f2d1b52cf618787cbccf19c6c8f2d1cd0530906", }, } diff --git a/core/bridges/erc20_logic.go b/core/bridges/erc20_logic.go index 434aee3e0b6..e59eda1f61e 100644 --- a/core/bridges/erc20_logic.go +++ b/core/bridges/erc20_logic.go @@ -107,7 +107,7 @@ func (e ERC20Logic) ListAsset( lifetimeLimit.BigInt(), withdrawThreshold.BigInt(), nonce.BigInt(), - "list_asset", + "listAsset", }...) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) @@ -182,7 +182,7 @@ func (e ERC20Logic) buildListAssetMessage( lifetimeLimit.BigInt(), withdrawThreshold.BigInt(), nonce.BigInt(), - "list_asset", + "listAsset", }...) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) @@ -266,7 +266,7 @@ func (e ERC20Logic) RemoveAsset( tokenAddressEth := ethcmn.HexToAddress(tokenAddress) buf, err := args.Pack([]interface{}{ - tokenAddressEth, nonce.BigInt(), "remove_asset", + tokenAddressEth, nonce.BigInt(), "removeAsset", }...) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) @@ -354,7 +354,7 @@ func (e ERC20Logic) buildWithdrawAssetMessage( hexEthPartyAddress, timestamp, nonce.BigInt(), - "withdraw_asset", + "withdrawAsset", }...) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) @@ -447,7 +447,7 @@ func (e ERC20Logic) SetAssetLimits( lifetimeLimit.BigInt(), withdrawThreshold.BigInt(), nonce.BigInt(), - "set_asset_limits", + "setAssetLimits", }...) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) @@ -509,7 +509,7 @@ func (e ERC20Logic) buildSetAssetLimitsMessage( lifetimeLimit.BigInt(), withdrawThreshold.BigInt(), nonce.BigInt(), - "set_asset_limits", + "setAssetLimits", }...) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) @@ -604,7 +604,7 @@ func (e ERC20Logic) buildWithdrawDelayMessage( buf, err := args.Pack([]interface{}{ delayBig, nonce.BigInt(), - "set_withdraw_delay", + "setWithdrawDelay", }...) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) @@ -671,7 +671,7 @@ func (e ERC20Logic) GlobalStop( buf, err := args.Pack([]interface{}{ nonce.BigInt(), - "global_stop", + "globalStop", }...) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) @@ -710,7 +710,7 @@ func (e ERC20Logic) GlobalResume( buf, err := args.Pack([]interface{}{ nonce.BigInt(), - "global_resume", + "globalResume", }...) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) @@ -750,7 +750,7 @@ func (e ERC20Logic) VerifyGlobalResume( buf, err := args.Pack([]interface{}{ nonce.BigInt(), - "global_resume", + "globalResume", }...) if err != nil { return nil, fmt.Errorf("couldn't pack abi message: %w", err) diff --git a/core/bridges/erc20_logic_test.go b/core/bridges/erc20_logic_test.go index e1d97c24674..aa88e5de414 100644 --- a/core/bridges/erc20_logic_test.go +++ b/core/bridges/erc20_logic_test.go @@ -47,12 +47,12 @@ func testListAsset(t *testing.T) { { name: "v1 scheme", v1: true, - expected: "7df8b88552c2f981e64b13f1ce3ee5dcb71e8f59ec057010b7b469120afff7d479f234714785cfc605230dfb2d17f9cc7858143196a13f357ce008e3f3f78a00", + expected: "a39fa614b7b4bb0cf5819840164ca48472d1bda98a49053f891dfb004f053d2c29a60df5423927dad057f1d3d6a04c6e6d82f1bf128db5d5a7a01bcc8b70ab0e", }, { name: "v2 scheme", v1: false, - expected: "03d8d648da4402bebd096f067cebf3e3b70f2c4e1cad6ca9eb757f554b6ca9efb84010887aeef543cf72cb5d78a741d0683befc6f5e0ca2d0347832232af610c", + expected: "d6810cef5534e232396ab0c572ca079fa41f728a20d98da3bfb59b81f183a96adee103d5f94348e9a8ce823446392109bc8bf10a27076cd0e232a1f808e0810c", }, } @@ -86,12 +86,12 @@ func testRemoveAsset(t *testing.T) { { name: "v1 scheme", v1: true, - expected: "9012eb20763500caf1a4d7640470449c7220872d7136e17c70231c269051cf80e08760d60850578ebf494e24610a54225c7d994f15f57d9f451e8f717eb3f904", + expected: "a1c183d1c076c518297fa75f0fa3fddf6e5e83e76800a2efdbce36be12d4a23e2b61bce8097fea701f5a274ec89d70f92ffdd83a82a4d2c65f82b905109c3d0f", }, { name: "v2 scheme", v1: false, - expected: "aa07e175a9a4c3dcb0f5dcbd24cc6636e699ee6a1daa9a80267cec8f0be130b86465fa56296743879f56d94d6be64a0b10b76bcee40d0d09ec078b2814b89500", + expected: "f11d5e0fa1c68edd1b43db30a0d02aaff8cef26c6140d30a4570615fa12a0e4e858a30b73a3763590a88614f366e9f433978653445b901e058dc07fe77595901", }, } @@ -122,12 +122,12 @@ func testWithdrawAsset(t *testing.T) { { name: "v1 scheme", v1: true, - expected: "0ff08571ab504acdce063a5a5a00dd8878d64ccb09ea6887aacd1fd41b517cd13f4e12edfaa4d06fef5d24087ba9e7c980532daa0a6f1fa329b8d75961f4ab03", + expected: "8c70e1bb8a74a9112ef475cdca37f63149453f5d3729164847aabf329c8932774922bb3cf41dfd7112fa8a0bdbe3f845b170e8f38406ae51fd7da00177dbc807", }, { name: "v2 scheme", v1: false, - expected: "9f2d7ec17059fd5d4697337a46899f73681dece748ea1342b3be24b5f34f0b934ad448f7e9bd3a113102d46d8433dd26458cf06c3fd7a1622d086faab1a77b08", + expected: "057bcb000d6961d4c8cd67f5a8a8ffac501f7077c23dcb4aab0127f1f4530e865d8627bccfb71559b9ddca0cb938b96c709fdf419d2f350ec9ab6416b888c70f", }, } diff --git a/core/bridges/erc20_multisigcontrol.go b/core/bridges/erc20_multisigcontrol.go index 177c30a14aa..577376fb141 100644 --- a/core/bridges/erc20_multisigcontrol.go +++ b/core/bridges/erc20_multisigcontrol.go @@ -60,7 +60,7 @@ func (e *ERC20MultiSigControl) BurnNonce( }, }) - buf, err := args.Pack([]interface{}{nonce.BigInt(), "burn_nonce"}...) + buf, err := args.Pack([]interface{}{nonce.BigInt(), "burnNonce"}...) if err != nil { return nil, err } @@ -93,7 +93,7 @@ func (e *ERC20MultiSigControl) SetThreshold( args := abi.Arguments([]abi.Argument{ { - Name: "new_threshold", + Name: "newThreshold", Type: typU16, }, { @@ -106,7 +106,7 @@ func (e *ERC20MultiSigControl) SetThreshold( }, }) - buf, err := args.Pack([]interface{}{newThreshold, nonce.BigInt(), "set_threshold"}...) + buf, err := args.Pack([]interface{}{newThreshold, nonce.BigInt(), "setThreshold"}...) if err != nil { return nil, err } @@ -151,7 +151,7 @@ func (e *ERC20MultiSigControl) AddSigner( }) newSignerAddr := ethcmn.HexToAddress(newSigner) - buf, err := args.Pack([]interface{}{newSignerAddr, nonce.BigInt(), "add_signer"}...) + buf, err := args.Pack([]interface{}{newSignerAddr, nonce.BigInt(), "addSigner"}...) if err != nil { return nil, err } @@ -197,7 +197,7 @@ func (e *ERC20MultiSigControl) RemoveSigner( }) oldSignerAddr := ethcmn.HexToAddress(oldSigner) - buf, err := args.Pack([]interface{}{oldSignerAddr, nonce.BigInt(), "remove_signer"}...) + buf, err := args.Pack([]interface{}{oldSignerAddr, nonce.BigInt(), "removeSigner"}...) if err != nil { return nil, err } diff --git a/core/bridges/erc20_multisigcontrol_test.go b/core/bridges/erc20_multisigcontrol_test.go index 998b24e760d..1e40e318540 100644 --- a/core/bridges/erc20_multisigcontrol_test.go +++ b/core/bridges/erc20_multisigcontrol_test.go @@ -49,12 +49,12 @@ func testSetThreshold(t *testing.T) { { name: "v1 scheme", v1: true, - expected: "a2c61b473f15a1729e8593d65748e7a9813102e0d7304598af556525206db599fb79b9750349c6cb564a2f3ecdf233dd19b1598302e0cb91218adff1c609ac09", + expected: "537eee3a9151d3f9a0a076c9521d3ca014efaefb20fd9be1b36c1e2475897f82b79b0de759b24a52a1a66c272c85628acd182a3cef2ae92b91a595f8d8123c06", }, { name: "v2 scheme", v1: false, - expected: "aa79559d350a9b139d04d7883b7ec26b3948bba503fddcc55f8a868a69ef48dad32ffb4233a041401e482e71232fc339aa6deffda31bcd978596a6a0a6d64b0c", + expected: "e1bc702b74ca31f08d1d3534b6b147f641cf94713a809dd5b7ed5cb6e61b1892f1d2327ef14194bac488f41a661b805c6ca2f3daba7108eee46949229a126c0e", }, } @@ -84,12 +84,12 @@ func testAddSigner(t *testing.T) { { name: "v1 scheme", v1: true, - expected: "7bdc018935610f23667b31d4eee248160ab39caa1e70ad20da49bf8971d5a16b30f71a09d9aaf5b532defdb7710d85c226e98cb90a49bc4b4401b33f3c5a1601", + expected: "52201353f14009c7638a6460af9340871de8d34a198d4957a142aa8098922829d4963eff0e6951fdecfe04566c865225b4c41dd2393b40d4de76fb9819142d0c", }, { name: "v2 scheme", v1: false, - expected: "f86654970ab8aa7b8f1ac72cd1349cd667acd21b7ff2078653d488f3ab65a446df1b4878692d7f07e2f0111bed069fd7cf5c32f07ae88ed059624480cd0edd07", + expected: "67e5a71935eaf134f4cbfadbefa96e510c97d6198accfcec76b046ee46ecd59fe4ae6f371973a8f4be7a67dd20b38c421b0f24061f1c95fe1e9322d04997e407", }, } @@ -120,12 +120,12 @@ func testRemoveSigner(t *testing.T) { { name: "v1 scheme", v1: true, - expected: "98ea2303c68dbb0a88bdb7dad8c6e2db9698cd992667399a378e682dbdf16e74a9d304a32e36b48de81c0e99449a7a37c1a7ef94af1e85aa88a808f8d7126c0c", + expected: "4bf8057aa87a4ec5049766b0eb40426c9ba0464cda2ce203d16c590f3153657689143161908923b0c6ab32dec57c4c5aca7e4aaef24b8d22362413908868ea00", }, { name: "v2 scheme", v1: false, - expected: "e17efd360ce488a7299175473f257544391e3823db314e31cc69e6ae2730ead994e89bfab5813ea1379c4b6e499d131308ebe516ba6142f9f77479083685020b", + expected: "648f08b810bcbe589b79f7476f48de6e2c3528fbb059427f3876745dec51128952210d5e509e307d0dc2f6ba67f65bda7a28d96a8d31a365d04398cdde78150b", }, } diff --git a/core/client/eth/client.go b/core/client/eth/client.go index faae6b04690..1150fb63144 100644 --- a/core/client/eth/client.go +++ b/core/client/eth/client.go @@ -96,14 +96,6 @@ func (c *PrimaryClient) UpdateEthereumConfig(ctx context.Context, ethConfig *typ return fmt.Errorf("updated chain ID does not match the one set during start up, expected %v got %v", ethConfig.ChainID(), chainID) } - if err := c.verifyCollateralContract(ctx, ethConfig); err != nil { - return fmt.Errorf("failed to verify collateral bridge contract: %w", err) - } - - if err := c.verifyMultisigContract(ctx, ethConfig); err != nil { - return fmt.Errorf("failed to verify multisig control contract: %w", err) - } - c.ethConfig = ethConfig return nil @@ -171,19 +163,3 @@ func (c *PrimaryClient) VerifyContract(ctx context.Context, address ethcommon.Ad return nil } - -func (c *PrimaryClient) verifyCollateralContract(ctx context.Context, ethConfig *types.EthereumConfig) error { - if address := ethConfig.CollateralBridge(); address.HasAddress() { - return c.VerifyContract(ctx, address.Address(), ContractHashes["collateral"]) - } - - return nil -} - -func (c *PrimaryClient) verifyMultisigContract(ctx context.Context, ethConfig *types.EthereumConfig) error { - if address := ethConfig.MultiSigControl(); address.HasAddress() { - return c.VerifyContract(ctx, address.Address(), ContractHashes["multisig"]) - } - - return nil -} diff --git a/core/collateral/checkpoint.go b/core/collateral/checkpoint.go index 1b45ad4861f..9c8c3d0438c 100644 --- a/core/collateral/checkpoint.go +++ b/core/collateral/checkpoint.go @@ -59,7 +59,7 @@ var partyOverrides = map[string]types.AccountType{ systemOwner + types.AccountTypeMakerReceivedFeeReward.String(): types.AccountTypeMakerReceivedFeeReward, systemOwner + types.AccountTypeMakerPaidFeeReward.String(): types.AccountTypeMakerPaidFeeReward, systemOwner + types.AccountTypeLPFeeReward.String(): types.AccountTypeLPFeeReward, - systemOwner + types.AccountTypeAveragePositionReward.String(): types.AccountTypeAveragePositionReward, + systemOwner + types.AccountTypeAverageNotionalReward.String(): types.AccountTypeAverageNotionalReward, systemOwner + types.AccountTypeRelativeReturnReward.String(): types.AccountTypeRelativeReturnReward, systemOwner + types.AccountTypeReturnVolatilityReward.String(): types.AccountTypeReturnVolatilityReward, systemOwner + types.AccountTypeValidatorRankingReward.String(): types.AccountTypeValidatorRankingReward, @@ -67,6 +67,7 @@ var partyOverrides = map[string]types.AccountType{ systemOwner + types.AccountTypeFeesInfrastructure.String(): types.AccountTypeFeesInfrastructure, systemOwner + types.AccountTypePendingTransfers.String(): types.AccountTypePendingTransfers, systemOwner + types.AccountTypeRealisedReturnReward.String(): types.AccountTypeRealisedReturnReward, + systemOwner + types.AccountTypeEligibleEntitiesReward.String(): types.AccountTypeEligibleEntitiesReward, } var tradingRewardAccountTypes = map[types.AccountType]struct{}{ @@ -74,11 +75,12 @@ var tradingRewardAccountTypes = map[types.AccountType]struct{}{ types.AccountTypeMakerPaidFeeReward: {}, types.AccountTypeLPFeeReward: {}, types.AccountTypeMarketProposerReward: {}, - types.AccountTypeAveragePositionReward: {}, + types.AccountTypeAverageNotionalReward: {}, types.AccountTypeRelativeReturnReward: {}, types.AccountTypeReturnVolatilityReward: {}, types.AccountTypeValidatorRankingReward: {}, types.AccountTypeRealisedReturnReward: {}, + types.AccountTypeEligibleEntitiesReward: {}, } func (e *Engine) Load(ctx context.Context, data []byte) error { @@ -194,8 +196,8 @@ func (e *Engine) getCheckpointBalances() []*checkpoint.AssetBalance { types.AccountTypeLPFeeReward, types.AccountTypeMakerReceivedFeeReward, types.AccountTypeMakerPaidFeeReward, types.AccountTypeMarketProposerReward, types.AccountTypeFeesInfrastructure, types.AccountTypePendingTransfers, types.AccountTypeNetworkTreasury, types.AccountTypeGlobalInsurance, types.AccountTypeVestedRewards, - types.AccountTypeAveragePositionReward, types.AccountTypeRelativeReturnReward, types.AccountTypeRealisedReturnReward, - types.AccountTypeReturnVolatilityReward, types.AccountTypeValidatorRankingReward: + types.AccountTypeAverageNotionalReward, types.AccountTypeRelativeReturnReward, types.AccountTypeRealisedReturnReward, + types.AccountTypeReturnVolatilityReward, types.AccountTypeValidatorRankingReward, types.AccountTypeEligibleEntitiesReward: owner := acc.Owner // NB: market insurance accounts funds will flow implicitly using this logic into the network treasury for the asset // similarly LP Fee bonus distribution bonus account would fall over into the network treasury of the asset. @@ -216,11 +218,12 @@ func (e *Engine) getCheckpointBalances() []*checkpoint.AssetBalance { acc.Type == types.AccountTypeMakerReceivedFeeReward || acc.Type == types.AccountTypeMakerPaidFeeReward || acc.Type == types.AccountTypeMarketProposerReward || - acc.Type == types.AccountTypeAveragePositionReward || + acc.Type == types.AccountTypeAverageNotionalReward || acc.Type == types.AccountTypeRelativeReturnReward || acc.Type == types.AccountTypeReturnVolatilityReward || acc.Type == types.AccountTypeValidatorRankingReward || - acc.Type == types.AccountTypeRealisedReturnReward { + acc.Type == types.AccountTypeRealisedReturnReward || + acc.Type == types.AccountTypeEligibleEntitiesReward { owner += separator + acc.MarketID } diff --git a/core/collateral/engine.go b/core/collateral/engine.go index 39640306389..2cc9e77314d 100644 --- a/core/collateral/engine.go +++ b/core/collateral/engine.go @@ -208,6 +208,15 @@ func (e *Engine) CheckOrderSpamAllMarkets(party string) error { return fmt.Errorf("party " + party + " is not eligible to submit order transaction with no market in scope") } +func (e *Engine) GetAllParties() []string { + keys := make([]string, 0, len(e.partiesAccsBalanceCache)) + for k := range e.partiesAccsBalanceCache { + keys = append(keys, k) + } + sort.Strings(keys) + return keys +} + func (e *Engine) CheckOrderSpam(party, market string, assets []string) error { e.cacheLock.RLock() defer e.cacheLock.RUnlock() @@ -448,6 +457,14 @@ func (e *Engine) ReloadConf(cfg Config) { e.cfgMu.Unlock() } +func (e *Engine) OnEpochEvent(ctx context.Context, epoch types.Epoch) { + if epoch.Action == vega.EpochAction_EPOCH_ACTION_START { + e.snapshotBalances() + } +} + +func (e *Engine) OnEpochRestore(ctx context.Context, epoch types.Epoch) {} + // EnableAsset adds a new asset in the collateral engine // this enable the asset to be used by new markets or // parties to deposit funds. @@ -732,7 +749,7 @@ func (e *Engine) transferSpotFees(ctx context.Context, marketID string, assetID for _, transfer := range transfers { req, err := e.getSpotFeeTransferRequest( - transfer, makerFee, infraFee, liquiFee, marketID, assetID) + ctx, transfer, makerFee, infraFee, liquiFee, marketID, assetID) if err != nil { e.log.Error("Failed to build transfer request for event", logging.Error(err)) @@ -763,6 +780,7 @@ func (e *Engine) transferSpotFees(ctx context.Context, marketID string, assetID } func (e *Engine) getSpotFeeTransferRequest( + ctx context.Context, t *types.Transfer, makerFee, infraFee, liquiFee *types.Account, marketID, assetID string, @@ -795,6 +813,13 @@ func (e *Engine) getSpotFeeTransferRequest( return nil, err } + networkTreausry, err := e.GetNetworkTreasuryAccount(assetID) + if err != nil { + return nil, err + } + + buyBackAccount := e.getOrCreateBuyBackFeesAccount(ctx, assetID) + treq := &types.TransferRequest{ Amount: t.Amount.Amount.Clone(), MinAmount: t.Amount.Amount.Clone(), @@ -814,6 +839,20 @@ func (e *Engine) getSpotFeeTransferRequest( treq.FromAccount = []*types.Account{infraFee} treq.ToAccount = []*types.Account{general} return treq, nil + case types.TransferTypeTreasuryPay: + amt := num.Min(treq.Amount, general.Balance.Clone()) + treq.Amount = amt + treq.MinAmount = amt + treq.FromAccount = []*types.Account{general} + treq.ToAccount = []*types.Account{networkTreausry} + return treq, nil + case types.TransferTypeBuyBackFeePay: + amt := num.Min(treq.Amount, general.Balance.Clone()) + treq.Amount = amt + treq.MinAmount = amt + treq.FromAccount = []*types.Account{general} + treq.ToAccount = []*types.Account{buyBackAccount} + return treq, nil case types.TransferTypeLiquidityFeePay: amt := num.Min(treq.Amount, general.Balance.Clone()) treq.Amount = amt @@ -836,6 +875,17 @@ func (e *Engine) getSpotFeeTransferRequest( treq.FromAccount = []*types.Account{makerFee} treq.ToAccount = []*types.Account{general} return treq, nil + case types.TransferTypeHighMakerRebateReceive: + treq.FromAccount = []*types.Account{makerFee} + treq.ToAccount = []*types.Account{general} + return treq, nil + case types.TransferTypeHighMakerRebatePay: + amt := num.Min(treq.Amount, general.Balance.Clone()) + treq.Amount = amt + treq.MinAmount = amt + treq.FromAccount = []*types.Account{general} + treq.ToAccount = []*types.Account{makerFee} + return treq, nil case types.TransferTypeLiquidityFeeAllocate: partyLiquidityFee, err := partyLiquidityFeeAccount() if err != nil { @@ -2234,6 +2284,12 @@ func (e *Engine) getFeeTransferRequest( general *types.Account err error ) + networkTreausry, err := e.GetNetworkTreasuryAccount(assetID) + if err != nil { + return nil, err + } + buyBackAccount := e.getOrCreateBuyBackFeesAccount(ctx, assetID) + if t.Owner == types.NetworkParty { general, err = e.GetMarketInsurancePoolAccount(marketID, assetID) if err != nil { @@ -2295,6 +2351,28 @@ func (e *Engine) getFeeTransferRequest( treq.FromAccount = append(treq.FromAccount, orderMargin) } treq.ToAccount = []*types.Account{infraFee} + case types.TransferTypeTreasuryPay: + margin, err := marginAccount() + if err != nil { + return nil, err + } + orderMargin := orderMarginAccount() + treq.FromAccount = []*types.Account{general, margin} + if orderMargin != nil { + treq.FromAccount = append(treq.FromAccount, orderMargin) + } + treq.ToAccount = []*types.Account{networkTreausry} + case types.TransferTypeBuyBackFeePay: + margin, err := marginAccount() + if err != nil { + return nil, err + } + orderMargin := orderMarginAccount() + treq.FromAccount = []*types.Account{general, margin} + if orderMargin != nil { + treq.FromAccount = append(treq.FromAccount, orderMargin) + } + treq.ToAccount = []*types.Account{buyBackAccount} case types.TransferTypeInfrastructureFeeDistribute: treq.FromAccount = []*types.Account{infraFee} treq.ToAccount = []*types.Account{general} @@ -2326,6 +2404,21 @@ func (e *Engine) getFeeTransferRequest( case types.TransferTypeMakerFeeReceive: treq.FromAccount = []*types.Account{makerFee} treq.ToAccount = []*types.Account{general} + case types.TransferTypeHighMakerRebatePay: + margin, err := marginAccount() + if err != nil { + return nil, err + } + orderMargin := orderMarginAccount() + treq.FromAccount = []*types.Account{general, margin} + if orderMargin != nil { + treq.FromAccount = append(treq.FromAccount, orderMargin) + } + treq.ToAccount = []*types.Account{makerFee} + case types.TransferTypeHighMakerRebateReceive: + treq.FromAccount = []*types.Account{makerFee} + treq.ToAccount = []*types.Account{general} + return treq, nil case types.TransferTypeLiquidityFeeAllocate: partyLiquidityFee, err := partyLiquidityFeeAccount() if err != nil { @@ -2640,9 +2733,9 @@ func (e *Engine) getGovernanceTransferFundsTransferRequest(ctx context.Context, // this could not exists as well, let's just create in this case case types.AccountTypeGlobalReward, types.AccountTypeLPFeeReward, types.AccountTypeMakerReceivedFeeReward, - types.AccountTypeMakerPaidFeeReward, types.AccountTypeMarketProposerReward, types.AccountTypeAveragePositionReward, + types.AccountTypeMakerPaidFeeReward, types.AccountTypeMarketProposerReward, types.AccountTypeAverageNotionalReward, types.AccountTypeRelativeReturnReward, types.AccountTypeReturnVolatilityReward, types.AccountTypeRealisedReturnReward, - types.AccountTypeValidatorRankingReward: + types.AccountTypeValidatorRankingReward, types.AccountTypeEligibleEntitiesReward: market := noMarket if len(t.Market) > 0 { market = t.Market @@ -2747,9 +2840,9 @@ func (e *Engine) getTransferFundsTransferRequest(ctx context.Context, t *types.T // this could not exists as well, let's just create in this case case types.AccountTypeGlobalReward, types.AccountTypeLPFeeReward, types.AccountTypeMakerReceivedFeeReward, types.AccountTypeNetworkTreasury, - types.AccountTypeMakerPaidFeeReward, types.AccountTypeMarketProposerReward, types.AccountTypeAveragePositionReward, + types.AccountTypeMakerPaidFeeReward, types.AccountTypeMarketProposerReward, types.AccountTypeAverageNotionalReward, types.AccountTypeRelativeReturnReward, types.AccountTypeReturnVolatilityReward, types.AccountTypeRealisedReturnReward, - types.AccountTypeValidatorRankingReward: + types.AccountTypeValidatorRankingReward, types.AccountTypeEligibleEntitiesReward: market := noMarket if len(t.Market) > 0 { market = t.Market @@ -4594,6 +4687,26 @@ func (e *Engine) GetNetworkTreasuryAccount(asset string) (*types.Account, error) return e.GetAccountByID(e.accountID(noMarket, systemOwner, asset, types.AccountTypeNetworkTreasury)) } +func (e *Engine) getOrCreateBuyBackFeesAccount(ctx context.Context, asset string) *types.Account { + accID := e.accountID(noMarket, systemOwner, asset, types.AccountTypeBuyBackFees) + 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: types.AccountTypeBuyBackFees, + } + e.accs[accID] = ntAcc + e.addAccountToHashableSlice(ntAcc) + e.broker.Send(events.NewAccountEvent(ctx, *ntAcc)) + return ntAcc +} + func (e *Engine) GetOrCreateNetworkTreasuryAccount(ctx context.Context, asset string) *types.Account { accID := e.accountID(noMarket, systemOwner, asset, types.AccountTypeNetworkTreasury) acc, err := e.GetAccountByID(accID) diff --git a/core/collateral/engine_test.go b/core/collateral/engine_test.go index d418e8a015f..64a589824f5 100644 --- a/core/collateral/engine_test.go +++ b/core/collateral/engine_test.go @@ -574,7 +574,7 @@ func testFeeTransferContinuousOKWithEnoughInGenral(t *testing.T) { defer eng.Finish() party := "myparty" // create party - eng.broker.EXPECT().Send(gomock.Any()).Times(3) + eng.broker.EXPECT().Send(gomock.Any()).Times(4) general, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) require.NoError(t, err) @@ -617,7 +617,7 @@ func testFeeTransferContinuousOKWith0Amount(t *testing.T) { defer eng.Finish() party := "myparty" // create party - eng.broker.EXPECT().Send(gomock.Any()).Times(3) + eng.broker.EXPECT().Send(gomock.Any()).Times(4) general, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) require.NoError(t, err) @@ -662,7 +662,7 @@ func testFeeTransferContinuousOKWithEnoughInMargin(t *testing.T) { defer eng.Finish() party := "myparty" // create party - eng.broker.EXPECT().Send(gomock.Any()).Times(3) + eng.broker.EXPECT().Send(gomock.Any()).Times(4) _, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) require.NoError(t, err) @@ -704,7 +704,7 @@ func testFeeTransferContinuousOKCheckAccountEvents(t *testing.T) { defer eng.Finish() party := "myparty" // create party - eng.broker.EXPECT().Send(gomock.Any()).Times(3) + eng.broker.EXPECT().Send(gomock.Any()).Times(4) _, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) require.NoError(t, err) @@ -776,7 +776,7 @@ func testFeeTransferContinuousOKWithEnoughInGeneralAndMargin(t *testing.T) { defer eng.Finish() party := "myparty" // create party - eng.broker.EXPECT().Send(gomock.Any()).Times(3) + eng.broker.EXPECT().Send(gomock.Any()).Times(4) general, err := eng.CreatePartyGeneralAccount(context.Background(), party, testMarketAsset) require.NoError(t, err) diff --git a/core/contracts/ERC20_Bridge_Logic_Restricted.abi b/core/contracts/ERC20_Bridge_Logic_Restricted.abi index 6a253ca9b7c..65c254b2d34 100644 --- a/core/contracts/ERC20_Bridge_Logic_Restricted.abi +++ b/core/contracts/ERC20_Bridge_Logic_Restricted.abi @@ -1 +1,539 @@ -[{"inputs":[{"internalType":"address payable","name":"erc20_asset_pool","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user_address","type":"address"},{"indexed":true,"internalType":"address","name":"asset_source","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"vega_public_key","type":"bytes32"}],"name":"Asset_Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset_source","type":"address"},{"indexed":false,"internalType":"uint256","name":"lifetime_limit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"withdraw_threshold","type":"uint256"}],"name":"Asset_Limits_Updated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset_source","type":"address"},{"indexed":true,"internalType":"bytes32","name":"vega_asset_id","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"Asset_Listed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset_source","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"Asset_Removed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user_address","type":"address"},{"indexed":true,"internalType":"address","name":"asset_source","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"Asset_Withdrawn","type":"event"},{"anonymous":false,"inputs":[],"name":"Bridge_Resumed","type":"event"},{"anonymous":false,"inputs":[],"name":"Bridge_Stopped","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"withdraw_delay","type":"uint256"}],"name":"Bridge_Withdraw_Delay_Set","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"}],"name":"Depositor_Exempted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"}],"name":"Depositor_Exemption_Revoked","type":"event"},{"inputs":[],"name":"default_withdraw_delay","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset_source","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"vega_public_key","type":"bytes32"}],"name":"deposit_asset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"erc20_asset_pool_address","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"exempt_depositor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset_source","type":"address"}],"name":"get_asset_deposit_lifetime_limit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"vega_asset_id","type":"bytes32"}],"name":"get_asset_source","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"get_multisig_control_address","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset_source","type":"address"}],"name":"get_vega_asset_id","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset_source","type":"address"}],"name":"get_withdraw_threshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"global_resume","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"global_stop","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset_source","type":"address"}],"name":"is_asset_listed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"depositor","type":"address"}],"name":"is_exempt_depositor","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"is_stopped","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset_source","type":"address"},{"internalType":"bytes32","name":"vega_asset_id","type":"bytes32"},{"internalType":"uint256","name":"lifetime_limit","type":"uint256"},{"internalType":"uint256","name":"withdraw_threshold","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"list_asset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset_source","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"remove_asset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revoke_exempt_depositor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset_source","type":"address"},{"internalType":"uint256","name":"lifetime_limit","type":"uint256"},{"internalType":"uint256","name":"threshold","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"set_asset_limits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"delay","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"set_withdraw_delay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset_source","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"target","type":"address"},{"internalType":"uint256","name":"creation","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"withdraw_asset","outputs":[],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file +[ + { + "type": "function", + "name": "depositAsset", + "inputs": [ + { + "name": "assetSource", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "vegaPublicKey", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "exemptDepositor", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getAssetDepositLifetimeLimit", + "inputs": [ + { + "name": "assetSource", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getAssetSource", + "inputs": [ + { + "name": "vegaAssetId", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getMultisigControlAddress", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "address", + "internalType": "address" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getVegaAssetId", + "inputs": [ + { + "name": "assetSource", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bytes32", + "internalType": "bytes32" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getWithdrawThreshold", + "inputs": [ + { + "name": "assetSource", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "uint256", + "internalType": "uint256" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "globalResume", + "inputs": [ + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "signatures", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "globalStop", + "inputs": [ + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "signatures", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "isAssetListed", + "inputs": [ + { + "name": "assetSource", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isExemptDepositor", + "inputs": [ + { + "name": "depositor", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "listAsset", + "inputs": [ + { + "name": "assetSource", + "type": "address", + "internalType": "address" + }, + { + "name": "vegaAssetId", + "type": "bytes32", + "internalType": "bytes32" + }, + { + "name": "lifetimeLimit", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "withdrawThreshold", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "signatures", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "removeAsset", + "inputs": [ + { + "name": "assetSource", + "type": "address", + "internalType": "address" + }, + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "signatures", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "revokeExemptDepositor", + "inputs": [], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setAssetLimits", + "inputs": [ + { + "name": "assetSource", + "type": "address", + "internalType": "address" + }, + { + "name": "lifetimeLimit", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "threshold", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "signatures", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setWithdrawDelay", + "inputs": [ + { + "name": "delay", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "signatures", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "withdrawAsset", + "inputs": [ + { + "name": "assetSource", + "type": "address", + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "target", + "type": "address", + "internalType": "address" + }, + { + "name": "creation", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "signatures", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "AssetDeposited", + "inputs": [ + { + "name": "userAddress", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "assetSource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "vegaPublicKey", + "type": "bytes32", + "indexed": false, + "internalType": "bytes32" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "AssetLimitsUpdated", + "inputs": [ + { + "name": "assetSource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "lifetimeLimit", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "withdrawThreshold", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "AssetListed", + "inputs": [ + { + "name": "assetSource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "vegaAssetId", + "type": "bytes32", + "indexed": true, + "internalType": "bytes32" + }, + { + "name": "nonce", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "AssetRemoved", + "inputs": [ + { + "name": "assetSource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "nonce", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "AssetWithdrawn", + "inputs": [ + { + "name": "userAddress", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "assetSource", + "type": "address", + "indexed": true, + "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "nonce", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeResumed", + "inputs": [], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeStopped", + "inputs": [], + "anonymous": false + }, + { + "type": "event", + "name": "BridgeWithdrawDelaySet", + "inputs": [ + { + "name": "withdraw_delay", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "DepositorExempted", + "inputs": [ + { + "name": "depositor", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "DepositorExemptionRevoked", + "inputs": [ + { + "name": "depositor", + "type": "address", + "indexed": true, + "internalType": "address" + } + ], + "anonymous": false + } +] diff --git a/core/contracts/MultisigControl.abi b/core/contracts/MultisigControl.abi index 966e302c270..cff1dd08ec2 100644 --- a/core/contracts/MultisigControl.abi +++ b/core/contracts/MultisigControl.abi @@ -1 +1,252 @@ -[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"NonceBurnt","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"new_signer","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"SignerAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"old_signer","type":"address"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"SignerRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"new_threshold","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"ThresholdSet","type":"event"},{"inputs":[{"internalType":"address","name":"new_signer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"add_signer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"burn_nonce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"get_current_threshold","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"get_valid_signer_count","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"is_nonce_used","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"signer_address","type":"address"}],"name":"is_valid_signer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"old_signer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"remove_signer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"new_threshold","type":"uint16"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"bytes","name":"signatures","type":"bytes"}],"name":"set_threshold","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"signers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"signatures","type":"bytes"},{"internalType":"bytes","name":"message","type":"bytes"},{"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"verify_signatures","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file +[ + { + "type": "function", + "name": "addSigner", + "inputs": [ + { + "name": "newSigner", + "type": "address", + "internalType": "address" + }, + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "signatures", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "burnNonce", + "inputs": [ + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "signatures", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "getCurrentThreshold", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint16", + "internalType": "uint16" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "getValidSignerCount", + "inputs": [], + "outputs": [ + { + "name": "", + "type": "uint8", + "internalType": "uint8" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isNonceUsed", + "inputs": [ + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "isValidSigner", + "inputs": [ + { + "name": "signer_address", + "type": "address", + "internalType": "address" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "view" + }, + { + "type": "function", + "name": "removeSigner", + "inputs": [ + { + "name": "oldSigner", + "type": "address", + "internalType": "address" + }, + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "signatures", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "setThreshold", + "inputs": [ + { + "name": "newThreshold", + "type": "uint16", + "internalType": "uint16" + }, + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + }, + { + "name": "signatures", + "type": "bytes", + "internalType": "bytes" + } + ], + "outputs": [], + "stateMutability": "nonpayable" + }, + { + "type": "function", + "name": "verifySignatures", + "inputs": [ + { + "name": "signatures", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "message", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "nonce", + "type": "uint256", + "internalType": "uint256" + } + ], + "outputs": [ + { + "name": "", + "type": "bool", + "internalType": "bool" + } + ], + "stateMutability": "nonpayable" + }, + { + "type": "event", + "name": "NonceBurnt", + "inputs": [ + { + "name": "nonce", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SignerAdded", + "inputs": [ + { + "name": "newSigner", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "nonce", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "SignerRemoved", + "inputs": [ + { + "name": "oldSigner", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "nonce", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + }, + { + "type": "event", + "name": "ThresholdSet", + "inputs": [ + { + "name": "newThreshold", + "type": "uint16", + "indexed": false, + "internalType": "uint16" + }, + { + "name": "nonce", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + } + ], + "anonymous": false + } +] diff --git a/core/contracts/erc20_bridge_logic_restricted/erc20_bridge_logic_restricted.go b/core/contracts/erc20_bridge_logic_restricted/erc20_bridge_logic_restricted.go index 9421d767f8b..4ad9bd8c2e0 100644 --- a/core/contracts/erc20_bridge_logic_restricted/erc20_bridge_logic_restricted.go +++ b/core/contracts/erc20_bridge_logic_restricted/erc20_bridge_logic_restricted.go @@ -31,7 +31,7 @@ var ( // Erc20BridgeLogicRestrictedMetaData contains all meta data concerning the Erc20BridgeLogicRestricted contract. var Erc20BridgeLogicRestrictedMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[{\"internalType\":\"addresspayable\",\"name\":\"erc20_asset_pool\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user_address\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"vega_public_key\",\"type\":\"bytes32\"}],\"name\":\"Asset_Deposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"lifetime_limit\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"withdraw_threshold\",\"type\":\"uint256\"}],\"name\":\"Asset_Limits_Updated\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"vega_asset_id\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"Asset_Listed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"Asset_Removed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"user_address\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"Asset_Withdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"Bridge_Resumed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"Bridge_Stopped\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"withdraw_delay\",\"type\":\"uint256\"}],\"name\":\"Bridge_Withdraw_Delay_Set\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"}],\"name\":\"Depositor_Exempted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"}],\"name\":\"Depositor_Exemption_Revoked\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"default_withdraw_delay\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"vega_public_key\",\"type\":\"bytes32\"}],\"name\":\"deposit_asset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"erc20_asset_pool_address\",\"outputs\":[{\"internalType\":\"addresspayable\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"exempt_depositor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"}],\"name\":\"get_asset_deposit_lifetime_limit\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"vega_asset_id\",\"type\":\"bytes32\"}],\"name\":\"get_asset_source\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"get_multisig_control_address\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"}],\"name\":\"get_vega_asset_id\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"}],\"name\":\"get_withdraw_threshold\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"global_resume\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"global_stop\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"}],\"name\":\"is_asset_listed\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"depositor\",\"type\":\"address\"}],\"name\":\"is_exempt_depositor\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"is_stopped\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"},{\"internalType\":\"bytes32\",\"name\":\"vega_asset_id\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"lifetime_limit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"withdraw_threshold\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"list_asset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"remove_asset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"revoke_exempt_depositor\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"lifetime_limit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"threshold\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"set_asset_limits\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"delay\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"set_withdraw_delay\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"asset_source\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"creation\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"withdraw_asset\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"type\":\"function\",\"name\":\"depositAsset\",\"inputs\":[{\"name\":\"assetSource\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"vegaPublicKey\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"exemptDepositor\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"getAssetDepositLifetimeLimit\",\"inputs\":[{\"name\":\"assetSource\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getAssetSource\",\"inputs\":[{\"name\":\"vegaAssetId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getMultisigControlAddress\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"address\",\"internalType\":\"address\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getVegaAssetId\",\"inputs\":[{\"name\":\"assetSource\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getWithdrawThreshold\",\"inputs\":[{\"name\":\"assetSource\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"globalResume\",\"inputs\":[{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"signatures\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"globalStop\",\"inputs\":[{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"signatures\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"isAssetListed\",\"inputs\":[{\"name\":\"assetSource\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isExemptDepositor\",\"inputs\":[{\"name\":\"depositor\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"listAsset\",\"inputs\":[{\"name\":\"assetSource\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"vegaAssetId\",\"type\":\"bytes32\",\"internalType\":\"bytes32\"},{\"name\":\"lifetimeLimit\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"withdrawThreshold\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"signatures\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"removeAsset\",\"inputs\":[{\"name\":\"assetSource\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"signatures\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"revokeExemptDepositor\",\"inputs\":[],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setAssetLimits\",\"inputs\":[{\"name\":\"assetSource\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"lifetimeLimit\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"threshold\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"signatures\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setWithdrawDelay\",\"inputs\":[{\"name\":\"delay\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"signatures\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"withdrawAsset\",\"inputs\":[{\"name\":\"assetSource\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"target\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"creation\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"signatures\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"AssetDeposited\",\"inputs\":[{\"name\":\"userAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"assetSource\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"vegaPublicKey\",\"type\":\"bytes32\",\"indexed\":false,\"internalType\":\"bytes32\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"AssetLimitsUpdated\",\"inputs\":[{\"name\":\"assetSource\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"lifetimeLimit\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"withdrawThreshold\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"AssetListed\",\"inputs\":[{\"name\":\"assetSource\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"vegaAssetId\",\"type\":\"bytes32\",\"indexed\":true,\"internalType\":\"bytes32\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"AssetRemoved\",\"inputs\":[{\"name\":\"assetSource\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"AssetWithdrawn\",\"inputs\":[{\"name\":\"userAddress\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"assetSource\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"},{\"name\":\"amount\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"BridgeResumed\",\"inputs\":[],\"anonymous\":false},{\"type\":\"event\",\"name\":\"BridgeStopped\",\"inputs\":[],\"anonymous\":false},{\"type\":\"event\",\"name\":\"BridgeWithdrawDelaySet\",\"inputs\":[{\"name\":\"withdraw_delay\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"DepositorExempted\",\"inputs\":[{\"name\":\"depositor\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"DepositorExemptionRevoked\",\"inputs\":[{\"name\":\"depositor\",\"type\":\"address\",\"indexed\":true,\"internalType\":\"address\"}],\"anonymous\":false}]", } // Erc20BridgeLogicRestrictedABI is the input ABI used to generate the binding from. @@ -180,12 +180,12 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorRaw) Tran return _Erc20BridgeLogicRestricted.Contract.contract.Transact(opts, method, params...) } -// DefaultWithdrawDelay is a free data retrieval call binding the contract method 0x3f4f199d. +// GetAssetDepositLifetimeLimit is a free data retrieval call binding the contract method 0x5d1e1a73. // -// Solidity: function default_withdraw_delay() view returns(uint256) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) DefaultWithdrawDelay(opts *bind.CallOpts) (*big.Int, error) { +// Solidity: function getAssetDepositLifetimeLimit(address assetSource) view returns(uint256) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) GetAssetDepositLifetimeLimit(opts *bind.CallOpts, assetSource common.Address) (*big.Int, error) { var out []interface{} - err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "default_withdraw_delay") + err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "getAssetDepositLifetimeLimit", assetSource) if err != nil { return *new(*big.Int), err @@ -197,26 +197,26 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) DefaultWith } -// DefaultWithdrawDelay is a free data retrieval call binding the contract method 0x3f4f199d. +// GetAssetDepositLifetimeLimit is a free data retrieval call binding the contract method 0x5d1e1a73. // -// Solidity: function default_withdraw_delay() view returns(uint256) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) DefaultWithdrawDelay() (*big.Int, error) { - return _Erc20BridgeLogicRestricted.Contract.DefaultWithdrawDelay(&_Erc20BridgeLogicRestricted.CallOpts) +// Solidity: function getAssetDepositLifetimeLimit(address assetSource) view returns(uint256) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) GetAssetDepositLifetimeLimit(assetSource common.Address) (*big.Int, error) { + return _Erc20BridgeLogicRestricted.Contract.GetAssetDepositLifetimeLimit(&_Erc20BridgeLogicRestricted.CallOpts, assetSource) } -// DefaultWithdrawDelay is a free data retrieval call binding the contract method 0x3f4f199d. +// GetAssetDepositLifetimeLimit is a free data retrieval call binding the contract method 0x5d1e1a73. // -// Solidity: function default_withdraw_delay() view returns(uint256) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) DefaultWithdrawDelay() (*big.Int, error) { - return _Erc20BridgeLogicRestricted.Contract.DefaultWithdrawDelay(&_Erc20BridgeLogicRestricted.CallOpts) +// Solidity: function getAssetDepositLifetimeLimit(address assetSource) view returns(uint256) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) GetAssetDepositLifetimeLimit(assetSource common.Address) (*big.Int, error) { + return _Erc20BridgeLogicRestricted.Contract.GetAssetDepositLifetimeLimit(&_Erc20BridgeLogicRestricted.CallOpts, assetSource) } -// Erc20AssetPoolAddress is a free data retrieval call binding the contract method 0x9356aab8. +// GetAssetSource is a free data retrieval call binding the contract method 0xb653e56d. // -// Solidity: function erc20_asset_pool_address() view returns(address) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) Erc20AssetPoolAddress(opts *bind.CallOpts) (common.Address, error) { +// Solidity: function getAssetSource(bytes32 vegaAssetId) view returns(address) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) GetAssetSource(opts *bind.CallOpts, vegaAssetId [32]byte) (common.Address, error) { var out []interface{} - err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "erc20_asset_pool_address") + err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "getAssetSource", vegaAssetId) if err != nil { return *new(common.Address), err @@ -228,88 +228,26 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) Erc20AssetP } -// Erc20AssetPoolAddress is a free data retrieval call binding the contract method 0x9356aab8. +// GetAssetSource is a free data retrieval call binding the contract method 0xb653e56d. // -// Solidity: function erc20_asset_pool_address() view returns(address) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) Erc20AssetPoolAddress() (common.Address, error) { - return _Erc20BridgeLogicRestricted.Contract.Erc20AssetPoolAddress(&_Erc20BridgeLogicRestricted.CallOpts) +// Solidity: function getAssetSource(bytes32 vegaAssetId) view returns(address) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) GetAssetSource(vegaAssetId [32]byte) (common.Address, error) { + return _Erc20BridgeLogicRestricted.Contract.GetAssetSource(&_Erc20BridgeLogicRestricted.CallOpts, vegaAssetId) } -// Erc20AssetPoolAddress is a free data retrieval call binding the contract method 0x9356aab8. +// GetAssetSource is a free data retrieval call binding the contract method 0xb653e56d. // -// Solidity: function erc20_asset_pool_address() view returns(address) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) Erc20AssetPoolAddress() (common.Address, error) { - return _Erc20BridgeLogicRestricted.Contract.Erc20AssetPoolAddress(&_Erc20BridgeLogicRestricted.CallOpts) +// Solidity: function getAssetSource(bytes32 vegaAssetId) view returns(address) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) GetAssetSource(vegaAssetId [32]byte) (common.Address, error) { + return _Erc20BridgeLogicRestricted.Contract.GetAssetSource(&_Erc20BridgeLogicRestricted.CallOpts, vegaAssetId) } -// GetAssetDepositLifetimeLimit is a free data retrieval call binding the contract method 0x354a897a. +// GetMultisigControlAddress is a free data retrieval call binding the contract method 0x81a8915e. // -// Solidity: function get_asset_deposit_lifetime_limit(address asset_source) view returns(uint256) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) GetAssetDepositLifetimeLimit(opts *bind.CallOpts, asset_source common.Address) (*big.Int, error) { - var out []interface{} - err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "get_asset_deposit_lifetime_limit", asset_source) - - if err != nil { - return *new(*big.Int), err - } - - out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) - - return out0, err - -} - -// GetAssetDepositLifetimeLimit is a free data retrieval call binding the contract method 0x354a897a. -// -// Solidity: function get_asset_deposit_lifetime_limit(address asset_source) view returns(uint256) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) GetAssetDepositLifetimeLimit(asset_source common.Address) (*big.Int, error) { - return _Erc20BridgeLogicRestricted.Contract.GetAssetDepositLifetimeLimit(&_Erc20BridgeLogicRestricted.CallOpts, asset_source) -} - -// GetAssetDepositLifetimeLimit is a free data retrieval call binding the contract method 0x354a897a. -// -// Solidity: function get_asset_deposit_lifetime_limit(address asset_source) view returns(uint256) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) GetAssetDepositLifetimeLimit(asset_source common.Address) (*big.Int, error) { - return _Erc20BridgeLogicRestricted.Contract.GetAssetDepositLifetimeLimit(&_Erc20BridgeLogicRestricted.CallOpts, asset_source) -} - -// GetAssetSource is a free data retrieval call binding the contract method 0x786b0bc0. -// -// Solidity: function get_asset_source(bytes32 vega_asset_id) view returns(address) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) GetAssetSource(opts *bind.CallOpts, vega_asset_id [32]byte) (common.Address, error) { - var out []interface{} - err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "get_asset_source", vega_asset_id) - - if err != nil { - return *new(common.Address), err - } - - out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) - - return out0, err - -} - -// GetAssetSource is a free data retrieval call binding the contract method 0x786b0bc0. -// -// Solidity: function get_asset_source(bytes32 vega_asset_id) view returns(address) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) GetAssetSource(vega_asset_id [32]byte) (common.Address, error) { - return _Erc20BridgeLogicRestricted.Contract.GetAssetSource(&_Erc20BridgeLogicRestricted.CallOpts, vega_asset_id) -} - -// GetAssetSource is a free data retrieval call binding the contract method 0x786b0bc0. -// -// Solidity: function get_asset_source(bytes32 vega_asset_id) view returns(address) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) GetAssetSource(vega_asset_id [32]byte) (common.Address, error) { - return _Erc20BridgeLogicRestricted.Contract.GetAssetSource(&_Erc20BridgeLogicRestricted.CallOpts, vega_asset_id) -} - -// GetMultisigControlAddress is a free data retrieval call binding the contract method 0xc58dc3b9. -// -// Solidity: function get_multisig_control_address() view returns(address) +// Solidity: function getMultisigControlAddress() view returns(address) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) GetMultisigControlAddress(opts *bind.CallOpts) (common.Address, error) { var out []interface{} - err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "get_multisig_control_address") + err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "getMultisigControlAddress") if err != nil { return *new(common.Address), err @@ -321,26 +259,26 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) GetMultisig } -// GetMultisigControlAddress is a free data retrieval call binding the contract method 0xc58dc3b9. +// GetMultisigControlAddress is a free data retrieval call binding the contract method 0x81a8915e. // -// Solidity: function get_multisig_control_address() view returns(address) +// Solidity: function getMultisigControlAddress() view returns(address) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) GetMultisigControlAddress() (common.Address, error) { return _Erc20BridgeLogicRestricted.Contract.GetMultisigControlAddress(&_Erc20BridgeLogicRestricted.CallOpts) } -// GetMultisigControlAddress is a free data retrieval call binding the contract method 0xc58dc3b9. +// GetMultisigControlAddress is a free data retrieval call binding the contract method 0x81a8915e. // -// Solidity: function get_multisig_control_address() view returns(address) +// Solidity: function getMultisigControlAddress() view returns(address) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) GetMultisigControlAddress() (common.Address, error) { return _Erc20BridgeLogicRestricted.Contract.GetMultisigControlAddress(&_Erc20BridgeLogicRestricted.CallOpts) } -// GetVegaAssetId is a free data retrieval call binding the contract method 0xa06b5d39. +// GetVegaAssetId is a free data retrieval call binding the contract method 0xf8c2dbe0. // -// Solidity: function get_vega_asset_id(address asset_source) view returns(bytes32) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) GetVegaAssetId(opts *bind.CallOpts, asset_source common.Address) ([32]byte, error) { +// Solidity: function getVegaAssetId(address assetSource) view returns(bytes32) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) GetVegaAssetId(opts *bind.CallOpts, assetSource common.Address) ([32]byte, error) { var out []interface{} - err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "get_vega_asset_id", asset_source) + err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "getVegaAssetId", assetSource) if err != nil { return *new([32]byte), err @@ -352,26 +290,26 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) GetVegaAsse } -// GetVegaAssetId is a free data retrieval call binding the contract method 0xa06b5d39. +// GetVegaAssetId is a free data retrieval call binding the contract method 0xf8c2dbe0. // -// Solidity: function get_vega_asset_id(address asset_source) view returns(bytes32) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) GetVegaAssetId(asset_source common.Address) ([32]byte, error) { - return _Erc20BridgeLogicRestricted.Contract.GetVegaAssetId(&_Erc20BridgeLogicRestricted.CallOpts, asset_source) +// Solidity: function getVegaAssetId(address assetSource) view returns(bytes32) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) GetVegaAssetId(assetSource common.Address) ([32]byte, error) { + return _Erc20BridgeLogicRestricted.Contract.GetVegaAssetId(&_Erc20BridgeLogicRestricted.CallOpts, assetSource) } -// GetVegaAssetId is a free data retrieval call binding the contract method 0xa06b5d39. +// GetVegaAssetId is a free data retrieval call binding the contract method 0xf8c2dbe0. // -// Solidity: function get_vega_asset_id(address asset_source) view returns(bytes32) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) GetVegaAssetId(asset_source common.Address) ([32]byte, error) { - return _Erc20BridgeLogicRestricted.Contract.GetVegaAssetId(&_Erc20BridgeLogicRestricted.CallOpts, asset_source) +// Solidity: function getVegaAssetId(address assetSource) view returns(bytes32) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) GetVegaAssetId(assetSource common.Address) ([32]byte, error) { + return _Erc20BridgeLogicRestricted.Contract.GetVegaAssetId(&_Erc20BridgeLogicRestricted.CallOpts, assetSource) } -// GetWithdrawThreshold is a free data retrieval call binding the contract method 0xe8a7bce0. +// GetWithdrawThreshold is a free data retrieval call binding the contract method 0xf3dd4013. // -// Solidity: function get_withdraw_threshold(address asset_source) view returns(uint256) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) GetWithdrawThreshold(opts *bind.CallOpts, asset_source common.Address) (*big.Int, error) { +// Solidity: function getWithdrawThreshold(address assetSource) view returns(uint256) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) GetWithdrawThreshold(opts *bind.CallOpts, assetSource common.Address) (*big.Int, error) { var out []interface{} - err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "get_withdraw_threshold", asset_source) + err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "getWithdrawThreshold", assetSource) if err != nil { return *new(*big.Int), err @@ -383,26 +321,26 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) GetWithdraw } -// GetWithdrawThreshold is a free data retrieval call binding the contract method 0xe8a7bce0. +// GetWithdrawThreshold is a free data retrieval call binding the contract method 0xf3dd4013. // -// Solidity: function get_withdraw_threshold(address asset_source) view returns(uint256) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) GetWithdrawThreshold(asset_source common.Address) (*big.Int, error) { - return _Erc20BridgeLogicRestricted.Contract.GetWithdrawThreshold(&_Erc20BridgeLogicRestricted.CallOpts, asset_source) +// Solidity: function getWithdrawThreshold(address assetSource) view returns(uint256) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) GetWithdrawThreshold(assetSource common.Address) (*big.Int, error) { + return _Erc20BridgeLogicRestricted.Contract.GetWithdrawThreshold(&_Erc20BridgeLogicRestricted.CallOpts, assetSource) } -// GetWithdrawThreshold is a free data retrieval call binding the contract method 0xe8a7bce0. +// GetWithdrawThreshold is a free data retrieval call binding the contract method 0xf3dd4013. // -// Solidity: function get_withdraw_threshold(address asset_source) view returns(uint256) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) GetWithdrawThreshold(asset_source common.Address) (*big.Int, error) { - return _Erc20BridgeLogicRestricted.Contract.GetWithdrawThreshold(&_Erc20BridgeLogicRestricted.CallOpts, asset_source) +// Solidity: function getWithdrawThreshold(address assetSource) view returns(uint256) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) GetWithdrawThreshold(assetSource common.Address) (*big.Int, error) { + return _Erc20BridgeLogicRestricted.Contract.GetWithdrawThreshold(&_Erc20BridgeLogicRestricted.CallOpts, assetSource) } -// IsAssetListed is a free data retrieval call binding the contract method 0x7fd27b7f. +// IsAssetListed is a free data retrieval call binding the contract method 0x47eaef01. // -// Solidity: function is_asset_listed(address asset_source) view returns(bool) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) IsAssetListed(opts *bind.CallOpts, asset_source common.Address) (bool, error) { +// Solidity: function isAssetListed(address assetSource) view returns(bool) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) IsAssetListed(opts *bind.CallOpts, assetSource common.Address) (bool, error) { var out []interface{} - err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "is_asset_listed", asset_source) + err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "isAssetListed", assetSource) if err != nil { return *new(bool), err @@ -414,26 +352,26 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) IsAssetList } -// IsAssetListed is a free data retrieval call binding the contract method 0x7fd27b7f. +// IsAssetListed is a free data retrieval call binding the contract method 0x47eaef01. // -// Solidity: function is_asset_listed(address asset_source) view returns(bool) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) IsAssetListed(asset_source common.Address) (bool, error) { - return _Erc20BridgeLogicRestricted.Contract.IsAssetListed(&_Erc20BridgeLogicRestricted.CallOpts, asset_source) +// Solidity: function isAssetListed(address assetSource) view returns(bool) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) IsAssetListed(assetSource common.Address) (bool, error) { + return _Erc20BridgeLogicRestricted.Contract.IsAssetListed(&_Erc20BridgeLogicRestricted.CallOpts, assetSource) } -// IsAssetListed is a free data retrieval call binding the contract method 0x7fd27b7f. +// IsAssetListed is a free data retrieval call binding the contract method 0x47eaef01. // -// Solidity: function is_asset_listed(address asset_source) view returns(bool) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) IsAssetListed(asset_source common.Address) (bool, error) { - return _Erc20BridgeLogicRestricted.Contract.IsAssetListed(&_Erc20BridgeLogicRestricted.CallOpts, asset_source) +// Solidity: function isAssetListed(address assetSource) view returns(bool) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) IsAssetListed(assetSource common.Address) (bool, error) { + return _Erc20BridgeLogicRestricted.Contract.IsAssetListed(&_Erc20BridgeLogicRestricted.CallOpts, assetSource) } -// IsExemptDepositor is a free data retrieval call binding the contract method 0x15c0df9d. +// IsExemptDepositor is a free data retrieval call binding the contract method 0xe275317e. // -// Solidity: function is_exempt_depositor(address depositor) view returns(bool) +// Solidity: function isExemptDepositor(address depositor) view returns(bool) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) IsExemptDepositor(opts *bind.CallOpts, depositor common.Address) (bool, error) { var out []interface{} - err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "is_exempt_depositor", depositor) + err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "isExemptDepositor", depositor) if err != nil { return *new(bool), err @@ -445,259 +383,228 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) IsExemptDep } -// IsExemptDepositor is a free data retrieval call binding the contract method 0x15c0df9d. +// IsExemptDepositor is a free data retrieval call binding the contract method 0xe275317e. // -// Solidity: function is_exempt_depositor(address depositor) view returns(bool) +// Solidity: function isExemptDepositor(address depositor) view returns(bool) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) IsExemptDepositor(depositor common.Address) (bool, error) { return _Erc20BridgeLogicRestricted.Contract.IsExemptDepositor(&_Erc20BridgeLogicRestricted.CallOpts, depositor) } -// IsExemptDepositor is a free data retrieval call binding the contract method 0x15c0df9d. +// IsExemptDepositor is a free data retrieval call binding the contract method 0xe275317e. // -// Solidity: function is_exempt_depositor(address depositor) view returns(bool) +// Solidity: function isExemptDepositor(address depositor) view returns(bool) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) IsExemptDepositor(depositor common.Address) (bool, error) { return _Erc20BridgeLogicRestricted.Contract.IsExemptDepositor(&_Erc20BridgeLogicRestricted.CallOpts, depositor) } -// IsStopped is a free data retrieval call binding the contract method 0xe272e9d0. -// -// Solidity: function is_stopped() view returns(bool) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCaller) IsStopped(opts *bind.CallOpts) (bool, error) { - var out []interface{} - err := _Erc20BridgeLogicRestricted.contract.Call(opts, &out, "is_stopped") - - if err != nil { - return *new(bool), err - } - - out0 := *abi.ConvertType(out[0], new(bool)).(*bool) - - return out0, err - -} - -// IsStopped is a free data retrieval call binding the contract method 0xe272e9d0. -// -// Solidity: function is_stopped() view returns(bool) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) IsStopped() (bool, error) { - return _Erc20BridgeLogicRestricted.Contract.IsStopped(&_Erc20BridgeLogicRestricted.CallOpts) -} - -// IsStopped is a free data retrieval call binding the contract method 0xe272e9d0. -// -// Solidity: function is_stopped() view returns(bool) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedCallerSession) IsStopped() (bool, error) { - return _Erc20BridgeLogicRestricted.Contract.IsStopped(&_Erc20BridgeLogicRestricted.CallOpts) -} - -// DepositAsset is a paid mutator transaction binding the contract method 0xf7683932. +// DepositAsset is a paid mutator transaction binding the contract method 0xa372b1d2. // -// Solidity: function deposit_asset(address asset_source, uint256 amount, bytes32 vega_public_key) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) DepositAsset(opts *bind.TransactOpts, asset_source common.Address, amount *big.Int, vega_public_key [32]byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.contract.Transact(opts, "deposit_asset", asset_source, amount, vega_public_key) +// Solidity: function depositAsset(address assetSource, uint256 amount, bytes32 vegaPublicKey) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) DepositAsset(opts *bind.TransactOpts, assetSource common.Address, amount *big.Int, vegaPublicKey [32]byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.contract.Transact(opts, "depositAsset", assetSource, amount, vegaPublicKey) } -// DepositAsset is a paid mutator transaction binding the contract method 0xf7683932. +// DepositAsset is a paid mutator transaction binding the contract method 0xa372b1d2. // -// Solidity: function deposit_asset(address asset_source, uint256 amount, bytes32 vega_public_key) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) DepositAsset(asset_source common.Address, amount *big.Int, vega_public_key [32]byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.Contract.DepositAsset(&_Erc20BridgeLogicRestricted.TransactOpts, asset_source, amount, vega_public_key) +// Solidity: function depositAsset(address assetSource, uint256 amount, bytes32 vegaPublicKey) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) DepositAsset(assetSource common.Address, amount *big.Int, vegaPublicKey [32]byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.Contract.DepositAsset(&_Erc20BridgeLogicRestricted.TransactOpts, assetSource, amount, vegaPublicKey) } -// DepositAsset is a paid mutator transaction binding the contract method 0xf7683932. +// DepositAsset is a paid mutator transaction binding the contract method 0xa372b1d2. // -// Solidity: function deposit_asset(address asset_source, uint256 amount, bytes32 vega_public_key) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) DepositAsset(asset_source common.Address, amount *big.Int, vega_public_key [32]byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.Contract.DepositAsset(&_Erc20BridgeLogicRestricted.TransactOpts, asset_source, amount, vega_public_key) +// Solidity: function depositAsset(address assetSource, uint256 amount, bytes32 vegaPublicKey) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) DepositAsset(assetSource common.Address, amount *big.Int, vegaPublicKey [32]byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.Contract.DepositAsset(&_Erc20BridgeLogicRestricted.TransactOpts, assetSource, amount, vegaPublicKey) } -// ExemptDepositor is a paid mutator transaction binding the contract method 0xb76fbb75. +// ExemptDepositor is a paid mutator transaction binding the contract method 0xfc4e5482. // -// Solidity: function exempt_depositor() returns() +// Solidity: function exemptDepositor() returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) ExemptDepositor(opts *bind.TransactOpts) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.contract.Transact(opts, "exempt_depositor") + return _Erc20BridgeLogicRestricted.contract.Transact(opts, "exemptDepositor") } -// ExemptDepositor is a paid mutator transaction binding the contract method 0xb76fbb75. +// ExemptDepositor is a paid mutator transaction binding the contract method 0xfc4e5482. // -// Solidity: function exempt_depositor() returns() +// Solidity: function exemptDepositor() returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) ExemptDepositor() (*types.Transaction, error) { return _Erc20BridgeLogicRestricted.Contract.ExemptDepositor(&_Erc20BridgeLogicRestricted.TransactOpts) } -// ExemptDepositor is a paid mutator transaction binding the contract method 0xb76fbb75. +// ExemptDepositor is a paid mutator transaction binding the contract method 0xfc4e5482. // -// Solidity: function exempt_depositor() returns() +// Solidity: function exemptDepositor() returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) ExemptDepositor() (*types.Transaction, error) { return _Erc20BridgeLogicRestricted.Contract.ExemptDepositor(&_Erc20BridgeLogicRestricted.TransactOpts) } -// GlobalResume is a paid mutator transaction binding the contract method 0xd72ed529. +// GlobalResume is a paid mutator transaction binding the contract method 0x3c00cb9c. // -// Solidity: function global_resume(uint256 nonce, bytes signatures) returns() +// Solidity: function globalResume(uint256 nonce, bytes signatures) returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) GlobalResume(opts *bind.TransactOpts, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.contract.Transact(opts, "global_resume", nonce, signatures) + return _Erc20BridgeLogicRestricted.contract.Transact(opts, "globalResume", nonce, signatures) } -// GlobalResume is a paid mutator transaction binding the contract method 0xd72ed529. +// GlobalResume is a paid mutator transaction binding the contract method 0x3c00cb9c. // -// Solidity: function global_resume(uint256 nonce, bytes signatures) returns() +// Solidity: function globalResume(uint256 nonce, bytes signatures) returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) GlobalResume(nonce *big.Int, signatures []byte) (*types.Transaction, error) { return _Erc20BridgeLogicRestricted.Contract.GlobalResume(&_Erc20BridgeLogicRestricted.TransactOpts, nonce, signatures) } -// GlobalResume is a paid mutator transaction binding the contract method 0xd72ed529. +// GlobalResume is a paid mutator transaction binding the contract method 0x3c00cb9c. // -// Solidity: function global_resume(uint256 nonce, bytes signatures) returns() +// Solidity: function globalResume(uint256 nonce, bytes signatures) returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) GlobalResume(nonce *big.Int, signatures []byte) (*types.Transaction, error) { return _Erc20BridgeLogicRestricted.Contract.GlobalResume(&_Erc20BridgeLogicRestricted.TransactOpts, nonce, signatures) } -// GlobalStop is a paid mutator transaction binding the contract method 0x9dfd3c88. +// GlobalStop is a paid mutator transaction binding the contract method 0xdaff4e2f. // -// Solidity: function global_stop(uint256 nonce, bytes signatures) returns() +// Solidity: function globalStop(uint256 nonce, bytes signatures) returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) GlobalStop(opts *bind.TransactOpts, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.contract.Transact(opts, "global_stop", nonce, signatures) + return _Erc20BridgeLogicRestricted.contract.Transact(opts, "globalStop", nonce, signatures) } -// GlobalStop is a paid mutator transaction binding the contract method 0x9dfd3c88. +// GlobalStop is a paid mutator transaction binding the contract method 0xdaff4e2f. // -// Solidity: function global_stop(uint256 nonce, bytes signatures) returns() +// Solidity: function globalStop(uint256 nonce, bytes signatures) returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) GlobalStop(nonce *big.Int, signatures []byte) (*types.Transaction, error) { return _Erc20BridgeLogicRestricted.Contract.GlobalStop(&_Erc20BridgeLogicRestricted.TransactOpts, nonce, signatures) } -// GlobalStop is a paid mutator transaction binding the contract method 0x9dfd3c88. +// GlobalStop is a paid mutator transaction binding the contract method 0xdaff4e2f. // -// Solidity: function global_stop(uint256 nonce, bytes signatures) returns() +// Solidity: function globalStop(uint256 nonce, bytes signatures) returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) GlobalStop(nonce *big.Int, signatures []byte) (*types.Transaction, error) { return _Erc20BridgeLogicRestricted.Contract.GlobalStop(&_Erc20BridgeLogicRestricted.TransactOpts, nonce, signatures) } -// ListAsset is a paid mutator transaction binding the contract method 0x0ff3562c. +// ListAsset is a paid mutator transaction binding the contract method 0x8ed131e0. // -// Solidity: function list_asset(address asset_source, bytes32 vega_asset_id, uint256 lifetime_limit, uint256 withdraw_threshold, uint256 nonce, bytes signatures) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) ListAsset(opts *bind.TransactOpts, asset_source common.Address, vega_asset_id [32]byte, lifetime_limit *big.Int, withdraw_threshold *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.contract.Transact(opts, "list_asset", asset_source, vega_asset_id, lifetime_limit, withdraw_threshold, nonce, signatures) +// Solidity: function listAsset(address assetSource, bytes32 vegaAssetId, uint256 lifetimeLimit, uint256 withdrawThreshold, uint256 nonce, bytes signatures) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) ListAsset(opts *bind.TransactOpts, assetSource common.Address, vegaAssetId [32]byte, lifetimeLimit *big.Int, withdrawThreshold *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.contract.Transact(opts, "listAsset", assetSource, vegaAssetId, lifetimeLimit, withdrawThreshold, nonce, signatures) } -// ListAsset is a paid mutator transaction binding the contract method 0x0ff3562c. +// ListAsset is a paid mutator transaction binding the contract method 0x8ed131e0. // -// Solidity: function list_asset(address asset_source, bytes32 vega_asset_id, uint256 lifetime_limit, uint256 withdraw_threshold, uint256 nonce, bytes signatures) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) ListAsset(asset_source common.Address, vega_asset_id [32]byte, lifetime_limit *big.Int, withdraw_threshold *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.Contract.ListAsset(&_Erc20BridgeLogicRestricted.TransactOpts, asset_source, vega_asset_id, lifetime_limit, withdraw_threshold, nonce, signatures) +// Solidity: function listAsset(address assetSource, bytes32 vegaAssetId, uint256 lifetimeLimit, uint256 withdrawThreshold, uint256 nonce, bytes signatures) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) ListAsset(assetSource common.Address, vegaAssetId [32]byte, lifetimeLimit *big.Int, withdrawThreshold *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.Contract.ListAsset(&_Erc20BridgeLogicRestricted.TransactOpts, assetSource, vegaAssetId, lifetimeLimit, withdrawThreshold, nonce, signatures) } -// ListAsset is a paid mutator transaction binding the contract method 0x0ff3562c. +// ListAsset is a paid mutator transaction binding the contract method 0x8ed131e0. // -// Solidity: function list_asset(address asset_source, bytes32 vega_asset_id, uint256 lifetime_limit, uint256 withdraw_threshold, uint256 nonce, bytes signatures) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) ListAsset(asset_source common.Address, vega_asset_id [32]byte, lifetime_limit *big.Int, withdraw_threshold *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.Contract.ListAsset(&_Erc20BridgeLogicRestricted.TransactOpts, asset_source, vega_asset_id, lifetime_limit, withdraw_threshold, nonce, signatures) +// Solidity: function listAsset(address assetSource, bytes32 vegaAssetId, uint256 lifetimeLimit, uint256 withdrawThreshold, uint256 nonce, bytes signatures) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) ListAsset(assetSource common.Address, vegaAssetId [32]byte, lifetimeLimit *big.Int, withdrawThreshold *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.Contract.ListAsset(&_Erc20BridgeLogicRestricted.TransactOpts, assetSource, vegaAssetId, lifetimeLimit, withdrawThreshold, nonce, signatures) } -// RemoveAsset is a paid mutator transaction binding the contract method 0xc76de358. +// RemoveAsset is a paid mutator transaction binding the contract method 0x8ef973ea. // -// Solidity: function remove_asset(address asset_source, uint256 nonce, bytes signatures) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) RemoveAsset(opts *bind.TransactOpts, asset_source common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.contract.Transact(opts, "remove_asset", asset_source, nonce, signatures) +// Solidity: function removeAsset(address assetSource, uint256 nonce, bytes signatures) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) RemoveAsset(opts *bind.TransactOpts, assetSource common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.contract.Transact(opts, "removeAsset", assetSource, nonce, signatures) } -// RemoveAsset is a paid mutator transaction binding the contract method 0xc76de358. +// RemoveAsset is a paid mutator transaction binding the contract method 0x8ef973ea. // -// Solidity: function remove_asset(address asset_source, uint256 nonce, bytes signatures) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) RemoveAsset(asset_source common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.Contract.RemoveAsset(&_Erc20BridgeLogicRestricted.TransactOpts, asset_source, nonce, signatures) +// Solidity: function removeAsset(address assetSource, uint256 nonce, bytes signatures) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) RemoveAsset(assetSource common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.Contract.RemoveAsset(&_Erc20BridgeLogicRestricted.TransactOpts, assetSource, nonce, signatures) } -// RemoveAsset is a paid mutator transaction binding the contract method 0xc76de358. +// RemoveAsset is a paid mutator transaction binding the contract method 0x8ef973ea. // -// Solidity: function remove_asset(address asset_source, uint256 nonce, bytes signatures) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) RemoveAsset(asset_source common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.Contract.RemoveAsset(&_Erc20BridgeLogicRestricted.TransactOpts, asset_source, nonce, signatures) +// Solidity: function removeAsset(address assetSource, uint256 nonce, bytes signatures) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) RemoveAsset(assetSource common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.Contract.RemoveAsset(&_Erc20BridgeLogicRestricted.TransactOpts, assetSource, nonce, signatures) } -// RevokeExemptDepositor is a paid mutator transaction binding the contract method 0x6a1c6fa4. +// RevokeExemptDepositor is a paid mutator transaction binding the contract method 0x66a3edc4. // -// Solidity: function revoke_exempt_depositor() returns() +// Solidity: function revokeExemptDepositor() returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) RevokeExemptDepositor(opts *bind.TransactOpts) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.contract.Transact(opts, "revoke_exempt_depositor") + return _Erc20BridgeLogicRestricted.contract.Transact(opts, "revokeExemptDepositor") } -// RevokeExemptDepositor is a paid mutator transaction binding the contract method 0x6a1c6fa4. +// RevokeExemptDepositor is a paid mutator transaction binding the contract method 0x66a3edc4. // -// Solidity: function revoke_exempt_depositor() returns() +// Solidity: function revokeExemptDepositor() returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) RevokeExemptDepositor() (*types.Transaction, error) { return _Erc20BridgeLogicRestricted.Contract.RevokeExemptDepositor(&_Erc20BridgeLogicRestricted.TransactOpts) } -// RevokeExemptDepositor is a paid mutator transaction binding the contract method 0x6a1c6fa4. +// RevokeExemptDepositor is a paid mutator transaction binding the contract method 0x66a3edc4. // -// Solidity: function revoke_exempt_depositor() returns() +// Solidity: function revokeExemptDepositor() returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) RevokeExemptDepositor() (*types.Transaction, error) { return _Erc20BridgeLogicRestricted.Contract.RevokeExemptDepositor(&_Erc20BridgeLogicRestricted.TransactOpts) } -// SetAssetLimits is a paid mutator transaction binding the contract method 0x41fb776d. +// SetAssetLimits is a paid mutator transaction binding the contract method 0x10725d39. // -// Solidity: function set_asset_limits(address asset_source, uint256 lifetime_limit, uint256 threshold, uint256 nonce, bytes signatures) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) SetAssetLimits(opts *bind.TransactOpts, asset_source common.Address, lifetime_limit *big.Int, threshold *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.contract.Transact(opts, "set_asset_limits", asset_source, lifetime_limit, threshold, nonce, signatures) +// Solidity: function setAssetLimits(address assetSource, uint256 lifetimeLimit, uint256 threshold, uint256 nonce, bytes signatures) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) SetAssetLimits(opts *bind.TransactOpts, assetSource common.Address, lifetimeLimit *big.Int, threshold *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.contract.Transact(opts, "setAssetLimits", assetSource, lifetimeLimit, threshold, nonce, signatures) } -// SetAssetLimits is a paid mutator transaction binding the contract method 0x41fb776d. +// SetAssetLimits is a paid mutator transaction binding the contract method 0x10725d39. // -// Solidity: function set_asset_limits(address asset_source, uint256 lifetime_limit, uint256 threshold, uint256 nonce, bytes signatures) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) SetAssetLimits(asset_source common.Address, lifetime_limit *big.Int, threshold *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.Contract.SetAssetLimits(&_Erc20BridgeLogicRestricted.TransactOpts, asset_source, lifetime_limit, threshold, nonce, signatures) +// Solidity: function setAssetLimits(address assetSource, uint256 lifetimeLimit, uint256 threshold, uint256 nonce, bytes signatures) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) SetAssetLimits(assetSource common.Address, lifetimeLimit *big.Int, threshold *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.Contract.SetAssetLimits(&_Erc20BridgeLogicRestricted.TransactOpts, assetSource, lifetimeLimit, threshold, nonce, signatures) } -// SetAssetLimits is a paid mutator transaction binding the contract method 0x41fb776d. +// SetAssetLimits is a paid mutator transaction binding the contract method 0x10725d39. // -// Solidity: function set_asset_limits(address asset_source, uint256 lifetime_limit, uint256 threshold, uint256 nonce, bytes signatures) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) SetAssetLimits(asset_source common.Address, lifetime_limit *big.Int, threshold *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.Contract.SetAssetLimits(&_Erc20BridgeLogicRestricted.TransactOpts, asset_source, lifetime_limit, threshold, nonce, signatures) +// Solidity: function setAssetLimits(address assetSource, uint256 lifetimeLimit, uint256 threshold, uint256 nonce, bytes signatures) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) SetAssetLimits(assetSource common.Address, lifetimeLimit *big.Int, threshold *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.Contract.SetAssetLimits(&_Erc20BridgeLogicRestricted.TransactOpts, assetSource, lifetimeLimit, threshold, nonce, signatures) } -// SetWithdrawDelay is a paid mutator transaction binding the contract method 0x5a246728. +// SetWithdrawDelay is a paid mutator transaction binding the contract method 0x9fde4ad0. // -// Solidity: function set_withdraw_delay(uint256 delay, uint256 nonce, bytes signatures) returns() +// Solidity: function setWithdrawDelay(uint256 delay, uint256 nonce, bytes signatures) returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) SetWithdrawDelay(opts *bind.TransactOpts, delay *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.contract.Transact(opts, "set_withdraw_delay", delay, nonce, signatures) + return _Erc20BridgeLogicRestricted.contract.Transact(opts, "setWithdrawDelay", delay, nonce, signatures) } -// SetWithdrawDelay is a paid mutator transaction binding the contract method 0x5a246728. +// SetWithdrawDelay is a paid mutator transaction binding the contract method 0x9fde4ad0. // -// Solidity: function set_withdraw_delay(uint256 delay, uint256 nonce, bytes signatures) returns() +// Solidity: function setWithdrawDelay(uint256 delay, uint256 nonce, bytes signatures) returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) SetWithdrawDelay(delay *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { return _Erc20BridgeLogicRestricted.Contract.SetWithdrawDelay(&_Erc20BridgeLogicRestricted.TransactOpts, delay, nonce, signatures) } -// SetWithdrawDelay is a paid mutator transaction binding the contract method 0x5a246728. +// SetWithdrawDelay is a paid mutator transaction binding the contract method 0x9fde4ad0. // -// Solidity: function set_withdraw_delay(uint256 delay, uint256 nonce, bytes signatures) returns() +// Solidity: function setWithdrawDelay(uint256 delay, uint256 nonce, bytes signatures) returns() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) SetWithdrawDelay(delay *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { return _Erc20BridgeLogicRestricted.Contract.SetWithdrawDelay(&_Erc20BridgeLogicRestricted.TransactOpts, delay, nonce, signatures) } -// WithdrawAsset is a paid mutator transaction binding the contract method 0x3ad90635. +// WithdrawAsset is a paid mutator transaction binding the contract method 0x13b99c74. // -// Solidity: function withdraw_asset(address asset_source, uint256 amount, address target, uint256 creation, uint256 nonce, bytes signatures) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) WithdrawAsset(opts *bind.TransactOpts, asset_source common.Address, amount *big.Int, target common.Address, creation *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.contract.Transact(opts, "withdraw_asset", asset_source, amount, target, creation, nonce, signatures) +// Solidity: function withdrawAsset(address assetSource, uint256 amount, address target, uint256 creation, uint256 nonce, bytes signatures) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactor) WithdrawAsset(opts *bind.TransactOpts, assetSource common.Address, amount *big.Int, target common.Address, creation *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.contract.Transact(opts, "withdrawAsset", assetSource, amount, target, creation, nonce, signatures) } -// WithdrawAsset is a paid mutator transaction binding the contract method 0x3ad90635. +// WithdrawAsset is a paid mutator transaction binding the contract method 0x13b99c74. // -// Solidity: function withdraw_asset(address asset_source, uint256 amount, address target, uint256 creation, uint256 nonce, bytes signatures) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) WithdrawAsset(asset_source common.Address, amount *big.Int, target common.Address, creation *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.Contract.WithdrawAsset(&_Erc20BridgeLogicRestricted.TransactOpts, asset_source, amount, target, creation, nonce, signatures) +// Solidity: function withdrawAsset(address assetSource, uint256 amount, address target, uint256 creation, uint256 nonce, bytes signatures) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedSession) WithdrawAsset(assetSource common.Address, amount *big.Int, target common.Address, creation *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.Contract.WithdrawAsset(&_Erc20BridgeLogicRestricted.TransactOpts, assetSource, amount, target, creation, nonce, signatures) } -// WithdrawAsset is a paid mutator transaction binding the contract method 0x3ad90635. +// WithdrawAsset is a paid mutator transaction binding the contract method 0x13b99c74. // -// Solidity: function withdraw_asset(address asset_source, uint256 amount, address target, uint256 creation, uint256 nonce, bytes signatures) returns() -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) WithdrawAsset(asset_source common.Address, amount *big.Int, target common.Address, creation *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _Erc20BridgeLogicRestricted.Contract.WithdrawAsset(&_Erc20BridgeLogicRestricted.TransactOpts, asset_source, amount, target, creation, nonce, signatures) +// Solidity: function withdrawAsset(address assetSource, uint256 amount, address target, uint256 creation, uint256 nonce, bytes signatures) returns() +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedTransactorSession) WithdrawAsset(assetSource common.Address, amount *big.Int, target common.Address, creation *big.Int, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _Erc20BridgeLogicRestricted.Contract.WithdrawAsset(&_Erc20BridgeLogicRestricted.TransactOpts, assetSource, amount, target, creation, nonce, signatures) } // Erc20BridgeLogicRestrictedAssetDepositedIterator is returned from FilterAssetDeposited and is used to iterate over the raw logs and unpacked data for AssetDeposited events raised by the Erc20BridgeLogicRestricted contract. @@ -776,42 +683,42 @@ type Erc20BridgeLogicRestrictedAssetDeposited struct { Raw types.Log // Blockchain specific contextual infos } -// FilterAssetDeposited is a free log retrieval operation binding the contract event 0x3724ff5e82ddc640a08d68b0b782a5991aea0de51a8dd10a59cdbe5b3ec4e6bf. +// FilterAssetDeposited is a free log retrieval operation binding the contract event 0x682eab6abd797cfe9f53779827c6526e4e1e5c2c0ad2c2a30d8af5a269f5608e. // -// Solidity: event Asset_Deposited(address indexed user_address, address indexed asset_source, uint256 amount, bytes32 vega_public_key) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterAssetDeposited(opts *bind.FilterOpts, user_address []common.Address, asset_source []common.Address) (*Erc20BridgeLogicRestrictedAssetDepositedIterator, error) { +// Solidity: event AssetDeposited(address indexed userAddress, address indexed assetSource, uint256 amount, bytes32 vegaPublicKey) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterAssetDeposited(opts *bind.FilterOpts, userAddress []common.Address, assetSource []common.Address) (*Erc20BridgeLogicRestrictedAssetDepositedIterator, error) { - var user_addressRule []interface{} - for _, user_addressItem := range user_address { - user_addressRule = append(user_addressRule, user_addressItem) + var userAddressRule []interface{} + for _, userAddressItem := range userAddress { + userAddressRule = append(userAddressRule, userAddressItem) } - var asset_sourceRule []interface{} - for _, asset_sourceItem := range asset_source { - asset_sourceRule = append(asset_sourceRule, asset_sourceItem) + var assetSourceRule []interface{} + for _, assetSourceItem := range assetSource { + assetSourceRule = append(assetSourceRule, assetSourceItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "Asset_Deposited", user_addressRule, asset_sourceRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "AssetDeposited", userAddressRule, assetSourceRule) if err != nil { return nil, err } - return &Erc20BridgeLogicRestrictedAssetDepositedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "Asset_Deposited", logs: logs, sub: sub}, nil + return &Erc20BridgeLogicRestrictedAssetDepositedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "AssetDeposited", logs: logs, sub: sub}, nil } -// WatchAssetDeposited is a free log subscription operation binding the contract event 0x3724ff5e82ddc640a08d68b0b782a5991aea0de51a8dd10a59cdbe5b3ec4e6bf. +// WatchAssetDeposited is a free log subscription operation binding the contract event 0x682eab6abd797cfe9f53779827c6526e4e1e5c2c0ad2c2a30d8af5a269f5608e. // -// Solidity: event Asset_Deposited(address indexed user_address, address indexed asset_source, uint256 amount, bytes32 vega_public_key) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAssetDeposited(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedAssetDeposited, user_address []common.Address, asset_source []common.Address) (event.Subscription, error) { +// Solidity: event AssetDeposited(address indexed userAddress, address indexed assetSource, uint256 amount, bytes32 vegaPublicKey) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAssetDeposited(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedAssetDeposited, userAddress []common.Address, assetSource []common.Address) (event.Subscription, error) { - var user_addressRule []interface{} - for _, user_addressItem := range user_address { - user_addressRule = append(user_addressRule, user_addressItem) + var userAddressRule []interface{} + for _, userAddressItem := range userAddress { + userAddressRule = append(userAddressRule, userAddressItem) } - var asset_sourceRule []interface{} - for _, asset_sourceItem := range asset_source { - asset_sourceRule = append(asset_sourceRule, asset_sourceItem) + var assetSourceRule []interface{} + for _, assetSourceItem := range assetSource { + assetSourceRule = append(assetSourceRule, assetSourceItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "Asset_Deposited", user_addressRule, asset_sourceRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "AssetDeposited", userAddressRule, assetSourceRule) if err != nil { return nil, err } @@ -822,7 +729,7 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAsse case log := <-logs: // New log arrived, parse the event and forward to the user event := new(Erc20BridgeLogicRestrictedAssetDeposited) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Asset_Deposited", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "AssetDeposited", log); err != nil { return err } event.Raw = log @@ -843,12 +750,12 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAsse }), nil } -// ParseAssetDeposited is a log parse operation binding the contract event 0x3724ff5e82ddc640a08d68b0b782a5991aea0de51a8dd10a59cdbe5b3ec4e6bf. +// ParseAssetDeposited is a log parse operation binding the contract event 0x682eab6abd797cfe9f53779827c6526e4e1e5c2c0ad2c2a30d8af5a269f5608e. // -// Solidity: event Asset_Deposited(address indexed user_address, address indexed asset_source, uint256 amount, bytes32 vega_public_key) +// Solidity: event AssetDeposited(address indexed userAddress, address indexed assetSource, uint256 amount, bytes32 vegaPublicKey) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) ParseAssetDeposited(log types.Log) (*Erc20BridgeLogicRestrictedAssetDeposited, error) { event := new(Erc20BridgeLogicRestrictedAssetDeposited) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Asset_Deposited", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "AssetDeposited", log); err != nil { return nil, err } event.Raw = log @@ -930,34 +837,34 @@ type Erc20BridgeLogicRestrictedAssetLimitsUpdated struct { Raw types.Log // Blockchain specific contextual infos } -// FilterAssetLimitsUpdated is a free log retrieval operation binding the contract event 0xfc7eab762b8751ad85c101fd1025c763b4e8d48f2093f506629b606618e884fe. +// FilterAssetLimitsUpdated is a free log retrieval operation binding the contract event 0x2b9be4c285a9f5ff31bc13fabbab637c16e5f945e41a8a0e00e699264e9a17b6. // -// Solidity: event Asset_Limits_Updated(address indexed asset_source, uint256 lifetime_limit, uint256 withdraw_threshold) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterAssetLimitsUpdated(opts *bind.FilterOpts, asset_source []common.Address) (*Erc20BridgeLogicRestrictedAssetLimitsUpdatedIterator, error) { +// Solidity: event AssetLimitsUpdated(address indexed assetSource, uint256 lifetimeLimit, uint256 withdrawThreshold) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterAssetLimitsUpdated(opts *bind.FilterOpts, assetSource []common.Address) (*Erc20BridgeLogicRestrictedAssetLimitsUpdatedIterator, error) { - var asset_sourceRule []interface{} - for _, asset_sourceItem := range asset_source { - asset_sourceRule = append(asset_sourceRule, asset_sourceItem) + var assetSourceRule []interface{} + for _, assetSourceItem := range assetSource { + assetSourceRule = append(assetSourceRule, assetSourceItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "Asset_Limits_Updated", asset_sourceRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "AssetLimitsUpdated", assetSourceRule) if err != nil { return nil, err } - return &Erc20BridgeLogicRestrictedAssetLimitsUpdatedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "Asset_Limits_Updated", logs: logs, sub: sub}, nil + return &Erc20BridgeLogicRestrictedAssetLimitsUpdatedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "AssetLimitsUpdated", logs: logs, sub: sub}, nil } -// WatchAssetLimitsUpdated is a free log subscription operation binding the contract event 0xfc7eab762b8751ad85c101fd1025c763b4e8d48f2093f506629b606618e884fe. +// WatchAssetLimitsUpdated is a free log subscription operation binding the contract event 0x2b9be4c285a9f5ff31bc13fabbab637c16e5f945e41a8a0e00e699264e9a17b6. // -// Solidity: event Asset_Limits_Updated(address indexed asset_source, uint256 lifetime_limit, uint256 withdraw_threshold) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAssetLimitsUpdated(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedAssetLimitsUpdated, asset_source []common.Address) (event.Subscription, error) { +// Solidity: event AssetLimitsUpdated(address indexed assetSource, uint256 lifetimeLimit, uint256 withdrawThreshold) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAssetLimitsUpdated(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedAssetLimitsUpdated, assetSource []common.Address) (event.Subscription, error) { - var asset_sourceRule []interface{} - for _, asset_sourceItem := range asset_source { - asset_sourceRule = append(asset_sourceRule, asset_sourceItem) + var assetSourceRule []interface{} + for _, assetSourceItem := range assetSource { + assetSourceRule = append(assetSourceRule, assetSourceItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "Asset_Limits_Updated", asset_sourceRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "AssetLimitsUpdated", assetSourceRule) if err != nil { return nil, err } @@ -968,7 +875,7 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAsse case log := <-logs: // New log arrived, parse the event and forward to the user event := new(Erc20BridgeLogicRestrictedAssetLimitsUpdated) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Asset_Limits_Updated", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "AssetLimitsUpdated", log); err != nil { return err } event.Raw = log @@ -989,12 +896,12 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAsse }), nil } -// ParseAssetLimitsUpdated is a log parse operation binding the contract event 0xfc7eab762b8751ad85c101fd1025c763b4e8d48f2093f506629b606618e884fe. +// ParseAssetLimitsUpdated is a log parse operation binding the contract event 0x2b9be4c285a9f5ff31bc13fabbab637c16e5f945e41a8a0e00e699264e9a17b6. // -// Solidity: event Asset_Limits_Updated(address indexed asset_source, uint256 lifetime_limit, uint256 withdraw_threshold) +// Solidity: event AssetLimitsUpdated(address indexed assetSource, uint256 lifetimeLimit, uint256 withdrawThreshold) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) ParseAssetLimitsUpdated(log types.Log) (*Erc20BridgeLogicRestrictedAssetLimitsUpdated, error) { event := new(Erc20BridgeLogicRestrictedAssetLimitsUpdated) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Asset_Limits_Updated", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "AssetLimitsUpdated", log); err != nil { return nil, err } event.Raw = log @@ -1076,42 +983,42 @@ type Erc20BridgeLogicRestrictedAssetListed struct { Raw types.Log // Blockchain specific contextual infos } -// FilterAssetListed is a free log retrieval operation binding the contract event 0x4180d77d05ff0d31650c548c23f2de07a3da3ad42e3dd6edd817b438a150452e. +// FilterAssetListed is a free log retrieval operation binding the contract event 0xec587e0b31172eee3a953d82b0c7f78c96be78084a92eb0463144c0446250d49. // -// Solidity: event Asset_Listed(address indexed asset_source, bytes32 indexed vega_asset_id, uint256 nonce) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterAssetListed(opts *bind.FilterOpts, asset_source []common.Address, vega_asset_id [][32]byte) (*Erc20BridgeLogicRestrictedAssetListedIterator, error) { +// Solidity: event AssetListed(address indexed assetSource, bytes32 indexed vegaAssetId, uint256 nonce) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterAssetListed(opts *bind.FilterOpts, assetSource []common.Address, vegaAssetId [][32]byte) (*Erc20BridgeLogicRestrictedAssetListedIterator, error) { - var asset_sourceRule []interface{} - for _, asset_sourceItem := range asset_source { - asset_sourceRule = append(asset_sourceRule, asset_sourceItem) + var assetSourceRule []interface{} + for _, assetSourceItem := range assetSource { + assetSourceRule = append(assetSourceRule, assetSourceItem) } - var vega_asset_idRule []interface{} - for _, vega_asset_idItem := range vega_asset_id { - vega_asset_idRule = append(vega_asset_idRule, vega_asset_idItem) + var vegaAssetIdRule []interface{} + for _, vegaAssetIdItem := range vegaAssetId { + vegaAssetIdRule = append(vegaAssetIdRule, vegaAssetIdItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "Asset_Listed", asset_sourceRule, vega_asset_idRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "AssetListed", assetSourceRule, vegaAssetIdRule) if err != nil { return nil, err } - return &Erc20BridgeLogicRestrictedAssetListedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "Asset_Listed", logs: logs, sub: sub}, nil + return &Erc20BridgeLogicRestrictedAssetListedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "AssetListed", logs: logs, sub: sub}, nil } -// WatchAssetListed is a free log subscription operation binding the contract event 0x4180d77d05ff0d31650c548c23f2de07a3da3ad42e3dd6edd817b438a150452e. +// WatchAssetListed is a free log subscription operation binding the contract event 0xec587e0b31172eee3a953d82b0c7f78c96be78084a92eb0463144c0446250d49. // -// Solidity: event Asset_Listed(address indexed asset_source, bytes32 indexed vega_asset_id, uint256 nonce) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAssetListed(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedAssetListed, asset_source []common.Address, vega_asset_id [][32]byte) (event.Subscription, error) { +// Solidity: event AssetListed(address indexed assetSource, bytes32 indexed vegaAssetId, uint256 nonce) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAssetListed(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedAssetListed, assetSource []common.Address, vegaAssetId [][32]byte) (event.Subscription, error) { - var asset_sourceRule []interface{} - for _, asset_sourceItem := range asset_source { - asset_sourceRule = append(asset_sourceRule, asset_sourceItem) + var assetSourceRule []interface{} + for _, assetSourceItem := range assetSource { + assetSourceRule = append(assetSourceRule, assetSourceItem) } - var vega_asset_idRule []interface{} - for _, vega_asset_idItem := range vega_asset_id { - vega_asset_idRule = append(vega_asset_idRule, vega_asset_idItem) + var vegaAssetIdRule []interface{} + for _, vegaAssetIdItem := range vegaAssetId { + vegaAssetIdRule = append(vegaAssetIdRule, vegaAssetIdItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "Asset_Listed", asset_sourceRule, vega_asset_idRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "AssetListed", assetSourceRule, vegaAssetIdRule) if err != nil { return nil, err } @@ -1122,7 +1029,7 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAsse case log := <-logs: // New log arrived, parse the event and forward to the user event := new(Erc20BridgeLogicRestrictedAssetListed) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Asset_Listed", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "AssetListed", log); err != nil { return err } event.Raw = log @@ -1143,12 +1050,12 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAsse }), nil } -// ParseAssetListed is a log parse operation binding the contract event 0x4180d77d05ff0d31650c548c23f2de07a3da3ad42e3dd6edd817b438a150452e. +// ParseAssetListed is a log parse operation binding the contract event 0xec587e0b31172eee3a953d82b0c7f78c96be78084a92eb0463144c0446250d49. // -// Solidity: event Asset_Listed(address indexed asset_source, bytes32 indexed vega_asset_id, uint256 nonce) +// Solidity: event AssetListed(address indexed assetSource, bytes32 indexed vegaAssetId, uint256 nonce) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) ParseAssetListed(log types.Log) (*Erc20BridgeLogicRestrictedAssetListed, error) { event := new(Erc20BridgeLogicRestrictedAssetListed) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Asset_Listed", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "AssetListed", log); err != nil { return nil, err } event.Raw = log @@ -1229,34 +1136,34 @@ type Erc20BridgeLogicRestrictedAssetRemoved struct { Raw types.Log // Blockchain specific contextual infos } -// FilterAssetRemoved is a free log retrieval operation binding the contract event 0x58ad5e799e2df93ab408be0e5c1870d44c80b5bca99dfaf7ddf0dab5e6b155c9. +// FilterAssetRemoved is a free log retrieval operation binding the contract event 0x3406221f53114f44c9a1bb93d08ee55735f39bf235a54741684a52501207bb54. // -// Solidity: event Asset_Removed(address indexed asset_source, uint256 nonce) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterAssetRemoved(opts *bind.FilterOpts, asset_source []common.Address) (*Erc20BridgeLogicRestrictedAssetRemovedIterator, error) { +// Solidity: event AssetRemoved(address indexed assetSource, uint256 nonce) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterAssetRemoved(opts *bind.FilterOpts, assetSource []common.Address) (*Erc20BridgeLogicRestrictedAssetRemovedIterator, error) { - var asset_sourceRule []interface{} - for _, asset_sourceItem := range asset_source { - asset_sourceRule = append(asset_sourceRule, asset_sourceItem) + var assetSourceRule []interface{} + for _, assetSourceItem := range assetSource { + assetSourceRule = append(assetSourceRule, assetSourceItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "Asset_Removed", asset_sourceRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "AssetRemoved", assetSourceRule) if err != nil { return nil, err } - return &Erc20BridgeLogicRestrictedAssetRemovedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "Asset_Removed", logs: logs, sub: sub}, nil + return &Erc20BridgeLogicRestrictedAssetRemovedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "AssetRemoved", logs: logs, sub: sub}, nil } -// WatchAssetRemoved is a free log subscription operation binding the contract event 0x58ad5e799e2df93ab408be0e5c1870d44c80b5bca99dfaf7ddf0dab5e6b155c9. +// WatchAssetRemoved is a free log subscription operation binding the contract event 0x3406221f53114f44c9a1bb93d08ee55735f39bf235a54741684a52501207bb54. // -// Solidity: event Asset_Removed(address indexed asset_source, uint256 nonce) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAssetRemoved(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedAssetRemoved, asset_source []common.Address) (event.Subscription, error) { +// Solidity: event AssetRemoved(address indexed assetSource, uint256 nonce) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAssetRemoved(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedAssetRemoved, assetSource []common.Address) (event.Subscription, error) { - var asset_sourceRule []interface{} - for _, asset_sourceItem := range asset_source { - asset_sourceRule = append(asset_sourceRule, asset_sourceItem) + var assetSourceRule []interface{} + for _, assetSourceItem := range assetSource { + assetSourceRule = append(assetSourceRule, assetSourceItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "Asset_Removed", asset_sourceRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "AssetRemoved", assetSourceRule) if err != nil { return nil, err } @@ -1267,7 +1174,7 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAsse case log := <-logs: // New log arrived, parse the event and forward to the user event := new(Erc20BridgeLogicRestrictedAssetRemoved) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Asset_Removed", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "AssetRemoved", log); err != nil { return err } event.Raw = log @@ -1288,12 +1195,12 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAsse }), nil } -// ParseAssetRemoved is a log parse operation binding the contract event 0x58ad5e799e2df93ab408be0e5c1870d44c80b5bca99dfaf7ddf0dab5e6b155c9. +// ParseAssetRemoved is a log parse operation binding the contract event 0x3406221f53114f44c9a1bb93d08ee55735f39bf235a54741684a52501207bb54. // -// Solidity: event Asset_Removed(address indexed asset_source, uint256 nonce) +// Solidity: event AssetRemoved(address indexed assetSource, uint256 nonce) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) ParseAssetRemoved(log types.Log) (*Erc20BridgeLogicRestrictedAssetRemoved, error) { event := new(Erc20BridgeLogicRestrictedAssetRemoved) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Asset_Removed", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "AssetRemoved", log); err != nil { return nil, err } event.Raw = log @@ -1376,42 +1283,42 @@ type Erc20BridgeLogicRestrictedAssetWithdrawn struct { Raw types.Log // Blockchain specific contextual infos } -// FilterAssetWithdrawn is a free log retrieval operation binding the contract event 0xa79be4f3361e32d396d64c478ecef73732cb40b2a75702c3b3b3226a2c83b5df. +// FilterAssetWithdrawn is a free log retrieval operation binding the contract event 0xdd3541b6a4a74daf4ad7d33c0d4b441e1bad2e93e12da692aa37c5febb19f7b8. // -// Solidity: event Asset_Withdrawn(address indexed user_address, address indexed asset_source, uint256 amount, uint256 nonce) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterAssetWithdrawn(opts *bind.FilterOpts, user_address []common.Address, asset_source []common.Address) (*Erc20BridgeLogicRestrictedAssetWithdrawnIterator, error) { +// Solidity: event AssetWithdrawn(address indexed userAddress, address indexed assetSource, uint256 amount, uint256 nonce) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterAssetWithdrawn(opts *bind.FilterOpts, userAddress []common.Address, assetSource []common.Address) (*Erc20BridgeLogicRestrictedAssetWithdrawnIterator, error) { - var user_addressRule []interface{} - for _, user_addressItem := range user_address { - user_addressRule = append(user_addressRule, user_addressItem) + var userAddressRule []interface{} + for _, userAddressItem := range userAddress { + userAddressRule = append(userAddressRule, userAddressItem) } - var asset_sourceRule []interface{} - for _, asset_sourceItem := range asset_source { - asset_sourceRule = append(asset_sourceRule, asset_sourceItem) + var assetSourceRule []interface{} + for _, assetSourceItem := range assetSource { + assetSourceRule = append(assetSourceRule, assetSourceItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "Asset_Withdrawn", user_addressRule, asset_sourceRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "AssetWithdrawn", userAddressRule, assetSourceRule) if err != nil { return nil, err } - return &Erc20BridgeLogicRestrictedAssetWithdrawnIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "Asset_Withdrawn", logs: logs, sub: sub}, nil + return &Erc20BridgeLogicRestrictedAssetWithdrawnIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "AssetWithdrawn", logs: logs, sub: sub}, nil } -// WatchAssetWithdrawn is a free log subscription operation binding the contract event 0xa79be4f3361e32d396d64c478ecef73732cb40b2a75702c3b3b3226a2c83b5df. +// WatchAssetWithdrawn is a free log subscription operation binding the contract event 0xdd3541b6a4a74daf4ad7d33c0d4b441e1bad2e93e12da692aa37c5febb19f7b8. // -// Solidity: event Asset_Withdrawn(address indexed user_address, address indexed asset_source, uint256 amount, uint256 nonce) -func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAssetWithdrawn(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedAssetWithdrawn, user_address []common.Address, asset_source []common.Address) (event.Subscription, error) { +// Solidity: event AssetWithdrawn(address indexed userAddress, address indexed assetSource, uint256 amount, uint256 nonce) +func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAssetWithdrawn(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedAssetWithdrawn, userAddress []common.Address, assetSource []common.Address) (event.Subscription, error) { - var user_addressRule []interface{} - for _, user_addressItem := range user_address { - user_addressRule = append(user_addressRule, user_addressItem) + var userAddressRule []interface{} + for _, userAddressItem := range userAddress { + userAddressRule = append(userAddressRule, userAddressItem) } - var asset_sourceRule []interface{} - for _, asset_sourceItem := range asset_source { - asset_sourceRule = append(asset_sourceRule, asset_sourceItem) + var assetSourceRule []interface{} + for _, assetSourceItem := range assetSource { + assetSourceRule = append(assetSourceRule, assetSourceItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "Asset_Withdrawn", user_addressRule, asset_sourceRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "AssetWithdrawn", userAddressRule, assetSourceRule) if err != nil { return nil, err } @@ -1422,7 +1329,7 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAsse case log := <-logs: // New log arrived, parse the event and forward to the user event := new(Erc20BridgeLogicRestrictedAssetWithdrawn) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Asset_Withdrawn", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "AssetWithdrawn", log); err != nil { return err } event.Raw = log @@ -1443,12 +1350,12 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchAsse }), nil } -// ParseAssetWithdrawn is a log parse operation binding the contract event 0xa79be4f3361e32d396d64c478ecef73732cb40b2a75702c3b3b3226a2c83b5df. +// ParseAssetWithdrawn is a log parse operation binding the contract event 0xdd3541b6a4a74daf4ad7d33c0d4b441e1bad2e93e12da692aa37c5febb19f7b8. // -// Solidity: event Asset_Withdrawn(address indexed user_address, address indexed asset_source, uint256 amount, uint256 nonce) +// Solidity: event AssetWithdrawn(address indexed userAddress, address indexed assetSource, uint256 amount, uint256 nonce) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) ParseAssetWithdrawn(log types.Log) (*Erc20BridgeLogicRestrictedAssetWithdrawn, error) { event := new(Erc20BridgeLogicRestrictedAssetWithdrawn) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Asset_Withdrawn", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "AssetWithdrawn", log); err != nil { return nil, err } event.Raw = log @@ -1527,24 +1434,24 @@ type Erc20BridgeLogicRestrictedBridgeResumed struct { Raw types.Log // Blockchain specific contextual infos } -// FilterBridgeResumed is a free log retrieval operation binding the contract event 0x79c02b0e60e0f00fe0370791204f2f175fe3f06f4816f3506ad4fa1b8e8cde0f. +// FilterBridgeResumed is a free log retrieval operation binding the contract event 0x287ac30ff6e68ce13e26be0638f2f8fb754a569fd9c2dc77bf8241411a647876. // -// Solidity: event Bridge_Resumed() +// Solidity: event BridgeResumed() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterBridgeResumed(opts *bind.FilterOpts) (*Erc20BridgeLogicRestrictedBridgeResumedIterator, error) { - logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "Bridge_Resumed") + logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "BridgeResumed") if err != nil { return nil, err } - return &Erc20BridgeLogicRestrictedBridgeResumedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "Bridge_Resumed", logs: logs, sub: sub}, nil + return &Erc20BridgeLogicRestrictedBridgeResumedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "BridgeResumed", logs: logs, sub: sub}, nil } -// WatchBridgeResumed is a free log subscription operation binding the contract event 0x79c02b0e60e0f00fe0370791204f2f175fe3f06f4816f3506ad4fa1b8e8cde0f. +// WatchBridgeResumed is a free log subscription operation binding the contract event 0x287ac30ff6e68ce13e26be0638f2f8fb754a569fd9c2dc77bf8241411a647876. // -// Solidity: event Bridge_Resumed() +// Solidity: event BridgeResumed() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchBridgeResumed(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedBridgeResumed) (event.Subscription, error) { - logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "Bridge_Resumed") + logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "BridgeResumed") if err != nil { return nil, err } @@ -1555,7 +1462,7 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchBrid case log := <-logs: // New log arrived, parse the event and forward to the user event := new(Erc20BridgeLogicRestrictedBridgeResumed) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Bridge_Resumed", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "BridgeResumed", log); err != nil { return err } event.Raw = log @@ -1576,12 +1483,12 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchBrid }), nil } -// ParseBridgeResumed is a log parse operation binding the contract event 0x79c02b0e60e0f00fe0370791204f2f175fe3f06f4816f3506ad4fa1b8e8cde0f. +// ParseBridgeResumed is a log parse operation binding the contract event 0x287ac30ff6e68ce13e26be0638f2f8fb754a569fd9c2dc77bf8241411a647876. // -// Solidity: event Bridge_Resumed() +// Solidity: event BridgeResumed() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) ParseBridgeResumed(log types.Log) (*Erc20BridgeLogicRestrictedBridgeResumed, error) { event := new(Erc20BridgeLogicRestrictedBridgeResumed) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Bridge_Resumed", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "BridgeResumed", log); err != nil { return nil, err } event.Raw = log @@ -1660,24 +1567,24 @@ type Erc20BridgeLogicRestrictedBridgeStopped struct { Raw types.Log // Blockchain specific contextual infos } -// FilterBridgeStopped is a free log retrieval operation binding the contract event 0x129d99581c8e70519df1f0733d3212f33d0ed3ea6144adacc336c647f1d36382. +// FilterBridgeStopped is a free log retrieval operation binding the contract event 0xc491bbb4be1472096d01e3f0b6f3c2ba9720a559c3422c36408dff58e42d3873. // -// Solidity: event Bridge_Stopped() +// Solidity: event BridgeStopped() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterBridgeStopped(opts *bind.FilterOpts) (*Erc20BridgeLogicRestrictedBridgeStoppedIterator, error) { - logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "Bridge_Stopped") + logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "BridgeStopped") if err != nil { return nil, err } - return &Erc20BridgeLogicRestrictedBridgeStoppedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "Bridge_Stopped", logs: logs, sub: sub}, nil + return &Erc20BridgeLogicRestrictedBridgeStoppedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "BridgeStopped", logs: logs, sub: sub}, nil } -// WatchBridgeStopped is a free log subscription operation binding the contract event 0x129d99581c8e70519df1f0733d3212f33d0ed3ea6144adacc336c647f1d36382. +// WatchBridgeStopped is a free log subscription operation binding the contract event 0xc491bbb4be1472096d01e3f0b6f3c2ba9720a559c3422c36408dff58e42d3873. // -// Solidity: event Bridge_Stopped() +// Solidity: event BridgeStopped() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchBridgeStopped(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedBridgeStopped) (event.Subscription, error) { - logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "Bridge_Stopped") + logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "BridgeStopped") if err != nil { return nil, err } @@ -1688,7 +1595,7 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchBrid case log := <-logs: // New log arrived, parse the event and forward to the user event := new(Erc20BridgeLogicRestrictedBridgeStopped) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Bridge_Stopped", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "BridgeStopped", log); err != nil { return err } event.Raw = log @@ -1709,12 +1616,12 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchBrid }), nil } -// ParseBridgeStopped is a log parse operation binding the contract event 0x129d99581c8e70519df1f0733d3212f33d0ed3ea6144adacc336c647f1d36382. +// ParseBridgeStopped is a log parse operation binding the contract event 0xc491bbb4be1472096d01e3f0b6f3c2ba9720a559c3422c36408dff58e42d3873. // -// Solidity: event Bridge_Stopped() +// Solidity: event BridgeStopped() func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) ParseBridgeStopped(log types.Log) (*Erc20BridgeLogicRestrictedBridgeStopped, error) { event := new(Erc20BridgeLogicRestrictedBridgeStopped) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Bridge_Stopped", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "BridgeStopped", log); err != nil { return nil, err } event.Raw = log @@ -1794,24 +1701,24 @@ type Erc20BridgeLogicRestrictedBridgeWithdrawDelaySet struct { Raw types.Log // Blockchain specific contextual infos } -// FilterBridgeWithdrawDelaySet is a free log retrieval operation binding the contract event 0x1c7e8f73a01b8af4e18dd34455a42a45ad742bdb79cfda77bbdf50db2391fc88. +// FilterBridgeWithdrawDelaySet is a free log retrieval operation binding the contract event 0xd7ce7fbb38daf3a282293f127641ae35e6f17cee1b51904e7b9c633f97df3b85. // -// Solidity: event Bridge_Withdraw_Delay_Set(uint256 withdraw_delay) +// Solidity: event BridgeWithdrawDelaySet(uint256 withdraw_delay) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterBridgeWithdrawDelaySet(opts *bind.FilterOpts) (*Erc20BridgeLogicRestrictedBridgeWithdrawDelaySetIterator, error) { - logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "Bridge_Withdraw_Delay_Set") + logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "BridgeWithdrawDelaySet") if err != nil { return nil, err } - return &Erc20BridgeLogicRestrictedBridgeWithdrawDelaySetIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "Bridge_Withdraw_Delay_Set", logs: logs, sub: sub}, nil + return &Erc20BridgeLogicRestrictedBridgeWithdrawDelaySetIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "BridgeWithdrawDelaySet", logs: logs, sub: sub}, nil } -// WatchBridgeWithdrawDelaySet is a free log subscription operation binding the contract event 0x1c7e8f73a01b8af4e18dd34455a42a45ad742bdb79cfda77bbdf50db2391fc88. +// WatchBridgeWithdrawDelaySet is a free log subscription operation binding the contract event 0xd7ce7fbb38daf3a282293f127641ae35e6f17cee1b51904e7b9c633f97df3b85. // -// Solidity: event Bridge_Withdraw_Delay_Set(uint256 withdraw_delay) +// Solidity: event BridgeWithdrawDelaySet(uint256 withdraw_delay) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchBridgeWithdrawDelaySet(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedBridgeWithdrawDelaySet) (event.Subscription, error) { - logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "Bridge_Withdraw_Delay_Set") + logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "BridgeWithdrawDelaySet") if err != nil { return nil, err } @@ -1822,7 +1729,7 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchBrid case log := <-logs: // New log arrived, parse the event and forward to the user event := new(Erc20BridgeLogicRestrictedBridgeWithdrawDelaySet) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Bridge_Withdraw_Delay_Set", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "BridgeWithdrawDelaySet", log); err != nil { return err } event.Raw = log @@ -1843,12 +1750,12 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchBrid }), nil } -// ParseBridgeWithdrawDelaySet is a log parse operation binding the contract event 0x1c7e8f73a01b8af4e18dd34455a42a45ad742bdb79cfda77bbdf50db2391fc88. +// ParseBridgeWithdrawDelaySet is a log parse operation binding the contract event 0xd7ce7fbb38daf3a282293f127641ae35e6f17cee1b51904e7b9c633f97df3b85. // -// Solidity: event Bridge_Withdraw_Delay_Set(uint256 withdraw_delay) +// Solidity: event BridgeWithdrawDelaySet(uint256 withdraw_delay) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) ParseBridgeWithdrawDelaySet(log types.Log) (*Erc20BridgeLogicRestrictedBridgeWithdrawDelaySet, error) { event := new(Erc20BridgeLogicRestrictedBridgeWithdrawDelaySet) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Bridge_Withdraw_Delay_Set", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "BridgeWithdrawDelaySet", log); err != nil { return nil, err } event.Raw = log @@ -1928,9 +1835,9 @@ type Erc20BridgeLogicRestrictedDepositorExempted struct { Raw types.Log // Blockchain specific contextual infos } -// FilterDepositorExempted is a free log retrieval operation binding the contract event 0xf56e0868b913034a60dbca9c89ee79f8b0fa18dadbc5f6665f2f9a2cf3f51cdb. +// FilterDepositorExempted is a free log retrieval operation binding the contract event 0x9864ffa3d25fb42ecb8e42e8c6655954e8c7427d44a7a9f8edfe0cea4a0108f8. // -// Solidity: event Depositor_Exempted(address indexed depositor) +// Solidity: event DepositorExempted(address indexed depositor) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterDepositorExempted(opts *bind.FilterOpts, depositor []common.Address) (*Erc20BridgeLogicRestrictedDepositorExemptedIterator, error) { var depositorRule []interface{} @@ -1938,16 +1845,16 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterDep depositorRule = append(depositorRule, depositorItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "Depositor_Exempted", depositorRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "DepositorExempted", depositorRule) if err != nil { return nil, err } - return &Erc20BridgeLogicRestrictedDepositorExemptedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "Depositor_Exempted", logs: logs, sub: sub}, nil + return &Erc20BridgeLogicRestrictedDepositorExemptedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "DepositorExempted", logs: logs, sub: sub}, nil } -// WatchDepositorExempted is a free log subscription operation binding the contract event 0xf56e0868b913034a60dbca9c89ee79f8b0fa18dadbc5f6665f2f9a2cf3f51cdb. +// WatchDepositorExempted is a free log subscription operation binding the contract event 0x9864ffa3d25fb42ecb8e42e8c6655954e8c7427d44a7a9f8edfe0cea4a0108f8. // -// Solidity: event Depositor_Exempted(address indexed depositor) +// Solidity: event DepositorExempted(address indexed depositor) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchDepositorExempted(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedDepositorExempted, depositor []common.Address) (event.Subscription, error) { var depositorRule []interface{} @@ -1955,7 +1862,7 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchDepo depositorRule = append(depositorRule, depositorItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "Depositor_Exempted", depositorRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "DepositorExempted", depositorRule) if err != nil { return nil, err } @@ -1966,7 +1873,7 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchDepo case log := <-logs: // New log arrived, parse the event and forward to the user event := new(Erc20BridgeLogicRestrictedDepositorExempted) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Depositor_Exempted", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "DepositorExempted", log); err != nil { return err } event.Raw = log @@ -1987,12 +1894,12 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchDepo }), nil } -// ParseDepositorExempted is a log parse operation binding the contract event 0xf56e0868b913034a60dbca9c89ee79f8b0fa18dadbc5f6665f2f9a2cf3f51cdb. +// ParseDepositorExempted is a log parse operation binding the contract event 0x9864ffa3d25fb42ecb8e42e8c6655954e8c7427d44a7a9f8edfe0cea4a0108f8. // -// Solidity: event Depositor_Exempted(address indexed depositor) +// Solidity: event DepositorExempted(address indexed depositor) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) ParseDepositorExempted(log types.Log) (*Erc20BridgeLogicRestrictedDepositorExempted, error) { event := new(Erc20BridgeLogicRestrictedDepositorExempted) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Depositor_Exempted", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "DepositorExempted", log); err != nil { return nil, err } event.Raw = log @@ -2072,9 +1979,9 @@ type Erc20BridgeLogicRestrictedDepositorExemptionRevoked struct { Raw types.Log // Blockchain specific contextual infos } -// FilterDepositorExemptionRevoked is a free log retrieval operation binding the contract event 0xe74b113dca87276d976f476a9b4b9da3c780a3262eaabad051ee4e98912936a4. +// FilterDepositorExemptionRevoked is a free log retrieval operation binding the contract event 0xd661c54cb18e99cf2cc5b8c6b57f7a094e8a1b967b5de45a609f6d0220e998d4. // -// Solidity: event Depositor_Exemption_Revoked(address indexed depositor) +// Solidity: event DepositorExemptionRevoked(address indexed depositor) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterDepositorExemptionRevoked(opts *bind.FilterOpts, depositor []common.Address) (*Erc20BridgeLogicRestrictedDepositorExemptionRevokedIterator, error) { var depositorRule []interface{} @@ -2082,16 +1989,16 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) FilterDep depositorRule = append(depositorRule, depositorItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "Depositor_Exemption_Revoked", depositorRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.FilterLogs(opts, "DepositorExemptionRevoked", depositorRule) if err != nil { return nil, err } - return &Erc20BridgeLogicRestrictedDepositorExemptionRevokedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "Depositor_Exemption_Revoked", logs: logs, sub: sub}, nil + return &Erc20BridgeLogicRestrictedDepositorExemptionRevokedIterator{contract: _Erc20BridgeLogicRestricted.contract, event: "DepositorExemptionRevoked", logs: logs, sub: sub}, nil } -// WatchDepositorExemptionRevoked is a free log subscription operation binding the contract event 0xe74b113dca87276d976f476a9b4b9da3c780a3262eaabad051ee4e98912936a4. +// WatchDepositorExemptionRevoked is a free log subscription operation binding the contract event 0xd661c54cb18e99cf2cc5b8c6b57f7a094e8a1b967b5de45a609f6d0220e998d4. // -// Solidity: event Depositor_Exemption_Revoked(address indexed depositor) +// Solidity: event DepositorExemptionRevoked(address indexed depositor) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchDepositorExemptionRevoked(opts *bind.WatchOpts, sink chan<- *Erc20BridgeLogicRestrictedDepositorExemptionRevoked, depositor []common.Address) (event.Subscription, error) { var depositorRule []interface{} @@ -2099,7 +2006,7 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchDepo depositorRule = append(depositorRule, depositorItem) } - logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "Depositor_Exemption_Revoked", depositorRule) + logs, sub, err := _Erc20BridgeLogicRestricted.contract.WatchLogs(opts, "DepositorExemptionRevoked", depositorRule) if err != nil { return nil, err } @@ -2110,7 +2017,7 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchDepo case log := <-logs: // New log arrived, parse the event and forward to the user event := new(Erc20BridgeLogicRestrictedDepositorExemptionRevoked) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Depositor_Exemption_Revoked", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "DepositorExemptionRevoked", log); err != nil { return err } event.Raw = log @@ -2131,12 +2038,12 @@ func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) WatchDepo }), nil } -// ParseDepositorExemptionRevoked is a log parse operation binding the contract event 0xe74b113dca87276d976f476a9b4b9da3c780a3262eaabad051ee4e98912936a4. +// ParseDepositorExemptionRevoked is a log parse operation binding the contract event 0xd661c54cb18e99cf2cc5b8c6b57f7a094e8a1b967b5de45a609f6d0220e998d4. // -// Solidity: event Depositor_Exemption_Revoked(address indexed depositor) +// Solidity: event DepositorExemptionRevoked(address indexed depositor) func (_Erc20BridgeLogicRestricted *Erc20BridgeLogicRestrictedFilterer) ParseDepositorExemptionRevoked(log types.Log) (*Erc20BridgeLogicRestrictedDepositorExemptionRevoked, error) { event := new(Erc20BridgeLogicRestrictedDepositorExemptionRevoked) - if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "Depositor_Exemption_Revoked", log); err != nil { + if err := _Erc20BridgeLogicRestricted.contract.UnpackLog(event, "DepositorExemptionRevoked", log); err != nil { return nil, err } event.Raw = log diff --git a/core/contracts/multisig_control/multisig_control.go b/core/contracts/multisig_control/multisig_control.go index 454c8fc2ff9..928fff3648d 100644 --- a/core/contracts/multisig_control/multisig_control.go +++ b/core/contracts/multisig_control/multisig_control.go @@ -31,7 +31,7 @@ var ( // MultisigControlMetaData contains all meta data concerning the MultisigControl contract. var MultisigControlMetaData = &bind.MetaData{ - ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"NonceBurnt\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"new_signer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"SignerAdded\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"old_signer\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"SignerRemoved\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint16\",\"name\":\"new_threshold\",\"type\":\"uint16\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"ThresholdSet\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"new_signer\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"add_signer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"burn_nonce\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"get_current_threshold\",\"outputs\":[{\"internalType\":\"uint16\",\"name\":\"\",\"type\":\"uint16\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"get_valid_signer_count\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"is_nonce_used\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"signer_address\",\"type\":\"address\"}],\"name\":\"is_valid_signer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"old_signer\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"remove_signer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint16\",\"name\":\"new_threshold\",\"type\":\"uint16\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"}],\"name\":\"set_threshold\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"signers\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"signatures\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"message\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"name\":\"verify_signatures\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + ABI: "[{\"type\":\"function\",\"name\":\"addSigner\",\"inputs\":[{\"name\":\"newSigner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"signatures\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"burnNonce\",\"inputs\":[{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"signatures\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"getCurrentThreshold\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint16\",\"internalType\":\"uint16\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"getValidSignerCount\",\"inputs\":[],\"outputs\":[{\"name\":\"\",\"type\":\"uint8\",\"internalType\":\"uint8\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isNonceUsed\",\"inputs\":[{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"isValidSigner\",\"inputs\":[{\"name\":\"signer_address\",\"type\":\"address\",\"internalType\":\"address\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"view\"},{\"type\":\"function\",\"name\":\"removeSigner\",\"inputs\":[{\"name\":\"oldSigner\",\"type\":\"address\",\"internalType\":\"address\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"signatures\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"setThreshold\",\"inputs\":[{\"name\":\"newThreshold\",\"type\":\"uint16\",\"internalType\":\"uint16\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"},{\"name\":\"signatures\",\"type\":\"bytes\",\"internalType\":\"bytes\"}],\"outputs\":[],\"stateMutability\":\"nonpayable\"},{\"type\":\"function\",\"name\":\"verifySignatures\",\"inputs\":[{\"name\":\"signatures\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"message\",\"type\":\"bytes\",\"internalType\":\"bytes\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"internalType\":\"uint256\"}],\"outputs\":[{\"name\":\"\",\"type\":\"bool\",\"internalType\":\"bool\"}],\"stateMutability\":\"nonpayable\"},{\"type\":\"event\",\"name\":\"NonceBurnt\",\"inputs\":[{\"name\":\"nonce\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SignerAdded\",\"inputs\":[{\"name\":\"newSigner\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"SignerRemoved\",\"inputs\":[{\"name\":\"oldSigner\",\"type\":\"address\",\"indexed\":false,\"internalType\":\"address\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false},{\"type\":\"event\",\"name\":\"ThresholdSet\",\"inputs\":[{\"name\":\"newThreshold\",\"type\":\"uint16\",\"indexed\":false,\"internalType\":\"uint16\"},{\"name\":\"nonce\",\"type\":\"uint256\",\"indexed\":false,\"internalType\":\"uint256\"}],\"anonymous\":false}]", } // MultisigControlABI is the input ABI used to generate the binding from. @@ -180,12 +180,12 @@ func (_MultisigControl *MultisigControlTransactorRaw) Transact(opts *bind.Transa return _MultisigControl.Contract.contract.Transact(opts, method, params...) } -// GetCurrentThreshold is a free data retrieval call binding the contract method 0xdbe528df. +// GetCurrentThreshold is a free data retrieval call binding the contract method 0x1b71bf6e. // -// Solidity: function get_current_threshold() view returns(uint16) +// Solidity: function getCurrentThreshold() view returns(uint16) func (_MultisigControl *MultisigControlCaller) GetCurrentThreshold(opts *bind.CallOpts) (uint16, error) { var out []interface{} - err := _MultisigControl.contract.Call(opts, &out, "get_current_threshold") + err := _MultisigControl.contract.Call(opts, &out, "getCurrentThreshold") if err != nil { return *new(uint16), err @@ -197,26 +197,26 @@ func (_MultisigControl *MultisigControlCaller) GetCurrentThreshold(opts *bind.Ca } -// GetCurrentThreshold is a free data retrieval call binding the contract method 0xdbe528df. +// GetCurrentThreshold is a free data retrieval call binding the contract method 0x1b71bf6e. // -// Solidity: function get_current_threshold() view returns(uint16) +// Solidity: function getCurrentThreshold() view returns(uint16) func (_MultisigControl *MultisigControlSession) GetCurrentThreshold() (uint16, error) { return _MultisigControl.Contract.GetCurrentThreshold(&_MultisigControl.CallOpts) } -// GetCurrentThreshold is a free data retrieval call binding the contract method 0xdbe528df. +// GetCurrentThreshold is a free data retrieval call binding the contract method 0x1b71bf6e. // -// Solidity: function get_current_threshold() view returns(uint16) +// Solidity: function getCurrentThreshold() view returns(uint16) func (_MultisigControl *MultisigControlCallerSession) GetCurrentThreshold() (uint16, error) { return _MultisigControl.Contract.GetCurrentThreshold(&_MultisigControl.CallOpts) } -// GetValidSignerCount is a free data retrieval call binding the contract method 0xb04e3dd1. +// GetValidSignerCount is a free data retrieval call binding the contract method 0x86778ed0. // -// Solidity: function get_valid_signer_count() view returns(uint8) +// Solidity: function getValidSignerCount() view returns(uint8) func (_MultisigControl *MultisigControlCaller) GetValidSignerCount(opts *bind.CallOpts) (uint8, error) { var out []interface{} - err := _MultisigControl.contract.Call(opts, &out, "get_valid_signer_count") + err := _MultisigControl.contract.Call(opts, &out, "getValidSignerCount") if err != nil { return *new(uint8), err @@ -228,26 +228,26 @@ func (_MultisigControl *MultisigControlCaller) GetValidSignerCount(opts *bind.Ca } -// GetValidSignerCount is a free data retrieval call binding the contract method 0xb04e3dd1. +// GetValidSignerCount is a free data retrieval call binding the contract method 0x86778ed0. // -// Solidity: function get_valid_signer_count() view returns(uint8) +// Solidity: function getValidSignerCount() view returns(uint8) func (_MultisigControl *MultisigControlSession) GetValidSignerCount() (uint8, error) { return _MultisigControl.Contract.GetValidSignerCount(&_MultisigControl.CallOpts) } -// GetValidSignerCount is a free data retrieval call binding the contract method 0xb04e3dd1. +// GetValidSignerCount is a free data retrieval call binding the contract method 0x86778ed0. // -// Solidity: function get_valid_signer_count() view returns(uint8) +// Solidity: function getValidSignerCount() view returns(uint8) func (_MultisigControl *MultisigControlCallerSession) GetValidSignerCount() (uint8, error) { return _MultisigControl.Contract.GetValidSignerCount(&_MultisigControl.CallOpts) } -// IsNonceUsed is a free data retrieval call binding the contract method 0x5b9fe26b. +// IsNonceUsed is a free data retrieval call binding the contract method 0x5d00bb12. // -// Solidity: function is_nonce_used(uint256 nonce) view returns(bool) +// Solidity: function isNonceUsed(uint256 nonce) view returns(bool) func (_MultisigControl *MultisigControlCaller) IsNonceUsed(opts *bind.CallOpts, nonce *big.Int) (bool, error) { var out []interface{} - err := _MultisigControl.contract.Call(opts, &out, "is_nonce_used", nonce) + err := _MultisigControl.contract.Call(opts, &out, "isNonceUsed", nonce) if err != nil { return *new(bool), err @@ -259,26 +259,26 @@ func (_MultisigControl *MultisigControlCaller) IsNonceUsed(opts *bind.CallOpts, } -// IsNonceUsed is a free data retrieval call binding the contract method 0x5b9fe26b. +// IsNonceUsed is a free data retrieval call binding the contract method 0x5d00bb12. // -// Solidity: function is_nonce_used(uint256 nonce) view returns(bool) +// Solidity: function isNonceUsed(uint256 nonce) view returns(bool) func (_MultisigControl *MultisigControlSession) IsNonceUsed(nonce *big.Int) (bool, error) { return _MultisigControl.Contract.IsNonceUsed(&_MultisigControl.CallOpts, nonce) } -// IsNonceUsed is a free data retrieval call binding the contract method 0x5b9fe26b. +// IsNonceUsed is a free data retrieval call binding the contract method 0x5d00bb12. // -// Solidity: function is_nonce_used(uint256 nonce) view returns(bool) +// Solidity: function isNonceUsed(uint256 nonce) view returns(bool) func (_MultisigControl *MultisigControlCallerSession) IsNonceUsed(nonce *big.Int) (bool, error) { return _MultisigControl.Contract.IsNonceUsed(&_MultisigControl.CallOpts, nonce) } -// IsValidSigner is a free data retrieval call binding the contract method 0x5f061559. +// IsValidSigner is a free data retrieval call binding the contract method 0xd5f50582. // -// Solidity: function is_valid_signer(address signer_address) view returns(bool) +// Solidity: function isValidSigner(address signer_address) view returns(bool) func (_MultisigControl *MultisigControlCaller) IsValidSigner(opts *bind.CallOpts, signer_address common.Address) (bool, error) { var out []interface{} - err := _MultisigControl.contract.Call(opts, &out, "is_valid_signer", signer_address) + err := _MultisigControl.contract.Call(opts, &out, "isValidSigner", signer_address) if err != nil { return *new(bool), err @@ -290,152 +290,121 @@ func (_MultisigControl *MultisigControlCaller) IsValidSigner(opts *bind.CallOpts } -// IsValidSigner is a free data retrieval call binding the contract method 0x5f061559. +// IsValidSigner is a free data retrieval call binding the contract method 0xd5f50582. // -// Solidity: function is_valid_signer(address signer_address) view returns(bool) +// Solidity: function isValidSigner(address signer_address) view returns(bool) func (_MultisigControl *MultisigControlSession) IsValidSigner(signer_address common.Address) (bool, error) { return _MultisigControl.Contract.IsValidSigner(&_MultisigControl.CallOpts, signer_address) } -// IsValidSigner is a free data retrieval call binding the contract method 0x5f061559. +// IsValidSigner is a free data retrieval call binding the contract method 0xd5f50582. // -// Solidity: function is_valid_signer(address signer_address) view returns(bool) +// Solidity: function isValidSigner(address signer_address) view returns(bool) func (_MultisigControl *MultisigControlCallerSession) IsValidSigner(signer_address common.Address) (bool, error) { return _MultisigControl.Contract.IsValidSigner(&_MultisigControl.CallOpts, signer_address) } -// Signers is a free data retrieval call binding the contract method 0x736c0d5b. +// AddSigner is a paid mutator transaction binding the contract method 0xea72ebd0. // -// Solidity: function signers(address ) view returns(bool) -func (_MultisigControl *MultisigControlCaller) Signers(opts *bind.CallOpts, arg0 common.Address) (bool, error) { - var out []interface{} - err := _MultisigControl.contract.Call(opts, &out, "signers", arg0) - - if err != nil { - return *new(bool), err - } - - out0 := *abi.ConvertType(out[0], new(bool)).(*bool) - - return out0, err - -} - -// Signers is a free data retrieval call binding the contract method 0x736c0d5b. -// -// Solidity: function signers(address ) view returns(bool) -func (_MultisigControl *MultisigControlSession) Signers(arg0 common.Address) (bool, error) { - return _MultisigControl.Contract.Signers(&_MultisigControl.CallOpts, arg0) -} - -// Signers is a free data retrieval call binding the contract method 0x736c0d5b. -// -// Solidity: function signers(address ) view returns(bool) -func (_MultisigControl *MultisigControlCallerSession) Signers(arg0 common.Address) (bool, error) { - return _MultisigControl.Contract.Signers(&_MultisigControl.CallOpts, arg0) -} - -// AddSigner is a paid mutator transaction binding the contract method 0xf8e3a660. -// -// Solidity: function add_signer(address new_signer, uint256 nonce, bytes signatures) returns() -func (_MultisigControl *MultisigControlTransactor) AddSigner(opts *bind.TransactOpts, new_signer common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _MultisigControl.contract.Transact(opts, "add_signer", new_signer, nonce, signatures) +// Solidity: function addSigner(address newSigner, uint256 nonce, bytes signatures) returns() +func (_MultisigControl *MultisigControlTransactor) AddSigner(opts *bind.TransactOpts, newSigner common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _MultisigControl.contract.Transact(opts, "addSigner", newSigner, nonce, signatures) } -// AddSigner is a paid mutator transaction binding the contract method 0xf8e3a660. +// AddSigner is a paid mutator transaction binding the contract method 0xea72ebd0. // -// Solidity: function add_signer(address new_signer, uint256 nonce, bytes signatures) returns() -func (_MultisigControl *MultisigControlSession) AddSigner(new_signer common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _MultisigControl.Contract.AddSigner(&_MultisigControl.TransactOpts, new_signer, nonce, signatures) +// Solidity: function addSigner(address newSigner, uint256 nonce, bytes signatures) returns() +func (_MultisigControl *MultisigControlSession) AddSigner(newSigner common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _MultisigControl.Contract.AddSigner(&_MultisigControl.TransactOpts, newSigner, nonce, signatures) } -// AddSigner is a paid mutator transaction binding the contract method 0xf8e3a660. +// AddSigner is a paid mutator transaction binding the contract method 0xea72ebd0. // -// Solidity: function add_signer(address new_signer, uint256 nonce, bytes signatures) returns() -func (_MultisigControl *MultisigControlTransactorSession) AddSigner(new_signer common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _MultisigControl.Contract.AddSigner(&_MultisigControl.TransactOpts, new_signer, nonce, signatures) +// Solidity: function addSigner(address newSigner, uint256 nonce, bytes signatures) returns() +func (_MultisigControl *MultisigControlTransactorSession) AddSigner(newSigner common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _MultisigControl.Contract.AddSigner(&_MultisigControl.TransactOpts, newSigner, nonce, signatures) } -// BurnNonce is a paid mutator transaction binding the contract method 0x5ec51639. +// BurnNonce is a paid mutator transaction binding the contract method 0xc15ce890. // -// Solidity: function burn_nonce(uint256 nonce, bytes signatures) returns() +// Solidity: function burnNonce(uint256 nonce, bytes signatures) returns() func (_MultisigControl *MultisigControlTransactor) BurnNonce(opts *bind.TransactOpts, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _MultisigControl.contract.Transact(opts, "burn_nonce", nonce, signatures) + return _MultisigControl.contract.Transact(opts, "burnNonce", nonce, signatures) } -// BurnNonce is a paid mutator transaction binding the contract method 0x5ec51639. +// BurnNonce is a paid mutator transaction binding the contract method 0xc15ce890. // -// Solidity: function burn_nonce(uint256 nonce, bytes signatures) returns() +// Solidity: function burnNonce(uint256 nonce, bytes signatures) returns() func (_MultisigControl *MultisigControlSession) BurnNonce(nonce *big.Int, signatures []byte) (*types.Transaction, error) { return _MultisigControl.Contract.BurnNonce(&_MultisigControl.TransactOpts, nonce, signatures) } -// BurnNonce is a paid mutator transaction binding the contract method 0x5ec51639. +// BurnNonce is a paid mutator transaction binding the contract method 0xc15ce890. // -// Solidity: function burn_nonce(uint256 nonce, bytes signatures) returns() +// Solidity: function burnNonce(uint256 nonce, bytes signatures) returns() func (_MultisigControl *MultisigControlTransactorSession) BurnNonce(nonce *big.Int, signatures []byte) (*types.Transaction, error) { return _MultisigControl.Contract.BurnNonce(&_MultisigControl.TransactOpts, nonce, signatures) } -// RemoveSigner is a paid mutator transaction binding the contract method 0x98c5f73e. +// RemoveSigner is a paid mutator transaction binding the contract method 0xa3dd8ff2. // -// Solidity: function remove_signer(address old_signer, uint256 nonce, bytes signatures) returns() -func (_MultisigControl *MultisigControlTransactor) RemoveSigner(opts *bind.TransactOpts, old_signer common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _MultisigControl.contract.Transact(opts, "remove_signer", old_signer, nonce, signatures) +// Solidity: function removeSigner(address oldSigner, uint256 nonce, bytes signatures) returns() +func (_MultisigControl *MultisigControlTransactor) RemoveSigner(opts *bind.TransactOpts, oldSigner common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _MultisigControl.contract.Transact(opts, "removeSigner", oldSigner, nonce, signatures) } -// RemoveSigner is a paid mutator transaction binding the contract method 0x98c5f73e. +// RemoveSigner is a paid mutator transaction binding the contract method 0xa3dd8ff2. // -// Solidity: function remove_signer(address old_signer, uint256 nonce, bytes signatures) returns() -func (_MultisigControl *MultisigControlSession) RemoveSigner(old_signer common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _MultisigControl.Contract.RemoveSigner(&_MultisigControl.TransactOpts, old_signer, nonce, signatures) +// Solidity: function removeSigner(address oldSigner, uint256 nonce, bytes signatures) returns() +func (_MultisigControl *MultisigControlSession) RemoveSigner(oldSigner common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _MultisigControl.Contract.RemoveSigner(&_MultisigControl.TransactOpts, oldSigner, nonce, signatures) } -// RemoveSigner is a paid mutator transaction binding the contract method 0x98c5f73e. +// RemoveSigner is a paid mutator transaction binding the contract method 0xa3dd8ff2. // -// Solidity: function remove_signer(address old_signer, uint256 nonce, bytes signatures) returns() -func (_MultisigControl *MultisigControlTransactorSession) RemoveSigner(old_signer common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _MultisigControl.Contract.RemoveSigner(&_MultisigControl.TransactOpts, old_signer, nonce, signatures) +// Solidity: function removeSigner(address oldSigner, uint256 nonce, bytes signatures) returns() +func (_MultisigControl *MultisigControlTransactorSession) RemoveSigner(oldSigner common.Address, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _MultisigControl.Contract.RemoveSigner(&_MultisigControl.TransactOpts, oldSigner, nonce, signatures) } -// SetThreshold is a paid mutator transaction binding the contract method 0x50ac8df8. +// SetThreshold is a paid mutator transaction binding the contract method 0xee9aea8a. // -// Solidity: function set_threshold(uint16 new_threshold, uint256 nonce, bytes signatures) returns() -func (_MultisigControl *MultisigControlTransactor) SetThreshold(opts *bind.TransactOpts, new_threshold uint16, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _MultisigControl.contract.Transact(opts, "set_threshold", new_threshold, nonce, signatures) +// Solidity: function setThreshold(uint16 newThreshold, uint256 nonce, bytes signatures) returns() +func (_MultisigControl *MultisigControlTransactor) SetThreshold(opts *bind.TransactOpts, newThreshold uint16, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _MultisigControl.contract.Transact(opts, "setThreshold", newThreshold, nonce, signatures) } -// SetThreshold is a paid mutator transaction binding the contract method 0x50ac8df8. +// SetThreshold is a paid mutator transaction binding the contract method 0xee9aea8a. // -// Solidity: function set_threshold(uint16 new_threshold, uint256 nonce, bytes signatures) returns() -func (_MultisigControl *MultisigControlSession) SetThreshold(new_threshold uint16, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _MultisigControl.Contract.SetThreshold(&_MultisigControl.TransactOpts, new_threshold, nonce, signatures) +// Solidity: function setThreshold(uint16 newThreshold, uint256 nonce, bytes signatures) returns() +func (_MultisigControl *MultisigControlSession) SetThreshold(newThreshold uint16, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _MultisigControl.Contract.SetThreshold(&_MultisigControl.TransactOpts, newThreshold, nonce, signatures) } -// SetThreshold is a paid mutator transaction binding the contract method 0x50ac8df8. +// SetThreshold is a paid mutator transaction binding the contract method 0xee9aea8a. // -// Solidity: function set_threshold(uint16 new_threshold, uint256 nonce, bytes signatures) returns() -func (_MultisigControl *MultisigControlTransactorSession) SetThreshold(new_threshold uint16, nonce *big.Int, signatures []byte) (*types.Transaction, error) { - return _MultisigControl.Contract.SetThreshold(&_MultisigControl.TransactOpts, new_threshold, nonce, signatures) +// Solidity: function setThreshold(uint16 newThreshold, uint256 nonce, bytes signatures) returns() +func (_MultisigControl *MultisigControlTransactorSession) SetThreshold(newThreshold uint16, nonce *big.Int, signatures []byte) (*types.Transaction, error) { + return _MultisigControl.Contract.SetThreshold(&_MultisigControl.TransactOpts, newThreshold, nonce, signatures) } -// VerifySignatures is a paid mutator transaction binding the contract method 0xba73659a. +// VerifySignatures is a paid mutator transaction binding the contract method 0x3fef2806. // -// Solidity: function verify_signatures(bytes signatures, bytes message, uint256 nonce) returns(bool) +// Solidity: function verifySignatures(bytes signatures, bytes message, uint256 nonce) returns(bool) func (_MultisigControl *MultisigControlTransactor) VerifySignatures(opts *bind.TransactOpts, signatures []byte, message []byte, nonce *big.Int) (*types.Transaction, error) { - return _MultisigControl.contract.Transact(opts, "verify_signatures", signatures, message, nonce) + return _MultisigControl.contract.Transact(opts, "verifySignatures", signatures, message, nonce) } -// VerifySignatures is a paid mutator transaction binding the contract method 0xba73659a. +// VerifySignatures is a paid mutator transaction binding the contract method 0x3fef2806. // -// Solidity: function verify_signatures(bytes signatures, bytes message, uint256 nonce) returns(bool) +// Solidity: function verifySignatures(bytes signatures, bytes message, uint256 nonce) returns(bool) func (_MultisigControl *MultisigControlSession) VerifySignatures(signatures []byte, message []byte, nonce *big.Int) (*types.Transaction, error) { return _MultisigControl.Contract.VerifySignatures(&_MultisigControl.TransactOpts, signatures, message, nonce) } -// VerifySignatures is a paid mutator transaction binding the contract method 0xba73659a. +// VerifySignatures is a paid mutator transaction binding the contract method 0x3fef2806. // -// Solidity: function verify_signatures(bytes signatures, bytes message, uint256 nonce) returns(bool) +// Solidity: function verifySignatures(bytes signatures, bytes message, uint256 nonce) returns(bool) func (_MultisigControl *MultisigControlTransactorSession) VerifySignatures(signatures []byte, message []byte, nonce *big.Int) (*types.Transaction, error) { return _MultisigControl.Contract.VerifySignatures(&_MultisigControl.TransactOpts, signatures, message, nonce) } @@ -650,7 +619,7 @@ type MultisigControlSignerAdded struct { // FilterSignerAdded is a free log retrieval operation binding the contract event 0x50999ebf9b59bf3157a58816611976f2d723378ad51457d7b0413209e0cdee59. // -// Solidity: event SignerAdded(address new_signer, uint256 nonce) +// Solidity: event SignerAdded(address newSigner, uint256 nonce) func (_MultisigControl *MultisigControlFilterer) FilterSignerAdded(opts *bind.FilterOpts) (*MultisigControlSignerAddedIterator, error) { logs, sub, err := _MultisigControl.contract.FilterLogs(opts, "SignerAdded") @@ -662,7 +631,7 @@ func (_MultisigControl *MultisigControlFilterer) FilterSignerAdded(opts *bind.Fi // WatchSignerAdded is a free log subscription operation binding the contract event 0x50999ebf9b59bf3157a58816611976f2d723378ad51457d7b0413209e0cdee59. // -// Solidity: event SignerAdded(address new_signer, uint256 nonce) +// Solidity: event SignerAdded(address newSigner, uint256 nonce) func (_MultisigControl *MultisigControlFilterer) WatchSignerAdded(opts *bind.WatchOpts, sink chan<- *MultisigControlSignerAdded) (event.Subscription, error) { logs, sub, err := _MultisigControl.contract.WatchLogs(opts, "SignerAdded") @@ -699,7 +668,7 @@ func (_MultisigControl *MultisigControlFilterer) WatchSignerAdded(opts *bind.Wat // ParseSignerAdded is a log parse operation binding the contract event 0x50999ebf9b59bf3157a58816611976f2d723378ad51457d7b0413209e0cdee59. // -// Solidity: event SignerAdded(address new_signer, uint256 nonce) +// Solidity: event SignerAdded(address newSigner, uint256 nonce) func (_MultisigControl *MultisigControlFilterer) ParseSignerAdded(log types.Log) (*MultisigControlSignerAdded, error) { event := new(MultisigControlSignerAdded) if err := _MultisigControl.contract.UnpackLog(event, "SignerAdded", log); err != nil { @@ -785,7 +754,7 @@ type MultisigControlSignerRemoved struct { // FilterSignerRemoved is a free log retrieval operation binding the contract event 0x99c1d2c0ed8107e4db2e5dbfb10a2549cd2a63cbe39cf99d2adffbcd03954418. // -// Solidity: event SignerRemoved(address old_signer, uint256 nonce) +// Solidity: event SignerRemoved(address oldSigner, uint256 nonce) func (_MultisigControl *MultisigControlFilterer) FilterSignerRemoved(opts *bind.FilterOpts) (*MultisigControlSignerRemovedIterator, error) { logs, sub, err := _MultisigControl.contract.FilterLogs(opts, "SignerRemoved") @@ -797,7 +766,7 @@ func (_MultisigControl *MultisigControlFilterer) FilterSignerRemoved(opts *bind. // WatchSignerRemoved is a free log subscription operation binding the contract event 0x99c1d2c0ed8107e4db2e5dbfb10a2549cd2a63cbe39cf99d2adffbcd03954418. // -// Solidity: event SignerRemoved(address old_signer, uint256 nonce) +// Solidity: event SignerRemoved(address oldSigner, uint256 nonce) func (_MultisigControl *MultisigControlFilterer) WatchSignerRemoved(opts *bind.WatchOpts, sink chan<- *MultisigControlSignerRemoved) (event.Subscription, error) { logs, sub, err := _MultisigControl.contract.WatchLogs(opts, "SignerRemoved") @@ -834,7 +803,7 @@ func (_MultisigControl *MultisigControlFilterer) WatchSignerRemoved(opts *bind.W // ParseSignerRemoved is a log parse operation binding the contract event 0x99c1d2c0ed8107e4db2e5dbfb10a2549cd2a63cbe39cf99d2adffbcd03954418. // -// Solidity: event SignerRemoved(address old_signer, uint256 nonce) +// Solidity: event SignerRemoved(address oldSigner, uint256 nonce) func (_MultisigControl *MultisigControlFilterer) ParseSignerRemoved(log types.Log) (*MultisigControlSignerRemoved, error) { event := new(MultisigControlSignerRemoved) if err := _MultisigControl.contract.UnpackLog(event, "SignerRemoved", log); err != nil { @@ -920,7 +889,7 @@ type MultisigControlThresholdSet struct { // FilterThresholdSet is a free log retrieval operation binding the contract event 0xf6d24c23627520a3b70e5dc66aa1249844b4bb407c2c153d9000a2b14a1e3c11. // -// Solidity: event ThresholdSet(uint16 new_threshold, uint256 nonce) +// Solidity: event ThresholdSet(uint16 newThreshold, uint256 nonce) func (_MultisigControl *MultisigControlFilterer) FilterThresholdSet(opts *bind.FilterOpts) (*MultisigControlThresholdSetIterator, error) { logs, sub, err := _MultisigControl.contract.FilterLogs(opts, "ThresholdSet") @@ -932,7 +901,7 @@ func (_MultisigControl *MultisigControlFilterer) FilterThresholdSet(opts *bind.F // WatchThresholdSet is a free log subscription operation binding the contract event 0xf6d24c23627520a3b70e5dc66aa1249844b4bb407c2c153d9000a2b14a1e3c11. // -// Solidity: event ThresholdSet(uint16 new_threshold, uint256 nonce) +// Solidity: event ThresholdSet(uint16 newThreshold, uint256 nonce) func (_MultisigControl *MultisigControlFilterer) WatchThresholdSet(opts *bind.WatchOpts, sink chan<- *MultisigControlThresholdSet) (event.Subscription, error) { logs, sub, err := _MultisigControl.contract.WatchLogs(opts, "ThresholdSet") @@ -969,7 +938,7 @@ func (_MultisigControl *MultisigControlFilterer) WatchThresholdSet(opts *bind.Wa // ParseThresholdSet is a log parse operation binding the contract event 0xf6d24c23627520a3b70e5dc66aa1249844b4bb407c2c153d9000a2b14a1e3c11. // -// Solidity: event ThresholdSet(uint16 new_threshold, uint256 nonce) +// Solidity: event ThresholdSet(uint16 newThreshold, uint256 nonce) func (_MultisigControl *MultisigControlFilterer) ParseThresholdSet(log types.Log) (*MultisigControlThresholdSet, error) { event := new(MultisigControlThresholdSet) if err := _MultisigControl.contract.UnpackLog(event, "ThresholdSet", log); err != nil { diff --git a/core/events/bus.go b/core/events/bus.go index bc21bac0741..3fa9e0f6c05 100644 --- a/core/events/bus.go +++ b/core/events/bus.go @@ -174,6 +174,10 @@ const ( CancelledOrdersEvent GameScoresEvent AMMPoolEvent + VolumeRebateProgramStartedEvent + VolumeRebateProgramEndedEvent + VolumeRebateProgramUpdatedEvent + VolumeRebateStatsUpdatedEvent ) var ( @@ -277,6 +281,10 @@ var ( eventspb.BusEventType_BUS_EVENT_TYPE_CANCELLED_ORDERS: CancelledOrdersEvent, eventspb.BusEventType_BUS_EVENT_TYPE_GAME_SCORES: GameScoresEvent, eventspb.BusEventType_BUS_EVENT_TYPE_AMM: AMMPoolEvent, + eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_PROGRAM_STARTED: VolumeRebateProgramStartedEvent, + eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_PROGRAM_ENDED: VolumeRebateProgramEndedEvent, + eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_PROGRAM_UPDATED: VolumeRebateProgramUpdatedEvent, + eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_STATS_UPDATED: VolumeRebateStatsUpdatedEvent, // If adding a type here, please also add it to datanode/broker/convert.go } @@ -371,6 +379,11 @@ var ( CancelledOrdersEvent: eventspb.BusEventType_BUS_EVENT_TYPE_CANCELLED_ORDERS, GameScoresEvent: eventspb.BusEventType_BUS_EVENT_TYPE_GAME_SCORES, AMMPoolEvent: eventspb.BusEventType_BUS_EVENT_TYPE_AMM, + VolumeRebateProgramStartedEvent: eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_PROGRAM_STARTED, + VolumeRebateProgramEndedEvent: eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_PROGRAM_ENDED, + VolumeRebateProgramUpdatedEvent: eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_PROGRAM_UPDATED, + VolumeRebateStatsUpdatedEvent: eventspb.BusEventType_BUS_EVENT_TYPE_VOLUME_REBATE_STATS_UPDATED, + // If adding a type here, please also add it to datanode/broker/convert.go } @@ -464,6 +477,10 @@ var ( CancelledOrdersEvent: "CancelledOrdersEvent", GameScoresEvent: "GameScoresEvent", AMMPoolEvent: "AMMPoolEvent", + VolumeRebateProgramStartedEvent: "VolumeRebateProgramStartedEvent", + VolumeRebateProgramEndedEvent: "VolumeRebateProgramEndedEvent", + VolumeRebateProgramUpdatedEvent: "VolumeRebateProgramUpdatedEvent", + VolumeRebateStatsUpdatedEvent: "VolumeRebateStatsUpdatedEvent", } ) diff --git a/core/events/referral_set.go b/core/events/referral_set.go index 7440065ec9c..8b7f67bb124 100644 --- a/core/events/referral_set.go +++ b/core/events/referral_set.go @@ -71,15 +71,13 @@ type ReferralSetStatsUpdated struct { func (t ReferralSetStatsUpdated) Unwrap() *types.ReferralSetStats { volume, _ := num.UintFromString(t.e.ReferralSetRunningNotionalTakerVolume, 10) stats := map[types.PartyID]*types.RefereeStats{} - rewardFactor, _ := num.DecimalFromString(t.e.RewardFactor) rewardsMultiplier, _ := num.DecimalFromString(t.e.RewardsMultiplier) - rewardsFactorMultiplier, _ := num.DecimalFromString(t.e.RewardsFactorMultiplier) - + rewardsFactorsMultiplier := types.FactorsFromRewardFactorsWithDefault(t.e.RewardFactorsMultiplier, t.e.RewardsFactorMultiplier) + rewardFactors := types.FactorsFromRewardFactorsWithDefault(t.e.RewardFactors, t.e.RewardFactor) for _, stat := range t.e.RefereesStats { - discountFactor, _ := num.DecimalFromString(stat.DiscountFactor) - + discountFactors := types.FactorsFromDiscountFactorsWithDefault(stat.DiscountFactors, stat.DiscountFactor) stats[types.PartyID(stat.PartyId)] = &types.RefereeStats{ - DiscountFactor: discountFactor, + DiscountFactors: discountFactors, } } @@ -89,9 +87,9 @@ func (t ReferralSetStatsUpdated) Unwrap() *types.ReferralSetStats { WasEligible: t.e.WasEligible, ReferralSetRunningVolume: volume, RefereesStats: stats, - RewardFactor: rewardFactor, + RewardFactors: rewardFactors, RewardsMultiplier: rewardsMultiplier, - RewardsFactorMultiplier: rewardsFactorMultiplier, + RewardsFactorsMultiplier: rewardsFactorsMultiplier, } } @@ -113,7 +111,7 @@ func NewReferralSetStatsUpdatedEvent(ctx context.Context, update *types.Referral for partyID, stat := range update.RefereesStats { refereesStats = append(refereesStats, &eventspb.RefereeStats{ PartyId: string(partyID), - DiscountFactor: stat.DiscountFactor.String(), + DiscountFactors: stat.DiscountFactors.IntoDiscountFactorsProto(), EpochNotionalTakerVolume: stat.TakerVolume.String(), }) } @@ -131,9 +129,9 @@ func NewReferralSetStatsUpdatedEvent(ctx context.Context, update *types.Referral ReferralSetRunningNotionalTakerVolume: update.ReferralSetRunningVolume.String(), ReferrerTakerVolume: update.ReferrerTakerVolume.String(), RefereesStats: refereesStats, - RewardFactor: update.RewardFactor.String(), + RewardFactors: update.RewardFactors.IntoRewardFactorsProto(), RewardsMultiplier: update.RewardsMultiplier.String(), - RewardsFactorMultiplier: update.RewardsFactorMultiplier.String(), + RewardFactorsMultiplier: update.RewardsFactorsMultiplier.IntoRewardFactorsProto(), }, } } diff --git a/core/events/transaction_result.go b/core/events/transaction_result.go index 6187393e336..8e35f1070c8 100644 --- a/core/events/transaction_result.go +++ b/core/events/transaction_result.go @@ -18,6 +18,7 @@ package events import ( "context" "fmt" + "sort" commandspb "code.vegaprotocol.io/vega/protos/vega/commands/v1" eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" @@ -48,15 +49,63 @@ func NewTransactionResultEventSuccess( evt := &TransactionResult{ Base: newBase(ctx, TransactionResultEvent), evt: &eventspb.TransactionResult{ - PartyId: party, - Hash: hash, - Status: true, + PartyId: party, + Hash: hash, + Status: true, + StatusDetail: eventspb.TransactionResult_STATUS_SUCCESS, }, } return evt.setTx(tx) } +type RawErrors interface { + GetRawErrors() map[string][]error +} + +func makeFailureDetails(err error) *eventspb.TransactionResult_FailureDetails { + if rawErr, isRawErr := err.(RawErrors); isRawErr { + keyErrors := []*eventspb.TransactionResult_KeyErrors{} + for k, v := range rawErr.GetRawErrors() { + e := &eventspb.TransactionResult_KeyErrors{ + Key: k, + } + + for _, ve := range v { + e.Errors = append(e.Errors, ve.Error()) + } + + keyErrors = append(keyErrors, e) + } + + sort.Slice(keyErrors, func(i, j int) bool { + return keyErrors[i].Key < keyErrors[j].Key + }) + + return &eventspb.TransactionResult_FailureDetails{ + Errors: keyErrors, + } + } + + return &eventspb.TransactionResult_FailureDetails{ + Error: err.Error(), + } +} + +type PartialError interface { + IsPartial() bool +} + +func getErrorStatus(err error) eventspb.TransactionResult_Status { + if partialErr, isPartialErr := err.(PartialError); isPartialErr { + if partialErr.IsPartial() { + return eventspb.TransactionResult_STATUS_PARTIAL_SUCCESS + } + } + + return eventspb.TransactionResult_STATUS_FAILURE +} + func NewTransactionResultEventFailure( ctx context.Context, hash, party string, @@ -66,13 +115,12 @@ func NewTransactionResultEventFailure( evt := &TransactionResult{ Base: newBase(ctx, TransactionResultEvent), evt: &eventspb.TransactionResult{ - PartyId: party, - Hash: hash, - Status: false, + PartyId: party, + Hash: hash, + Status: false, + StatusDetail: getErrorStatus(err), Extra: &eventspb.TransactionResult_Failure{ - Failure: &eventspb.TransactionResult_FailureDetails{ - Error: err.Error(), - }, + Failure: makeFailureDetails(err), }, }, } diff --git a/core/events/volume_rebate_program.go b/core/events/volume_rebate_program.go new file mode 100644 index 00000000000..1ff9f86663a --- /dev/null +++ b/core/events/volume_rebate_program.go @@ -0,0 +1,172 @@ +// Copyright (C) 2023 Gobalsky Labs Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package events + +import ( + "context" + "time" + + "code.vegaprotocol.io/vega/core/types" + "code.vegaprotocol.io/vega/libs/ptr" + eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" +) + +type VolumeRebateProgramStarted struct { + *Base + e *eventspb.VolumeRebateProgramStarted +} + +func (v *VolumeRebateProgramStarted) GetVolumeRebateProgramStarted() *eventspb.VolumeRebateProgramStarted { + return v.e +} + +func (t *VolumeRebateProgramStarted) StreamMessage() *eventspb.BusEvent { + busEvent := newBusEventFromBase(t.Base) + busEvent.Event = &eventspb.BusEvent_VolumeRebateProgramStarted{ + VolumeRebateProgramStarted: t.e, + } + + return busEvent +} + +func NewVolumeRebateProgramStartedEvent(ctx context.Context, p *types.VolumeRebateProgram, epochTime time.Time, epoch uint64) *VolumeRebateProgramStarted { + return &VolumeRebateProgramStarted{ + Base: newBase(ctx, VolumeRebateProgramStartedEvent), + e: &eventspb.VolumeRebateProgramStarted{ + Program: p.IntoProto(), + StartedAt: epochTime.UnixNano(), + AtEpoch: epoch, + }, + } +} + +func VolumeRebateProgramStartedEventFromStream(ctx context.Context, be *eventspb.BusEvent) *VolumeRebateProgramStarted { + return &VolumeRebateProgramStarted{ + Base: newBaseFromBusEvent(ctx, VolumeRebateProgramStartedEvent, be), + e: be.GetVolumeRebateProgramStarted(), + } +} + +type VolumeRebateProgramUpdated struct { + *Base + e *eventspb.VolumeRebateProgramUpdated +} + +func (v *VolumeRebateProgramUpdated) GetVolumeRebateProgramUpdated() *eventspb.VolumeRebateProgramUpdated { + return v.e +} + +func (t *VolumeRebateProgramUpdated) StreamMessage() *eventspb.BusEvent { + busEvent := newBusEventFromBase(t.Base) + busEvent.Event = &eventspb.BusEvent_VolumeRebateProgramUpdated{ + VolumeRebateProgramUpdated: t.e, + } + + return busEvent +} + +func NewVolumeRebateProgramUpdatedEvent(ctx context.Context, p *types.VolumeRebateProgram, epochTime time.Time, epoch uint64) *VolumeRebateProgramUpdated { + return &VolumeRebateProgramUpdated{ + Base: newBase(ctx, VolumeRebateProgramUpdatedEvent), + e: &eventspb.VolumeRebateProgramUpdated{ + Program: p.IntoProto(), + UpdatedAt: epochTime.UnixNano(), + AtEpoch: epoch, + }, + } +} + +func VolumeRebateProgramUpdatedEventFromStream(ctx context.Context, be *eventspb.BusEvent) *VolumeRebateProgramUpdated { + return &VolumeRebateProgramUpdated{ + Base: newBaseFromBusEvent(ctx, VolumeRebateProgramUpdatedEvent, be), + e: be.GetVolumeRebateProgramUpdated(), + } +} + +type VolumeRebateProgramEnded struct { + *Base + e *eventspb.VolumeRebateProgramEnded +} + +func (v *VolumeRebateProgramEnded) GetVolumeRebateProgramEnded() *eventspb.VolumeRebateProgramEnded { + return v.e +} + +func (t *VolumeRebateProgramEnded) StreamMessage() *eventspb.BusEvent { + busEvent := newBusEventFromBase(t.Base) + busEvent.Event = &eventspb.BusEvent_VolumeRebateProgramEnded{ + VolumeRebateProgramEnded: t.e, + } + + return busEvent +} + +func NewVolumeRebateProgramEndedEvent(ctx context.Context, version uint64, id string, epochTime time.Time, epoch uint64) *VolumeRebateProgramEnded { + return &VolumeRebateProgramEnded{ + Base: newBase(ctx, VolumeRebateProgramEndedEvent), + e: &eventspb.VolumeRebateProgramEnded{ + Version: version, + Id: id, + EndedAt: epochTime.UnixNano(), + AtEpoch: epoch, + }, + } +} + +func VolumeRebateProgramEndedEventFromStream(ctx context.Context, be *eventspb.BusEvent) *VolumeRebateProgramEnded { + return &VolumeRebateProgramEnded{ + Base: newBaseFromBusEvent(ctx, VolumeRebateProgramEndedEvent, be), + e: be.GetVolumeRebateProgramEnded(), + } +} + +type VolumeRebateStatsUpdated struct { + *Base + vdsu eventspb.VolumeRebateStatsUpdated +} + +func NewVolumeRebateStatsUpdatedEvent(ctx context.Context, vdsu *eventspb.VolumeRebateStatsUpdated) *VolumeRebateStatsUpdated { + order := &VolumeRebateStatsUpdated{ + Base: newBase(ctx, VolumeRebateStatsUpdatedEvent), + vdsu: *vdsu, + } + return order +} + +func (p *VolumeRebateStatsUpdated) VolumeRebateStatsUpdated() *eventspb.VolumeRebateStatsUpdated { + return ptr.From(p.vdsu) +} + +func (p VolumeRebateStatsUpdated) Proto() eventspb.VolumeRebateStatsUpdated { + return p.vdsu +} + +func (p VolumeRebateStatsUpdated) StreamMessage() *eventspb.BusEvent { + busEvent := newBusEventFromBase(p.Base) + busEvent.Event = &eventspb.BusEvent_VolumeRebateStatsUpdated{ + VolumeRebateStatsUpdated: ptr.From(p.vdsu), + } + + return busEvent +} + +func VolumeRebateStatsUpdatedEventFromStream(ctx context.Context, be *eventspb.BusEvent) *VolumeRebateStatsUpdated { + order := &VolumeRebateStatsUpdated{ + Base: newBaseFromBusEvent(ctx, VolumeRebateStatsUpdatedEvent, be), + vdsu: ptr.UnBox(be.GetVolumeRebateStatsUpdated()), + } + return order +} diff --git a/core/evtforward/ethereum/filterer.go b/core/evtforward/ethereum/filterer.go index 0396bce46ca..43dc2b7ebcc 100644 --- a/core/evtforward/ethereum/filterer.go +++ b/core/evtforward/ethereum/filterer.go @@ -42,18 +42,18 @@ import ( const ( logFiltererLogger = "log-filterer" - eventAssetListed = "Asset_Listed" - eventAssetRemoved = "Asset_Removed" - eventAssetDeposited = "Asset_Deposited" - eventAssetWithdrawn = "Asset_Withdrawn" - eventStakeDeposited = "Stake_Deposited" - eventStakeRemoved = "Stake_Removed" + eventAssetListed = "AssetListed" + eventAssetRemoved = "AssetRemoved" + eventAssetDeposited = "AssetDeposited" + eventAssetWithdrawn = "AssetWithdrawn" + eventStakeDeposited = "StakeDeposited" + eventStakeRemoved = "StakeRemoved" eventSignerAdded = "SignerAdded" eventSignerRemoved = "SignerRemoved" eventThresholdSet = "ThresholdSet" - eventAssetLimitsUpdated = "Asset_Limits_Updated" - eventBridgeStopped = "Bridge_Stopped" - eventBridgeResumed = "Bridge_Resumed" + eventAssetLimitsUpdated = "AssetLimitsUpdated" + eventBridgeStopped = "BridgeStopped" + eventBridgeResumed = "BridgeResumed" ) // Assets ... diff --git a/core/execution/amm/engine.go b/core/execution/amm/engine.go index 5dbc8038289..e74ed4a46b3 100644 --- a/core/execution/amm/engine.go +++ b/core/execution/amm/engine.go @@ -55,6 +55,7 @@ const ( type Collateral interface { GetAssetQuantum(asset string) (num.Decimal, error) + GetAllParties() []string GetPartyMarginAccount(market, party, asset string) (*types.Account, error) GetPartyGeneralAccount(party, asset string) (*types.Account, error) SubAccountUpdate( @@ -99,17 +100,16 @@ func (s *Sqrter) sqrt(u *num.Uint) num.Decimal { return num.DecimalZero() } - if r, ok := s.cache[u.String()]; ok { - return r - } + // caching was disabled here since it caused problems with snapshots (https://github.com/vegaprotocol/vega/issues/11523) + // and we changed tact to instead cache constant terms in calculations that *involve* sqrt's instead of the sqrt result + // directly. I'm leaving the ghost of this cache here incase we need to introduce it again, maybe as a LRU cache instead. + // if r, ok := s.cache[u.String()]; ok { + // return r + // } - // TODO that we may need to re-visit this depending on the performance impact - // but for now lets do it "properly" in full decimals and work out how we can - // improve it once we have reg-tests and performance data. r := num.UintOne().Sqrt(u) - // and cache it -- we can also maybe be more clever here and use a LRU but thats for later - s.cache[u.String()] = r + // s.cache[u.String()] = r return r } @@ -175,7 +175,7 @@ func New( priceFactor: priceFactor, positionFactor: positionFactor, parties: parties, - oneTick: oneTick, + oneTick: num.Max(num.UintOne(), oneTick), } } @@ -198,13 +198,8 @@ func NewFromProto( e.ammParties[v.Key] = v.Value } - // TODO consider whether we want the cache in the snapshot, it might be pretty large/slow and I'm not sure what we gain - for _, v := range state.Sqrter { - e.rooter.cache[v.Key] = num.MustDecimalFromString(v.Value) - } - for _, v := range state.Pools { - p, err := NewPoolFromProto(log, e.rooter.sqrt, e.collateral, e.position, v.Pool, v.Party, priceFactor) + p, err := NewPoolFromProto(log, e.rooter.sqrt, e.collateral, e.position, v.Pool, v.Party, priceFactor, positionFactor) if err != nil { return e, err } @@ -216,19 +211,10 @@ func NewFromProto( func (e *Engine) IntoProto() *v1.AmmState { state := &v1.AmmState{ - Sqrter: make([]*v1.StringMapEntry, 0, len(e.rooter.cache)), AmmPartyIds: make([]*v1.StringMapEntry, 0, len(e.ammParties)), Pools: make([]*v1.PoolMapEntry, 0, len(e.pools)), } - for k, v := range e.rooter.cache { - state.Sqrter = append(state.Sqrter, &v1.StringMapEntry{ - Key: k, - Value: v.String(), - }) - } - sort.Slice(state.Sqrter, func(i, j int) bool { return state.Sqrter[i].Key < state.Sqrter[j].Key }) - for k, v := range e.ammParties { state.AmmPartyIds = append(state.AmmPartyIds, &v1.StringMapEntry{ Key: k, @@ -427,15 +413,27 @@ func (e *Engine) submit(active []*Pool, agg *types.Order, inner, outer *num.Uint // if the pools consume the whole incoming order's volume, share it out pro-rata if agg.Remaining < total { + maxVolumes := make([]uint64, 0, len(volumes)) + // copy the available volumes for rounding. + maxVolumes = append(maxVolumes, volumes...) var retotal uint64 for i := range volumes { volumes[i] = agg.Remaining * volumes[i] / total retotal += volumes[i] } - // any lost crumbs due to integer division is given to the first pool + // any lost crumbs due to integer division is given to the pools that can accommodate it. if d := agg.Remaining - retotal; d != 0 { - volumes[0] += d + for i, v := range volumes { + if delta := maxVolumes[i] - v; delta != 0 { + if delta >= d { + volumes[i] += d + break + } + volumes[i] += delta + d -= delta + } + } } } @@ -695,6 +693,14 @@ func (e *Engine) Create( return nil, err } + // sanity check, a *new* AMM should not already have a position. If it does it means that the party + // previously had an AMM but it was stopped/cancelled while still holding a position which should not happen. + // It should have either handed its position over to the liquidation engine, or be in reduce-only mode + // and only be removed when its position is 0. + if pool.getPosition() != 0 { + e.log.Panic("AMM has position before existing") + } + e.log.Debug("AMM created", logging.String("owner", submit.Party), logging.String("poolID", pool.ID), @@ -814,6 +820,10 @@ func (e *Engine) MarketClosing(ctx context.Context) error { e.sendUpdate(ctx, p) e.marketActivityTracker.RemoveAMMParty(e.assetID, e.marketID, p.AMMParty) } + + e.pools = nil + e.poolsCpy = nil + e.ammParties = nil return nil } diff --git a/core/execution/amm/engine_test.go b/core/execution/amm/engine_test.go index 77c0dd7b426..f387988a8f4 100644 --- a/core/execution/amm/engine_test.go +++ b/core/execution/amm/engine_test.go @@ -462,7 +462,7 @@ func TestBestPricesAndVolumeNearBound(t *testing.T) { expectSubaccountCreation(t, tst, party, subAccount) whenAMMIsSubmitted(t, tst, submit) - tst.pos.EXPECT().GetPositionsByParty(gomock.Any()).Times(5).Return( + tst.pos.EXPECT().GetPositionsByParty(gomock.Any()).Times(3).Return( []events.MarketPosition{&marketPosition{size: 0, averageEntry: num.NewUint(0)}}, ) @@ -473,7 +473,7 @@ func TestBestPricesAndVolumeNearBound(t *testing.T) { assert.Equal(t, 1192, int(avolume)) // lets move its position so that the fair price is within one tick of the AMMs upper boundary - tst.pos.EXPECT().GetPositionsByParty(gomock.Any()).Times(5).Return( + tst.pos.EXPECT().GetPositionsByParty(gomock.Any()).Times(3).Return( []events.MarketPosition{&marketPosition{size: -222000, averageEntry: num.NewUint(0)}}, ) @@ -484,7 +484,7 @@ func TestBestPricesAndVolumeNearBound(t *testing.T) { assert.Equal(t, 103, int(avolume)) // lets move its position so that the fair price is within one tick of the AMMs upper boundary - tst.pos.EXPECT().GetPositionsByParty(gomock.Any()).Times(5).Return( + tst.pos.EXPECT().GetPositionsByParty(gomock.Any()).Times(3).Return( []events.MarketPosition{&marketPosition{size: 270400, averageEntry: num.NewUint(0)}}, ) @@ -492,7 +492,7 @@ func TestBestPricesAndVolumeNearBound(t *testing.T) { assert.Equal(t, "180000", bid.String()) // make sure we are capped to the boundary and not 179904 assert.Equal(t, "180104", ask.String()) assert.Equal(t, 58, int(bvolume)) - assert.Equal(t, 1463, int(avolume)) + assert.Equal(t, 1460, int(avolume)) } func testClosingReduceOnlyPool(t *testing.T) { @@ -656,9 +656,9 @@ func testMarketClosure(t *testing.T) { } require.NoError(t, tst.engine.MarketClosing(ctx)) - for _, p := range tst.engine.poolsCpy { - assert.Equal(t, types.AMMPoolStatusStopped, p.status) - } + require.Equal(t, 0, len(tst.engine.pools)) + require.Equal(t, 0, len(tst.engine.poolsCpy)) + require.Equal(t, 0, len(tst.engine.ammParties)) } func expectSubaccountCreation(t *testing.T, tst *tstEngine, party, subAccount string) { @@ -705,6 +705,8 @@ func whenAMMIsSubmitted(t *testing.T, tst *tstEngine, submission *types.SubmitAM subAccount := DeriveAMMParty(party, tst.marketID, "AMMv1", 0) expectBalanceChecks(t, tst, party, subAccount, submission.CommitmentAmount.Uint64()) + ensurePosition(t, tst.pos, 0, nil) + ctx := context.Background() pool, err := tst.engine.Create(ctx, submission, vgcrypto.RandomHash(), riskFactors, scalingFactors, slippage) require.NoError(t, err) @@ -794,7 +796,7 @@ func getTestEngineWithFactors(t *testing.T, priceFactor, positionFactor num.Deci teams := cmocks.NewMockTeams(ctrl) balanceChecker := cmocks.NewMockAccountBalanceChecker(ctrl) - mat := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + mat := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, col) parties := cmocks.NewMockParties(ctrl) parties.EXPECT().AssignDeriveKey(gomock.Any(), gomock.Any(), gomock.Any()).AnyTimes() diff --git a/core/execution/amm/estimator.go b/core/execution/amm/estimator.go index 453a01aba89..7c076c33686 100644 --- a/core/execution/amm/estimator.go +++ b/core/execution/amm/estimator.go @@ -20,12 +20,15 @@ import ( ) type EstimatedBounds struct { - PositionSizeAtUpper num.Decimal PositionSizeAtLower num.Decimal - LossOnCommitmentAtUpper num.Decimal LossOnCommitmentAtLower num.Decimal - LiquidationPriceAtUpper num.Decimal LiquidationPriceAtLower num.Decimal + TooWideLower bool + + PositionSizeAtUpper num.Decimal + LossOnCommitmentAtUpper num.Decimal + LiquidationPriceAtUpper num.Decimal + TooWideUpper bool } func EstimateBounds( @@ -34,37 +37,72 @@ func EstimateBounds( leverageLower, leverageUpper num.Decimal, balance *num.Uint, linearSlippageFactor, initialMargin, - riskFactorShort, riskFactorLong num.Decimal, + riskFactorShort, riskFactorLong, + priceFactor, positionFactor num.Decimal, ) EstimatedBounds { r := EstimatedBounds{} balanceD := balance.ToDecimal() + + oneTick, _ := num.UintFromDecimal(priceFactor) + oneTick = num.Max(num.UintOne(), oneTick) + if lowerPrice != nil { unitLower := LiquidityUnit(sqrter, basePrice, lowerPrice) + avgEntryLower := AverageEntryPrice(sqrter, unitLower, basePrice) riskFactorLower := RiskFactor(leverageLower, riskFactorLong, linearSlippageFactor, initialMargin) lowerPriceD := lowerPrice.ToDecimal() - boundPosLower := PositionAtLowerBound(riskFactorLower, balanceD, lowerPriceD, avgEntryLower) - lossLower := LossOnCommitment(avgEntryLower, lowerPriceD, boundPosLower) - liquidationPriceAtLower := LiquidationPrice(balanceD, lossLower, boundPosLower, lowerPriceD, linearSlippageFactor, riskFactorLong) + boundPosLower := PositionAtLowerBound(riskFactorLower, balanceD, lowerPriceD, avgEntryLower, positionFactor) + + // if the commitment is *so low* that the position at the bound is 0 then we will panic trying to calculate the rest + // and the "too wide" check below will flag it up as an invalid AMM defn + if !boundPosLower.IsZero() { + lossLower := LossOnCommitment(avgEntryLower, lowerPriceD, boundPosLower) - r.PositionSizeAtLower = boundPosLower.Truncate(5) - r.LiquidationPriceAtLower = liquidationPriceAtLower.Truncate(5) - r.LossOnCommitmentAtLower = lossLower.Truncate(5) + liquidationPriceAtLower := LiquidationPrice(balanceD, lossLower, boundPosLower, lowerPriceD, linearSlippageFactor, riskFactorLong) + + r.PositionSizeAtLower = boundPosLower.Mul(positionFactor) + r.LiquidationPriceAtLower = liquidationPriceAtLower + r.LossOnCommitmentAtLower = lossLower + } + + // now lets check that the lower bound is not too wide that the volume is spread too thin + l := unitLower.Mul(boundPosLower).Abs() + + pos := impliedPosition(sqrter.sqrt(num.UintZero().Sub(basePrice, oneTick)), sqrter.sqrt(basePrice), l) + if pos.LessThan(num.DecimalOne()) { + r.TooWideLower = true + } } if upperPrice != nil { unitUpper := LiquidityUnit(sqrter, upperPrice, basePrice) + avgEntryUpper := AverageEntryPrice(sqrter, unitUpper, upperPrice) riskFactorUpper := RiskFactor(leverageUpper, riskFactorShort, linearSlippageFactor, initialMargin) upperPriceD := upperPrice.ToDecimal() - boundPosUpper := PositionAtUpperBound(riskFactorUpper, balanceD, upperPriceD, avgEntryUpper) - lossUpper := LossOnCommitment(avgEntryUpper, upperPriceD, boundPosUpper) - liquidationPriceAtUpper := LiquidationPrice(balanceD, lossUpper, boundPosUpper, upperPriceD, linearSlippageFactor, riskFactorShort) - r.PositionSizeAtUpper = boundPosUpper.Truncate(5) - r.LiquidationPriceAtUpper = liquidationPriceAtUpper.Truncate(5) - r.LossOnCommitmentAtUpper = lossUpper.Truncate(5) + boundPosUpper := PositionAtUpperBound(riskFactorUpper, balanceD, upperPriceD, avgEntryUpper, positionFactor) + + // if the commitment is *so low* that the position at the bound is 0 then we will panic trying to calculate the rest + // and the "too wide" check below will flag it up as an invalid AMM defn + if !boundPosUpper.IsZero() { + lossUpper := LossOnCommitment(avgEntryUpper, upperPriceD, boundPosUpper) + + liquidationPriceAtUpper := LiquidationPrice(balanceD, lossUpper, boundPosUpper, upperPriceD, linearSlippageFactor, riskFactorShort) + r.PositionSizeAtUpper = boundPosUpper.Mul(positionFactor) + r.LiquidationPriceAtUpper = liquidationPriceAtUpper + r.LossOnCommitmentAtUpper = lossUpper + } + + // now lets check that the lower bound is not too wide that the volume is spread too thin + l := unitUpper.Mul(boundPosUpper).Abs() + + pos := impliedPosition(sqrter.sqrt(num.UintZero().Sub(upperPrice, oneTick)), sqrter.sqrt(upperPrice), l) + if pos.LessThan(num.DecimalOne()) { + r.TooWideUpper = true + } } return r @@ -93,33 +131,40 @@ func AverageEntryPrice(sqrter *Sqrter, lu num.Decimal, pu *num.Uint) num.Decimal } // Pvl = rf * b / (pl * (1 - rf) + rf * pa). -func PositionAtLowerBound(rf, b, pl, pa num.Decimal) num.Decimal { +func PositionAtLowerBound(rf, b, pl, pa, positionFactor num.Decimal) num.Decimal { oneSubRf := num.DecimalOne().Sub(rf) rfMulPa := rf.Mul(pa) - return rf.Mul(b).Div( + pv := rf.Mul(b).Div( pl.Mul(oneSubRf).Add(rfMulPa), ) + return pv } // Pvl = -rf * b / (pl * (1 + rf) - rf * pa). -func PositionAtUpperBound(rf, b, pl, pa num.Decimal) num.Decimal { +func PositionAtUpperBound(rf, b, pl, pa, positionFactor num.Decimal) num.Decimal { onePlusRf := num.DecimalOne().Add(rf) rfMulPa := rf.Mul(pa) - return rf.Neg().Mul(b).Div( + pv := rf.Neg().Mul(b).Div( pl.Mul(onePlusRf).Sub(rfMulPa), ) + return pv } -// lc = |pa - pb * pB|. +// lc = |(pa - pb) * pB|. func LossOnCommitment(pa, pb, pB num.Decimal) num.Decimal { - return pa.Sub(pb).Mul(pB).Abs() + res := pa.Sub(pb).Mul(pB).Abs() + return res } // Pliq = (b - lc - Pb * pb) / (|Pb| * (fl + mr) - Pb). func LiquidationPrice(b, lc, pB, pb, fl, mr num.Decimal) num.Decimal { - return b.Sub(lc).Sub(pB.Mul(pb)).Div( - pB.Abs().Mul(fl.Add(mr)).Sub(pB), - ) + // (b - lc - Pb * pb) + numer := b.Sub(lc).Sub(pB.Mul(pb)) + + // (|Pb| * (fl + mr) - Pb) + denom := pB.Abs().Mul(fl.Add(mr)).Sub(pB) + + return num.MaxD(num.DecimalZero(), numer.Div(denom)) } diff --git a/core/execution/amm/estimator_test.go b/core/execution/amm/estimator_test.go index 0ad4f869caf..07d6c6115e4 100644 --- a/core/execution/amm/estimator_test.go +++ b/core/execution/amm/estimator_test.go @@ -60,8 +60,8 @@ func TestEstimateSeparateFunctions(t *testing.T) { upperPriceD := upperPrice.ToDecimal() // test position at bounds - lowerBoundPos := PositionAtLowerBound(riskFactorLower, balance.ToDecimal(), lowerPriceD, avgEntryLower) - upperBoundPos := PositionAtUpperBound(riskFactorUpper, balance.ToDecimal(), upperPriceD, avgEntryUpper) + lowerBoundPos := PositionAtLowerBound(riskFactorLower, balance.ToDecimal(), lowerPriceD, avgEntryLower, num.DecimalOne()) + upperBoundPos := PositionAtUpperBound(riskFactorUpper, balance.ToDecimal(), upperPriceD, avgEntryUpper, num.DecimalOne()) assert.Equal(t, num.DecimalFromFloat(0.437).String(), lowerBoundPos.Round(3).String()) assert.Equal(t, num.DecimalFromFloat(-0.069).String(), upperBoundPos.Round(3).String()) @@ -116,6 +116,8 @@ func TestEstimate(t *testing.T) { initialMargin, riskFactorShort, riskFactorLong, + num.DecimalOne(), + num.DecimalOne(), ) assert.Equal(t, expectedMetrics.PositionSizeAtUpper.String(), metrics.PositionSizeAtUpper.Round(3).String()) @@ -124,6 +126,8 @@ func TestEstimate(t *testing.T) { assert.Equal(t, expectedMetrics.LossOnCommitmentAtLower.String(), metrics.LossOnCommitmentAtLower.Round(3).String()) assert.Equal(t, expectedMetrics.LiquidationPriceAtUpper.String(), metrics.LiquidationPriceAtUpper.Round(3).String()) assert.Equal(t, expectedMetrics.LiquidationPriceAtLower.String(), metrics.LiquidationPriceAtLower.Round(3).String()) + assert.True(t, metrics.TooWideLower) + assert.True(t, metrics.TooWideUpper) }) t.Run("test 0014-NP-VAMM-004", func(t *testing.T) { @@ -155,6 +159,8 @@ func TestEstimate(t *testing.T) { initialMargin, riskFactorShort, riskFactorLong, + num.DecimalOne(), + num.DecimalOne(), ) assert.Equal(t, expectedMetrics.PositionSizeAtUpper.String(), metrics.PositionSizeAtUpper.Round(3).String()) @@ -165,3 +171,66 @@ func TestEstimate(t *testing.T) { assert.Equal(t, expectedMetrics.LiquidationPriceAtLower.String(), metrics.LiquidationPriceAtLower.Round(3).String()) }) } + +func TestEstimatePositionFactor(t *testing.T) { + initialMargin := num.DecimalFromFloat(1.2) + riskFactorShort := num.DecimalFromFloat(0.05529953589167391) + riskFactorLong := num.DecimalFromFloat(0.05529953589167391) + linearSlippageFactor := num.DecimalFromFloat(0.01) + sqrter := NewSqrter() + + lowerPrice := num.MustUintFromString("80000000000000000000", 10) + basePrice := num.MustUintFromString("100000000000000000000", 10) + upperPrice := num.MustUintFromString("120000000000000000000", 10) + leverageUpper := num.DecimalFromFloat(0.5) + leverageLower := num.DecimalFromFloat(0.5) + balance := num.MustUintFromString("390500000000000000000000000", 10) + + expectedMetrics := EstimatedBounds{ + PositionSizeAtUpper: num.DecimalFromFloat(-1559159.284), + PositionSizeAtLower: num.DecimalFromFloat(2304613.63), + } + + metrics := EstimateBounds( + sqrter, + lowerPrice, + basePrice, + upperPrice, + leverageLower, + leverageUpper, + balance, + linearSlippageFactor, + initialMargin, + riskFactorShort, + riskFactorLong, + num.DecimalFromInt64(1000000000000000000), + num.DecimalOne(), + ) + + assert.Equal(t, expectedMetrics.PositionSizeAtUpper.String(), metrics.PositionSizeAtUpper.Round(3).String()) + assert.Equal(t, expectedMetrics.PositionSizeAtLower.String(), metrics.PositionSizeAtLower.Round(3).String()) + assert.False(t, metrics.TooWideLower) + assert.False(t, metrics.TooWideUpper) + + // if commitment is super low then we could panic, so test that we don't + metrics = EstimateBounds( + sqrter, + lowerPrice, + basePrice, + upperPrice, + leverageLower, + leverageUpper, + num.UintOne(), + linearSlippageFactor, + initialMargin, + riskFactorShort, + riskFactorLong, + num.DecimalFromInt64(1000000000000000000), + num.DecimalOne(), + ) + + assert.Equal(t, "0", metrics.PositionSizeAtUpper.Round(3).String()) + assert.Equal(t, "0", metrics.PositionSizeAtLower.Round(3).String()) + assert.True(t, metrics.TooWideLower) + assert.True(t, metrics.TooWideUpper) +} diff --git a/core/execution/amm/mocks/mocks.go b/core/execution/amm/mocks/mocks.go index 4df3c2c5add..3eab847a39e 100644 --- a/core/execution/amm/mocks/mocks.go +++ b/core/execution/amm/mocks/mocks.go @@ -55,6 +55,20 @@ func (mr *MockCollateralMockRecorder) CreatePartyAMMsSubAccounts(arg0, arg1, arg return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreatePartyAMMsSubAccounts", reflect.TypeOf((*MockCollateral)(nil).CreatePartyAMMsSubAccounts), arg0, arg1, arg2, arg3, arg4) } +// GetAllParties mocks base method. +func (m *MockCollateral) GetAllParties() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllParties") + ret0, _ := ret[0].([]string) + return ret0 +} + +// GetAllParties indicates an expected call of GetAllParties. +func (mr *MockCollateralMockRecorder) GetAllParties() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllParties", reflect.TypeOf((*MockCollateral)(nil).GetAllParties)) +} + // GetAssetQuantum mocks base method. func (m *MockCollateral) GetAssetQuantum(arg0 string) (decimal.Decimal, error) { m.ctrl.T.Helper() diff --git a/core/execution/amm/pool.go b/core/execution/amm/pool.go index 2b031df9911..e4c89e673e4 100644 --- a/core/execution/amm/pool.go +++ b/core/execution/amm/pool.go @@ -42,6 +42,9 @@ type curve struct { // note that this equals Vega's position at the boundary only in the lower curve, since Vega position == curve-position // in the upper curve Vega's position == 0 => position of `pv`` in curve-position, Vega's position pv => 0 in curve-position pv num.Decimal + + lDivSqrtPu num.Decimal + sqrtHigh num.Decimal } func (c *curve) volumeBetweenPrices(sqrt sqrtFn, st, nd *num.Uint) uint64 { @@ -56,8 +59,8 @@ func (c *curve) volumeBetweenPrices(sqrt sqrtFn, st, nd *num.Uint) uint64 { return 0 } - stP := impliedPosition(sqrt(st), sqrt(c.high), c.l) - ndP := impliedPosition(sqrt(nd), sqrt(c.high), c.l) + stP := impliedPosition(sqrt(st), c.sqrtHigh, c.l) + ndP := impliedPosition(sqrt(nd), c.sqrtHigh, c.l) // abs(P(st) - P(nd)) volume := stP.Sub(ndP).Abs() @@ -67,7 +70,7 @@ func (c *curve) volumeBetweenPrices(sqrt sqrtFn, st, nd *num.Uint) uint64 { // positionAtPrice returns the position of the AMM if its fair-price were the given price. This // will be signed for long/short as usual. func (c *curve) positionAtPrice(sqrt sqrtFn, price *num.Uint) int64 { - pos := impliedPosition(sqrt(price), sqrt(c.high), c.l) + pos := impliedPosition(sqrt(price), c.sqrtHigh, c.l) if c.isLower { return pos.IntPart() } @@ -111,6 +114,8 @@ type Pool struct { maxCalculationLevels *num.Uint // maximum number of price levels the AMM will be expanded into oneTick *num.Uint // one price tick + + fpCache map[int64]*num.Uint } func NewPool( @@ -145,9 +150,10 @@ func NewPool( position: position, priceFactor: priceFactor, positionFactor: positionFactor, - oneTick: oneTick, + oneTick: num.Max(num.UintOne(), oneTick), status: types.AMMPoolStatusActive, maxCalculationLevels: maxCalculationLevels, + fpCache: map[int64]*num.Uint{}, } err := pool.setCurves(rf, sf, linearSlippage) if err != nil { @@ -164,6 +170,7 @@ func NewPoolFromProto( state *snapshotpb.PoolMapEntry_Pool, party string, priceFactor num.Decimal, + positionFactor num.Decimal, ) (*Pool, error) { oneTick, _ := num.UintFromDecimal(priceFactor) @@ -232,17 +239,18 @@ func NewPoolFromProto( LeverageAtLowerBound: lowerLeverage, LeverageAtUpperBound: upperLeverage, }, - owner: party, - market: state.Market, - asset: state.Asset, - sqrt: sqrt, - collateral: collateral, - position: position, - lower: lowerCu, - upper: upperCu, - priceFactor: priceFactor, - oneTick: oneTick, - status: state.Status, + owner: party, + market: state.Market, + asset: state.Asset, + sqrt: sqrt, + collateral: collateral, + position: position, + lower: lowerCu, + upper: upperCu, + priceFactor: priceFactor, + positionFactor: positionFactor, + oneTick: num.Max(num.UintOne(), oneTick), + status: state.Status, }, nil } @@ -266,12 +274,18 @@ func NewCurveFromProto(c *snapshotpb.PoolMapEntry_Curve) (*curve, error) { if overflow { return nil, fmt.Errorf("failed to convert string to Uint: %s", c.Low) } + + sqrtHigh := num.UintOne().Sqrt(high) + lDivSqrtPu := l.Div(sqrtHigh) + return &curve{ - l: l, - high: high, - low: low, - empty: c.Empty, - pv: pv, + l: l, + high: high, + low: low, + empty: c.Empty, + pv: pv, + sqrtHigh: sqrtHigh, + lDivSqrtPu: lDivSqrtPu, }, nil } @@ -353,6 +367,7 @@ func (p *Pool) Update( sqrt: p.sqrt, oneTick: p.oneTick, maxCalculationLevels: p.maxCalculationLevels, + fpCache: map[int64]*num.Uint{}, } if err := updated.setCurves(rf, sf, linearSlippage); err != nil { return nil, err @@ -441,14 +456,20 @@ func generateCurve( // now we scale theoretical position by position factor so that is it feeds through into all subsequent equations pv = pv.Mul(positionFactor) + l := pv.Mul(lu) + + sqrtHigh := sqrt(high) + lDivSqrtPu := l.Div(sqrtHigh) // and finally calculate L = pv * Lu return &curve{ - l: pv.Mul(lu), - low: low, - high: high, - pv: pv, - isLower: isLower, + l: l, + low: low, + high: high, + pv: pv, + isLower: isLower, + lDivSqrtPu: lDivSqrtPu, + sqrtHigh: sqrtHigh, } } @@ -586,39 +607,52 @@ func (p *Pool) TradableVolumeInRange(side types.Side, price1 *num.Uint, price2 * st, nd = nd, st } - fp := p.fairPrice() + // map the given st/nd prices into positions, then the difference is the volume + asPosition := func(price *num.Uint) int64 { + switch { + case price.GT(p.lower.high): + // in upper curve + if !p.upper.empty { + return p.upper.positionAtPrice(p.sqrt, num.Min(p.upper.high, price)) + } + case price.LT(p.lower.high): + // in lower curve + if !p.lower.empty { + return p.lower.positionAtPrice(p.sqrt, num.Max(p.lower.low, price)) + } + } + return 0 + } + + stP := asPosition(st) + ndP := asPosition(nd) + if side == types.SideSell { - // want all buy volume so everything below fair price - nd = num.Min(fp, nd) + // want all buy volume so everything below fair price, where the AMM is long + ndP = num.MaxV(pos, ndP) } if side == types.SideBuy { - // want all sell volume so everything above fair price - st = num.Max(fp, st) + // want all sell volume so everything above fair price, where the AMM is short + stP = num.MinV(pos, stP) } - var other *curve - var volume uint64 - // get the curve based on the pool's current position, if the position is zero we take the curve the trade will put us in - // e.g trading with an incoming buy order will make the pool short, so we take the upper curve. - if pos < 0 || (pos == 0 && side == types.SideBuy) { - volume = p.upper.volumeBetweenPrices(p.sqrt, st, nd) - other = p.lower - } else { - volume = p.lower.volumeBetweenPrices(p.sqrt, st, nd) - other = p.upper + if !p.closing() { + return uint64(stP - ndP) } - if p.closing() { - return num.MinV(volume, uint64(num.AbsV(pos))) + if pos > 0 { + // if closing and long, we have no volume at short prices, so cap range to > 0 + stP = num.MaxV(0, stP) + ndP = num.MaxV(0, ndP) } - // if the position is non-zero, the incoming order could push us across to the other curve - // so we need to check for volume there too - if pos != 0 { - volume += other.volumeBetweenPrices(p.sqrt, st, nd) + if pos < 0 { + // if closing and short, we have no volume at long prices, so cap range to < 0 + stP = num.MinV(0, stP) + ndP = num.MinV(0, ndP) } - return volume + return num.MinV(uint64(stP-ndP), uint64(num.AbsV(pos))) } // getBalance returns the total balance of the pool i.e it's general account + it's margin account. @@ -694,6 +728,10 @@ func (p *Pool) fairPrice() *num.Uint { return p.lower.high.Clone() } + if fp, ok := p.fpCache[pos]; ok { + return fp.Clone() + } + cu := p.lower pv := num.DecimalFromInt64(pos) if pos < 0 { @@ -706,10 +744,8 @@ func (p *Pool) fairPrice() *num.Uint { panic("should not be calculating fair-price on empty-curve side") } - l := cu.l - // pv * sqrt(pu) * (1/L) + 1 - denom := pv.Mul(p.sqrt(cu.high)).Div(l).Add(num.DecimalOne()) + denom := pv.Mul(cu.sqrtHigh).Div(cu.l).Add(num.DecimalOne()) // sqrt(fp) = sqrt(pu) / denom sqrtPf := p.sqrt(cu.high).Div(denom) @@ -727,12 +763,16 @@ func (p *Pool) fairPrice() *num.Uint { } fairPrice, _ := num.UintFromDecimal(fp) + + p.fpCache = map[int64]*num.Uint{ + pos: fairPrice.Clone(), + } return fairPrice } // virtualBalancesShort returns the pools x, y balances when the pool has a negative position // -// x = P + Pv + L / sqrt(pl) +// x = P + Pv + L / sqrt(pu) // y = L * sqrt(fair-price). func (p *Pool) virtualBalancesShort(pos int64, fp *num.Uint) (num.Decimal, num.Decimal) { cu := p.upper @@ -748,10 +788,10 @@ func (p *Pool) virtualBalancesShort(pos int64, fp *num.Uint) (num.Decimal, num.D // Pv term2x := cu.pv - // L / sqrt(pl) - term3x := cu.l.Div(p.sqrt(cu.high)) + // L / sqrt(pu) + term3x := cu.lDivSqrtPu - // x = P + (cc * rf / pu) + (L / sqrt(pl)) + // x = P + (cc * rf / pu) + (L / sqrt(pu)) x := term2x.Add(term3x).Add(term1x) // now lets get y @@ -777,7 +817,7 @@ func (p *Pool) virtualBalancesLong(pos int64, fp *num.Uint) (num.Decimal, num.De term1x := num.DecimalFromInt64(pos) // L / sqrt(pu) - term2x := cu.l.Div(p.sqrt(cu.high)) + term2x := cu.lDivSqrtPu // x = P + (L / sqrt(pu)) x := term1x.Add(term2x) diff --git a/core/execution/amm/pool_test.go b/core/execution/amm/pool_test.go index 9dcc80a62cd..0444f469e83 100644 --- a/core/execution/amm/pool_test.go +++ b/core/execution/amm/pool_test.go @@ -38,6 +38,7 @@ func TestAMMPool(t *testing.T) { t.Run("test pool logic with position factor", testPoolPositionFactor) t.Run("test one sided pool", testOneSidedPool) t.Run("test near zero volume curve triggers and error", testNearZeroCurveErrors) + t.Run("test volume between prices when closing", testTradeableVolumeInRangeClosing) } func testTradeableVolumeInRange(t *testing.T) { @@ -85,7 +86,7 @@ func testTradeableVolumeInRange(t *testing.T) { price1: num.NewUint(500), price2: num.NewUint(3500), side: types.SideBuy, - expectedVolume: 1337, + expectedVolume: 1335, position: 700, // position at full lower boundary, incoming is by so whole volume of both curves is available }, { @@ -101,7 +102,7 @@ func testTradeableVolumeInRange(t *testing.T) { price1: num.NewUint(500), price2: num.NewUint(3500), side: types.SideBuy, - expectedVolume: 986, + expectedVolume: 985, position: 350, }, { @@ -109,14 +110,129 @@ func testTradeableVolumeInRange(t *testing.T) { price1: num.NewUint(500), price2: num.NewUint(3500), side: types.SideSell, - expectedVolume: 1053, + expectedVolume: 1052, position: -350, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ensurePositionN(t, p.pos, tt.position, num.UintZero(), 2) + ensurePositionN(t, p.pos, tt.position, num.UintZero(), 1) + volume := p.pool.TradableVolumeInRange(tt.side, tt.price1, tt.price2) + assert.Equal(t, int(tt.expectedVolume), int(volume)) + }) + } +} + +func testTradeableVolumeInRangeClosing(t *testing.T) { + p := newTestPool(t) + defer p.ctrl.Finish() + + // pool is reducing its + p.pool.status = types.AMMPoolStatusReduceOnly + + tests := []struct { + name string + price1 *num.Uint + price2 *num.Uint + position int64 + side types.Side + expectedVolume uint64 + nposcalls int + }{ + { + name: "0 position, 0 buy volume", + price1: num.NewUint(1800), + price2: num.NewUint(2200), + side: types.SideBuy, + expectedVolume: 0, + nposcalls: 1, + }, + { + name: "0 position, 0 sell volume", + price1: num.NewUint(1800), + price2: num.NewUint(2200), + side: types.SideSell, + expectedVolume: 0, + nposcalls: 1, + }, + { + name: "long position, 0 volume for incoming SELL", + price1: num.NewUint(1800), + price2: num.NewUint(2200), + side: types.SideSell, + position: 10, + expectedVolume: 0, + nposcalls: 1, + }, + { + name: "long position, 10 volume for incoming BUY", + price1: num.NewUint(1800), + price2: num.NewUint(2200), + side: types.SideBuy, + position: 10, + expectedVolume: 10, + nposcalls: 2, + }, + { + name: "short position, 0 volume for incoming BUY", + price1: num.NewUint(1800), + price2: num.NewUint(2200), + side: types.SideBuy, + position: -10, + expectedVolume: 0, + nposcalls: 1, + }, + { + name: "short position, 10 volume for incoming SELL", + price1: num.NewUint(1800), + price2: num.NewUint(2200), + side: types.SideSell, + position: -10, + expectedVolume: 10, + nposcalls: 2, + }, + { + name: "asking for SELL volume but for prices outside of price ranges", + price1: num.NewUint(2000), + price2: num.NewUint(2200), + side: types.SideBuy, + position: 10, + expectedVolume: 0, + nposcalls: 2, + }, + { + name: "asking for BUY volume but for prices outside of price ranges", + price1: num.NewUint(1800), + price2: num.NewUint(1850), + side: types.SideSell, + position: -10, + expectedVolume: 0, + nposcalls: 2, + }, + { + name: "asking for partial closing volume when long", + price1: num.NewUint(1800), + price2: num.NewUint(1850), + side: types.SideBuy, + position: 702, + expectedVolume: 186, + nposcalls: 2, + }, + { + name: "asking for partial closing volume when short", + price1: num.NewUint(2100), + price2: num.NewUint(2150), + side: types.SideSell, + position: -635, + expectedVolume: 155, + nposcalls: 2, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ensurePositionN(t, p.pos, tt.position, num.UintZero(), tt.nposcalls) volume := p.pool.TradableVolumeInRange(tt.side, tt.price1, tt.price2) assert.Equal(t, int(tt.expectedVolume), int(volume)) }) @@ -149,7 +265,7 @@ func TestTradeableVolumeWhenAtBoundary(t *testing.T) { defer p.ctrl.Finish() // when position is zero fair-price should be the base - ensurePositionN(t, p.pos, 0, num.UintZero(), 3) + ensurePositionN(t, p.pos, 0, num.UintZero(), 2) fp := p.pool.BestPrice(nil) assert.Equal(t, "6765400000000000000000", fp.String()) @@ -160,7 +276,7 @@ func TestTradeableVolumeWhenAtBoundary(t *testing.T) { assert.Equal(t, fullLong, int(volume)) // now lets pretend the AMM has fully traded out in that direction, best price will be near but not quite the lower bound - ensurePositionN(t, p.pos, int64(fullLong), num.UintZero(), 3) + ensurePositionN(t, p.pos, int64(fullLong), num.UintZero(), 2) fp = p.pool.BestPrice(nil) assert.Equal(t, "6712721893865935337785", fp.String()) assert.True(t, fp.GTE(num.MustUintFromString("6712720000000000000000", 10))) @@ -174,12 +290,12 @@ func testPoolPositionFactor(t *testing.T) { p := newTestPoolWithPositionFactor(t, num.DecimalFromInt64(1000)) defer p.ctrl.Finish() - ensurePositionN(t, p.pos, 0, num.UintZero(), 2) + ensurePositionN(t, p.pos, 0, num.UintZero(), 1) volume := p.pool.TradableVolumeInRange(types.SideBuy, num.NewUint(2000), num.NewUint(2200)) // with position factot of 1 the volume is 635 assert.Equal(t, int(635395), int(volume)) - ensurePositionN(t, p.pos, 0, num.UintZero(), 2) + ensurePositionN(t, p.pos, 0, num.UintZero(), 1) volume = p.pool.TradableVolumeInRange(types.SideSell, num.NewUint(1800), num.NewUint(2000)) // with position factot of 1 the volume is 702 assert.Equal(t, int(702411), int(volume)) @@ -256,18 +372,18 @@ func testOneSidedPool(t *testing.T) { defer p.ctrl.Finish() // side with liquidity returns volume - ensurePositionN(t, p.pos, 0, num.UintZero(), 2) + ensurePositionN(t, p.pos, 0, num.UintZero(), 1) volume := p.pool.TradableVolumeInRange(types.SideBuy, num.NewUint(2000), num.NewUint(2200)) assert.Equal(t, int(635), int(volume)) // empty side returns no volume - ensurePositionN(t, p.pos, 0, num.UintZero(), 2) + ensurePositionN(t, p.pos, 0, num.UintZero(), 1) volume = p.pool.TradableVolumeInRange(types.SideSell, num.NewUint(1800), num.NewUint(2000)) assert.Equal(t, int(0), int(volume)) // pool with short position and incoming sell only reports volume up to base // empty side returns no volume - ensurePositionN(t, p.pos, -10, num.UintZero(), 2) + ensurePositionN(t, p.pos, -10, num.UintZero(), 1) volume = p.pool.TradableVolumeInRange(types.SideSell, num.NewUint(1800), num.NewUint(2200)) assert.Equal(t, int(10), int(volume)) @@ -440,22 +556,22 @@ func TestNotebook(t *testing.T) { pos := int64(0) - ensurePositionN(t, p.pos, pos, num.UintZero(), 2) + ensurePositionN(t, p.pos, pos, num.UintZero(), 1) volume := p.pool.TradableVolumeInRange(types.SideSell, base, low) assert.Equal(t, int(702), int(volume)) - ensurePositionN(t, p.pos, pos, num.UintZero(), 2) + ensurePositionN(t, p.pos, pos, num.UintZero(), 1) volume = p.pool.TradableVolumeInRange(types.SideBuy, up, base) assert.Equal(t, int(635), int(volume)) lowmid := num.NewUint(1900) upmid := num.NewUint(2100) - ensurePositionN(t, p.pos, pos, num.UintZero(), 2) + ensurePositionN(t, p.pos, pos, num.UintZero(), 1) volume = p.pool.TradableVolumeInRange(types.SideSell, low, lowmid) assert.Equal(t, int(365), int(volume)) - ensurePositionN(t, p.pos, pos, num.UintZero(), 2) + ensurePositionN(t, p.pos, pos, num.UintZero(), 1) volume = p.pool.TradableVolumeInRange(types.SideBuy, upmid, up) assert.Equal(t, int(306), int(volume)) diff --git a/core/execution/amm/shape.go b/core/execution/amm/shape.go index de8c6070944..9cfc6fcb07d 100644 --- a/core/execution/amm/shape.go +++ b/core/execution/amm/shape.go @@ -132,7 +132,7 @@ func (sm *shapeMaker) calculateBoundaryOrders() (*types.Order, *types.Order) { bnd1 := sm.makeBoundaryOrder(st, sm.from) if sm.log.IsDebug() { - sm.log.Debug("creating boundary order", + sm.log.Debug("created boundary order", logging.String("price", bnd1.Price.String()), logging.String("side", bnd1.Side.String()), logging.Uint64("volume", bnd1.Size), @@ -143,7 +143,7 @@ func (sm *shapeMaker) calculateBoundaryOrders() (*types.Order, *types.Order) { bnd2 := sm.makeBoundaryOrder(sm.to, nd) if sm.log.IsDebug() { - sm.log.Debug("creating boundary order", + sm.log.Debug("created boundary order", logging.String("price", bnd2.Price.String()), logging.String("side", bnd2.Side.String()), logging.Uint64("volume", bnd2.Size), @@ -294,10 +294,25 @@ func (sm *shapeMaker) adjustRegion() bool { // only orders between fair-price -> base lower = sm.fairPrice.Clone() upper = sm.pool.lower.high.Clone() + + // if the AMM is super close to closing its position the delta between fair-price -> base + // could be very small, but the upshot is we know it will only be one order and can calculate + // directly + if num.UintZero().Sub(upper, lower).LTE(sm.oneTick) { + price := num.UintZero().Sub(sm.pool.lower.high, sm.oneTick) + sm.addOrder(uint64(sm.pos), price, types.SideSell) + return false + } } else { // only orders between base -> fair-price upper = sm.fairPrice.Clone() lower = sm.pool.lower.high.Clone() + + if num.UintZero().Sub(upper, lower).LTE(sm.oneTick) { + price := num.UintZero().Add(sm.pool.lower.high, sm.oneTick) + sm.addOrder(uint64(-sm.pos), price, types.SideBuy) + return false + } } } @@ -313,7 +328,6 @@ func (sm *shapeMaker) adjustRegion() bool { // cap the range to the pool's bounds, there will be no orders outside of this from := num.Max(sm.from, lower) to := num.Min(sm.to, upper) - switch { case sm.from.GT(sm.fairPrice): // if we are expanding entirely in the sell range to calculate the order at price `from` @@ -340,7 +354,7 @@ func (sm *shapeMaker) adjustRegion() bool { func (sm *shapeMaker) makeShape() ([]*types.Order, []*types.Order) { if !sm.adjustRegion() { // if there is no overlap between the input region and the AMM's bounds then there are no orders - return nil, nil + return sm.buys, sm.sells } // create accurate orders at the boundary of the adjusted region (even if we are going to make approximate internal steps) diff --git a/core/execution/amm/shape_test.go b/core/execution/amm/shape_test.go index 89fc14856fb..bd4c381c1dd 100644 --- a/core/execution/amm/shape_test.go +++ b/core/execution/amm/shape_test.go @@ -35,6 +35,7 @@ func TestOrderbookShape(t *testing.T) { t.Run("test orderbook shape AMM reduce only", testOrderbookShapeReduceOnly) t.Run("test orderbook shape boundary order when approx", testOrderbookShapeBoundaryOrder) t.Run("test orderbook shape region not divisible by tick", testOrderbookSubTick) + t.Run("test orderbook shape closing pool close to base", testClosingCloseToBase) } func testOrderbookShapeZeroPosition(t *testing.T) { @@ -359,3 +360,59 @@ func testOrderbookSubTick(t *testing.T) { assert.Equal(t, 0, len(sells)) } + +func testClosingCloseToBase(t *testing.T) { + p := newTestPoolWithSubmission(t, num.DecimalFromFloat(1), num.DecimalFromFloat(100), + &types.SubmitAMM{ + CommitmentAmount: num.NewUint(10000000), + Parameters: &types.ConcentratedLiquidityParameters{ + LowerBound: num.NewUint(10), + Base: num.NewUint(15), + UpperBound: num.NewUint(20), + }, + }, + ) + + defer p.ctrl.Finish() + + // its reducing + p.pool.status = types.AMMPoolStatusReduceOnly + + // and it is long one + position := int64(1) + ensurePositionN(t, p.pos, position, num.UintZero(), 2) + + // now pretend we are in auction and we have a sell order at 1000, so we need to expand the crossed + // region of 1000 -> 1383 + from := num.NewUint(1000) + to := num.NewUint(2000) + buys, sells := p.pool.OrderbookShape(from, to, nil) + + // should have one sell of volume 1 + assert.Equal(t, 0, len(buys)) + assert.Equal(t, 1, len(sells)) + assert.Equal(t, 1, int(sells[0].Size)) + assert.Equal(t, "14", sells[0].OriginalPrice.String()) + + // and it is short one + position = int64(-1) + ensurePositionN(t, p.pos, position, num.UintZero(), 2) + + buys, sells = p.pool.OrderbookShape(from, to, nil) + + // should have one sell of volume 1 + assert.Equal(t, 1, len(buys)) + assert.Equal(t, 0, len(sells)) + assert.Equal(t, 1, int(buys[0].Size)) + assert.Equal(t, "16", buys[0].OriginalPrice.String()) + + // no position + position = int64(0) + ensurePositionN(t, p.pos, position, num.UintZero(), 2) + + buys, sells = p.pool.OrderbookShape(from, to, nil) + + // should have one sell of volume 1 + assert.Equal(t, 0, len(buys)) + assert.Equal(t, 0, len(sells)) +} diff --git a/core/execution/common/errors.go b/core/execution/common/errors.go index 4c6aaeb0ccc..929501435f9 100644 --- a/core/execution/common/errors.go +++ b/core/execution/common/errors.go @@ -90,4 +90,5 @@ var ( ErrIsolatedMarginFullyCollateralised = errors.New("isolated margin not permitted on fully collateralised markets") // ErrSettlementDataOutOfRange is returned when a capped future receives settlement data that is outside of the acceptable range (either > max price, or neither 0 nor max for binary settlements). ErrSettlementDataOutOfRange = errors.New("settlement data is outside of the price cap") + ErrAMMBoundsOutsidePriceCap = errors.New("an AMM bound is outside of the price cap") ) diff --git a/core/execution/common/interfaces.go b/core/execution/common/interfaces.go index 7ac1b714e3a..a0f349363e0 100644 --- a/core/execution/common/interfaces.go +++ b/core/execution/common/interfaces.go @@ -195,6 +195,7 @@ type Collateral interface { GetOrCreateLiquidityFeesBonusDistributionAccount(ctx context.Context, marketID, asset string) (*types.Account, error) CheckOrderSpam(party, market string, assets []string) error CheckOrderSpamAllMarkets(party string) error + GetAllParties() []string // amm stuff SubAccountClosed(ctx context.Context, party, subAccount, asset, market string) ([]*types.LedgerMovement, error) @@ -351,6 +352,7 @@ type CommonMarket interface { GetMarketData() types.MarketData StartOpeningAuction(context.Context) error GetEquityShares() *EquityShares + GetEquitySharesForParty(partyID string) num.Decimal IntoType() types.Market OnEpochEvent(ctx context.Context, epoch types.Epoch) OnEpochRestore(ctx context.Context, epoch types.Epoch) @@ -374,6 +376,8 @@ type CommonMarket interface { OnMarketProbabilityOfTradingTauScalingUpdate(context.Context, num.Decimal) OnMarketValueWindowLengthUpdate(time.Duration) OnFeeFactorsInfrastructureFeeUpdate(context.Context, num.Decimal) + OnFeeFactorsTreasuryFeeUpdate(context.Context, num.Decimal) + OnFeeFactorsBuyBackFeeUpdate(context.Context, num.Decimal) OnFeeFactorsMakerFeeUpdate(context.Context, num.Decimal) OnMarkPriceUpdateMaximumFrequency(context.Context, time.Duration) OnMarketAuctionMinimumDurationUpdate(context.Context, time.Duration) @@ -413,6 +417,7 @@ type CommonMarket interface { type AccountBalanceChecker interface { GetAvailableBalance(party string) (*num.Uint, error) + GetAllStakingParties() []string } type Teams interface { diff --git a/core/execution/common/liquidity_provision_test.go b/core/execution/common/liquidity_provision_test.go index 779311dfc72..2349dfa782c 100644 --- a/core/execution/common/liquidity_provision_test.go +++ b/core/execution/common/liquidity_provision_test.go @@ -83,7 +83,7 @@ func newMarketLiquidity(t *testing.T) *marketLiquidityTest { teams := mocks.NewMockTeams(ctrl) bc := mocks.NewMockAccountBalanceChecker(ctrl) - marketTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker) + marketTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine) epochEngine.NotifyOnEpoch(marketTracker.OnEpochEvent, marketTracker.OnEpochRestore) amm := ammcmocks.NewMockAMM(ctrl) diff --git a/core/execution/common/market_activity_tracker.go b/core/execution/common/market_activity_tracker.go index c55337681de..3d43ec0f738 100644 --- a/core/execution/common/market_activity_tracker.go +++ b/core/execution/common/market_activity_tracker.go @@ -47,6 +47,11 @@ var ( dScalingFactor = num.DecimalFromInt64(scalingFactor) ) +type QuantumGetter interface { + GetAssetQuantum(asset string) (num.Decimal, error) + GetAllParties() []string +} + type twPosition struct { position uint64 // abs last recorded position t time.Time // time of last recorded position @@ -54,7 +59,8 @@ type twPosition struct { } type twNotional struct { - notional *num.Uint // last position's price + price *num.Uint // last position's price + notional *num.Uint // last position's notional value t time.Time // time of last recorded notional position currentEpochTWNotional *num.Uint // current epoch's running time-weighted notional position } @@ -67,6 +73,9 @@ type marketTracker struct { lpFees map[string]*num.Uint infraFees map[string]*num.Uint lpPaidFees map[string]*num.Uint + buybackFeesPaid map[string]*num.Uint + treasuryFeesPaid map[string]*num.Uint + markPrice *num.Uint totalMakerFeesReceived *num.Uint totalMakerFeesPaid *num.Uint @@ -105,6 +114,7 @@ type MarketActivityTracker struct { teams Teams balanceChecker AccountBalanceChecker eligibilityChecker EligibilityChecker + collateral QuantumGetter currentEpoch uint64 epochStartTime time.Time @@ -114,13 +124,15 @@ type MarketActivityTracker struct { partyTakerNotionalVolume map[string]*num.Uint marketToPartyTakerNotionalVolume map[string]map[string]*num.Uint takerFeesPaidInEpoch []map[string]map[string]map[string]*num.Uint + // maps game id to eligible parties over time window + eligibilityInEpoch map[string][]map[string]struct{} ss *snapshotState broker Broker } // NewMarketActivityTracker instantiates the fees tracker. -func NewMarketActivityTracker(log *logging.Logger, teams Teams, balanceChecker AccountBalanceChecker, broker Broker) *MarketActivityTracker { +func NewMarketActivityTracker(log *logging.Logger, teams Teams, balanceChecker AccountBalanceChecker, broker Broker, collateral QuantumGetter) *MarketActivityTracker { mat := &MarketActivityTracker{ log: log, balanceChecker: balanceChecker, @@ -131,7 +143,9 @@ func NewMarketActivityTracker(log *logging.Logger, teams Teams, balanceChecker A marketToPartyTakerNotionalVolume: map[string]map[string]*num.Uint{}, ss: &snapshotState{}, takerFeesPaidInEpoch: []map[string]map[string]map[string]*num.Uint{}, + eligibilityInEpoch: map[string][]map[string]struct{}{}, broker: broker, + collateral: collateral, } return mat @@ -187,6 +201,8 @@ func (mat *MarketActivityTracker) MarketProposed(asset, marketID, proposer strin lpFees: map[string]*num.Uint{}, infraFees: map[string]*num.Uint{}, lpPaidFees: map[string]*num.Uint{}, + buybackFeesPaid: map[string]*num.Uint{}, + treasuryFeesPaid: map[string]*num.Uint{}, totalMakerFeesReceived: num.UintZero(), totalMakerFeesPaid: num.UintZero(), totalLpFees: num.UintZero(), @@ -215,6 +231,30 @@ func (mat *MarketActivityTracker) MarketProposed(asset, marketID, proposer strin } } +// UpdateMarkPrice is called for a futures market when the mark price is recalculated. +func (mat *MarketActivityTracker) UpdateMarkPrice(asset, market string, markPrice *num.Uint) { + if amt, ok := mat.assetToMarketTrackers[asset]; ok { + if mt, ok := amt[market]; ok { + mt.markPrice = markPrice.Clone() + } + } +} + +// RestoreMarkPrice is called when a market is loaded from a snapshot and will set the price of the notional to +// the mark price is none is set (for migration). +func (mat *MarketActivityTracker) RestoreMarkPrice(asset, market string, markPrice *num.Uint) { + if amt, ok := mat.assetToMarketTrackers[asset]; ok { + if mt, ok := amt[market]; ok { + mt.markPrice = markPrice.Clone() + for _, twn := range mt.twNotional { + if twn.price == nil { + twn.price = markPrice.Clone() + } + } + } + } +} + func (mat *MarketActivityTracker) PublishGameMetric(ctx context.Context, dispatchStrategy []*vega.DispatchStrategy, now time.Time) { m := map[string]map[string]map[string]*num.Uint{} @@ -230,6 +270,9 @@ func (mat *MarketActivityTracker) PublishGameMetric(ctx context.Context, dispatc } } mat.takerFeesPaidInEpoch = append(mat.takerFeesPaidInEpoch, m) + for ds := range mat.eligibilityInEpoch { + mat.eligibilityInEpoch[ds] = append(mat.eligibilityInEpoch[ds], map[string]struct{}{}) + } for _, ds := range dispatchStrategy { if ds.Metric == vega.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING || ds.Metric == vega.DispatchMetric_DISPATCH_METRIC_LP_FEES_RECEIVED || @@ -255,6 +298,9 @@ func (mat *MarketActivityTracker) PublishGameMetric(ctx context.Context, dispatc } mat.takerFeesPaidInEpoch = mat.takerFeesPaidInEpoch[:len(mat.takerFeesPaidInEpoch)-1] mat.partyContributionCache = map[string][]*types.PartyContributionScore{} + for ds := range mat.eligibilityInEpoch { + mat.eligibilityInEpoch[ds] = mat.eligibilityInEpoch[ds][:len(mat.eligibilityInEpoch)-1] + } } func (mat *MarketActivityTracker) publishMetricForDispatchStrategy(ctx context.Context, ds *vega.DispatchStrategy, now time.Time) { @@ -467,7 +513,7 @@ func (mat *MarketActivityTracker) RemoveMarket(asset, marketID string) { func (mt *marketTracker) aggregatedFees() map[string]*num.Uint { totalFees := map[string]*num.Uint{} - fees := []map[string]*num.Uint{mt.infraFees, mt.lpPaidFees, mt.makerFeesPaid} + fees := []map[string]*num.Uint{mt.infraFees, mt.lpPaidFees, mt.makerFeesPaid, mt.buybackFeesPaid, mt.treasuryFeesPaid} for _, fee := range fees { for party, paid := range fee { if _, ok := totalFees[party]; !ok { @@ -503,6 +549,9 @@ func (mat *MarketActivityTracker) OnEpochEvent(ctx context.Context, epoch types. mat.takerFeesPaidInEpoch = mat.takerFeesPaidInEpoch[1:] } mat.takerFeesPaidInEpoch = append(mat.takerFeesPaidInEpoch, m) + for ds := range mat.eligibilityInEpoch { + mat.eligibilityInEpoch[ds] = append(mat.eligibilityInEpoch[ds], map[string]struct{}{}) + } } mat.currentEpoch = epoch.Seq } @@ -517,6 +566,63 @@ func (mat *MarketActivityTracker) clearDeletedMarkets() { } } +func (mat *MarketActivityTracker) CalculateTotalMakerContributionInQuantum(windowSize int) (map[string]*num.Uint, map[string]num.Decimal) { + m := map[string]*num.Uint{} + total := num.UintZero() + for ast, trackers := range mat.assetToMarketTrackers { + quantum, err := mat.collateral.GetAssetQuantum(ast) + if err != nil { + continue + } + for _, trckr := range trackers { + for i := 0; i < windowSize; i++ { + idx := len(trckr.epochMakerFeesReceived) - i - 1 + if idx < 0 { + break + } + partyFees := trckr.epochMakerFeesReceived[len(trckr.epochMakerFeesReceived)-i-1] + for party, fees := range partyFees { + if _, ok := m[party]; !ok { + m[party] = num.UintZero() + } + feesInQunatum, overflow := num.UintFromDecimal(fees.ToDecimal().Div(quantum)) + if overflow { + continue + } + m[party].AddSum(feesInQunatum) + total.AddSum(feesInQunatum) + } + } + } + } + if total.IsZero() { + return m, map[string]decimal.Decimal{} + } + totalFrac := num.DecimalZero() + fractions := []*types.PartyContributionScore{} + for p, f := range m { + frac := f.ToDecimal().Div(total.ToDecimal()) + fractions = append(fractions, &types.PartyContributionScore{Party: p, Score: frac}) + totalFrac = totalFrac.Add(frac) + } + capAtOne(fractions, totalFrac) + fracMap := make(map[string]num.Decimal, len(fractions)) + for _, partyFraction := range fractions { + fracMap[partyFraction.Party] = partyFraction.Score + } + return m, fracMap +} + +func capAtOne(partyFractions []*types.PartyContributionScore, total num.Decimal) { + if total.LessThanOrEqual(num.DecimalOne()) { + return + } + + sort.SliceStable(partyFractions, func(i, j int) bool { return partyFractions[i].Score.GreaterThan(partyFractions[j].Score) }) + delta := total.Sub(num.DecimalFromInt64(1)) + partyFractions[0].Score = num.MaxD(num.DecimalZero(), partyFractions[0].Score.Sub(delta)) +} + func (mt *marketTracker) calcFeesAtMilestone() { mt.epochMakerFeesReceived = append(mt.epochMakerFeesReceived, mt.makerFeesReceived) mt.epochMakerFeesPaid = append(mt.epochMakerFeesPaid, mt.makerFeesPaid) @@ -544,6 +650,8 @@ func (mt *marketTracker) clearFeeActivity() { mt.lpFees = map[string]*num.Uint{} mt.infraFees = map[string]*num.Uint{} mt.lpPaidFees = map[string]*num.Uint{} + mt.treasuryFeesPaid = map[string]*num.Uint{} + mt.buybackFeesPaid = map[string]*num.Uint{} mt.epochTotalMakerFeesReceived = append(mt.epochTotalMakerFeesReceived, mt.totalMakerFeesReceived) mt.epochTotalMakerFeesPaid = append(mt.epochTotalMakerFeesPaid, mt.totalMakerFeesPaid) @@ -573,6 +681,13 @@ func (mat *MarketActivityTracker) UpdateFeesFromTransfers(asset, market string, mat.addFees(mt.infraFees, t.Owner, t.Amount.Amount, num.UintZero()) case types.TransferTypeLiquidityFeePay: mat.addFees(mt.lpPaidFees, t.Owner, t.Amount.Amount, num.UintZero()) + case types.TransferTypeBuyBackFeePay: + mat.addFees(mt.buybackFeesPaid, t.Owner, t.Amount.Amount, num.UintZero()) + case types.TransferTypeTreasuryPay: + mat.addFees(mt.treasuryFeesPaid, t.Owner, t.Amount.Amount, num.UintZero()) + case types.TransferTypeHighMakerRebateReceive: + // we count high maker fee receive as maker fees for that purpose. + mat.addFees(mt.makerFeesReceived, t.Owner, t.Amount.Amount, mt.totalMakerFeesReceived) default: } } @@ -618,7 +733,7 @@ func (mat *MarketActivityTracker) RecordPosition(asset, party, market string, po } notional, _ := num.UintFromDecimal(num.UintZero().Mul(num.NewUint(absPos), price).ToDecimal().Div(positionFactor)) tracker.recordPosition(party, absPos, positionFactor, time, mat.epochStartTime) - tracker.recordNotional(party, notional, time, mat.epochStartTime) + tracker.recordNotional(party, notional, price, time, mat.epochStartTime) } } @@ -656,19 +771,32 @@ func (mat *MarketActivityTracker) filterParties( if len(mkts) == 0 { includedMarkets = mat.GetAllMarketIDs() } - if len(includedMarkets) > 0 { - trackers, ok := mat.assetToMarketTrackers[asset] - if !ok { - return map[string]struct{}{} + assets := []string{} + if len(asset) == 0 { + assets = make([]string, 0, len(mat.assetToMarketTrackers)) + for k := range mat.assetToMarketTrackers { + assets = append(assets, k) } - for _, mkt := range includedMarkets { - mt, ok := trackers[mkt] + sort.Strings(assets) + } else { + assets = append(assets, asset) + } + + if len(includedMarkets) > 0 { + for _, ast := range assets { + trackers, ok := mat.assetToMarketTrackers[ast] if !ok { continue } - mktParties := cacheFilter(mt) - for k := range mktParties { - parties[k] = struct{}{} + for _, mkt := range includedMarkets { + mt, ok := trackers[mkt] + if !ok { + continue + } + mktParties := cacheFilter(mt) + for k := range mktParties { + parties[k] = struct{}{} + } } } } @@ -692,7 +820,25 @@ func (mat *MarketActivityTracker) getPartiesInScope(ds *vega.DispatchStrategy) [ if ds.IndividualScope == vega.IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM { parties = mat.teams.GetAllPartiesInTeams(mat.minEpochsInTeamForRewardEligibility) } else if ds.IndividualScope == vega.IndividualScope_INDIVIDUAL_SCOPE_ALL { - parties = sortedK(mat.getAllParties(ds.AssetForMetric, ds.Markets)) + if ds.Metric == vega.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES { + notionalReq := num.UintZero() + stakingReq := num.UintZero() + if len(ds.NotionalTimeWeightedAveragePositionRequirement) > 0 { + notionalReq = num.MustUintFromString(ds.NotionalTimeWeightedAveragePositionRequirement, 10) + } + if len(ds.StakingRequirement) > 0 { + stakingReq = num.MustUintFromString(ds.StakingRequirement, 10) + } + if !notionalReq.IsZero() { + parties = sortedK(mat.getAllParties(ds.AssetForMetric, ds.Markets)) + } else if !stakingReq.IsZero() { + parties = mat.balanceChecker.GetAllStakingParties() + } else { + parties = mat.collateral.GetAllParties() + } + } else { + parties = sortedK(mat.getAllParties(ds.AssetForMetric, ds.Markets)) + } } else if ds.IndividualScope == vega.IndividualScope_INDIVIDUAL_SCOPE_NOT_IN_TEAM { parties = sortedK(excludePartiesInTeams(mat.getAllParties(ds.AssetForMetric, ds.Markets), mat.teams.GetAllPartiesInTeams(mat.minEpochsInTeamForRewardEligibility))) } else if ds.IndividualScope == vega.IndividualScope_INDIVIDUAL_SCOPE_AMM { @@ -706,6 +852,10 @@ func getGameID(ds *vega.DispatchStrategy) string { return hex.EncodeToString(crypto.Hash(p)) } +func (mat *MarketActivityTracker) GameFinished(gameID string) { + delete(mat.eligibilityInEpoch, gameID) +} + // CalculateMetricForIndividuals calculates the metric corresponding to the dispatch strategy and returns a slice of the contribution scores of the parties. // Markets in scope are the ones passed in the dispatch strategy if any or all available markets for the asset for metric. // Parties in scope depend on the `IndividualScope_INDIVIDUAL_SCOPE_IN_TEAM` and can include all parties, only those in teams, and only those not in teams. @@ -780,6 +930,25 @@ func (mat *MarketActivityTracker) isEligibleForReward(ctx context.Context, asset return isEligible, balance, notional } +func getEligibilityScore(party, gameID string, eligibilityInEpoch map[string][]map[string]struct{}, balance *num.Uint, notional *num.Uint, paidFees map[string]*num.Uint, windowSize int) *types.PartyContributionScore { + if _, ok := eligibilityInEpoch[gameID]; !ok { + eligibilityInEpoch[gameID] = []map[string]struct{}{{}} + eligibilityInEpoch[gameID][0][party] = struct{}{} + return &types.PartyContributionScore{Party: party, Score: num.DecimalOne(), IsEligible: true, StakingBalance: balance, OpenVolume: notional, TotalFeesPaid: paidFees[party], RankingIndex: -1} + } + m := eligibilityInEpoch[gameID] + if len(m) > windowSize { + m = m[1:] + } + m[len(m)-1][party] = struct{}{} + for _, mm := range m { + if _, ok := mm[party]; !ok { + return &types.PartyContributionScore{Party: party, Score: num.DecimalZero(), IsEligible: false, StakingBalance: balance, OpenVolume: notional, TotalFeesPaid: paidFees[party], RankingIndex: -1} + } + } + return &types.PartyContributionScore{Party: party, Score: num.DecimalOne(), IsEligible: true, StakingBalance: balance, OpenVolume: notional, TotalFeesPaid: paidFees[party], RankingIndex: -1} +} + func (mat *MarketActivityTracker) calculateMetricForIndividuals(ctx context.Context, asset string, parties []string, markets []string, metric vega.DispatchMetric, minStakingBalanceRequired *num.Uint, notionalTimeWeightedAveragePositionRequired *num.Uint, windowSize int, gameID string, interval int32) []*types.PartyContributionScore { ret := make([]*types.PartyContributionScore, 0, len(parties)) paidFees := mat.GetLastEpochTakeFees(asset, markets, interval) @@ -789,6 +958,10 @@ func (mat *MarketActivityTracker) calculateMetricForIndividuals(ctx context.Cont ret = append(ret, &types.PartyContributionScore{Party: party, Score: num.DecimalZero(), IsEligible: eligible, StakingBalance: balance, OpenVolume: notional, TotalFeesPaid: paidFees[party], RankingIndex: -1}) continue } + if metric == vega.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES { + ret = append(ret, getEligibilityScore(party, gameID, mat.eligibilityInEpoch, balance, notional, paidFees, windowSize)) + continue + } score, ok := mat.calculateMetricForParty(asset, party, markets, metric, windowSize) if !ok { ret = append(ret, &types.PartyContributionScore{Party: party, Score: num.DecimalZero(), IsEligible: false, StakingBalance: balance, OpenVolume: notional, TotalFeesPaid: paidFees[party], RankingIndex: -1}) @@ -822,7 +995,7 @@ func (mat *MarketActivityTracker) calculateMetricForTeams(ctx context.Context, a // calculateMetricForTeam returns the metric score for team and a slice of the score for each of its members. func (mat *MarketActivityTracker) calculateMetricForTeam(ctx context.Context, asset string, parties []string, marketsInScope []string, metric vega.DispatchMetric, minStakingBalanceRequired *num.Uint, notionalTimeWeightedAveragePositionRequired *num.Uint, windowSize int, topN num.Decimal, gameID string, paidFees map[string]*num.Uint) (num.Decimal, []*types.PartyContributionScore) { - return calculateMetricForTeamUtil(ctx, asset, parties, marketsInScope, metric, minStakingBalanceRequired, notionalTimeWeightedAveragePositionRequired, windowSize, topN, mat.isEligibleForReward, mat.calculateMetricForParty, gameID, paidFees) + return calculateMetricForTeamUtil(ctx, asset, parties, marketsInScope, metric, minStakingBalanceRequired, notionalTimeWeightedAveragePositionRequired, windowSize, topN, mat.isEligibleForReward, mat.calculateMetricForParty, gameID, paidFees, mat.eligibilityInEpoch) } func calculateMetricForTeamUtil(ctx context.Context, @@ -838,6 +1011,7 @@ func calculateMetricForTeamUtil(ctx context.Context, calculateMetricForParty func(asset, party string, marketsInScope []string, metric vega.DispatchMetric, windowSize int) (num.Decimal, bool), gameID string, paidFees map[string]*num.Uint, + eligibilityInEpoch map[string][]map[string]struct{}, ) (num.Decimal, []*types.PartyContributionScore) { teamPartyScores := []*types.PartyContributionScore{} eligibleTeamPartyScores := []*types.PartyContributionScore{} @@ -847,6 +1021,15 @@ func calculateMetricForTeamUtil(ctx context.Context, teamPartyScores = append(teamPartyScores, &types.PartyContributionScore{Party: party, Score: num.DecimalZero(), IsEligible: eligible, StakingBalance: balance, OpenVolume: notional, TotalFeesPaid: paidFees[party], RankingIndex: -1}) continue } + + if metric == vega.DispatchMetric_DISPATCH_METRIC_ELIGIBLE_ENTITIES { + score := getEligibilityScore(party, gameID, eligibilityInEpoch, balance, notional, paidFees, windowSize) + teamPartyScores = append(teamPartyScores, score) + if score.IsEligible { + eligibleTeamPartyScores = append(eligibleTeamPartyScores, score) + } + continue + } if score, ok := calculateMetricForParty(asset, party, marketsInScope, metric, windowSize); ok { teamPartyScores = append(teamPartyScores, &types.PartyContributionScore{Party: party, Score: score, IsEligible: eligible, StakingBalance: balance, OpenVolume: notional, TotalFeesPaid: paidFees[party], RankingIndex: -1}) eligibleTeamPartyScores = append(eligibleTeamPartyScores, &types.PartyContributionScore{Party: party, Score: score, IsEligible: eligible, StakingBalance: balance, OpenVolume: notional, TotalFeesPaid: paidFees[party], RankingIndex: -1}) @@ -904,7 +1087,6 @@ func (mat *MarketActivityTracker) calculateMetricForParty(asset, party string, m if metric == vega.DispatchMetric_DISPATCH_METRIC_VALIDATOR_RANKING { mat.log.Panic("unexpected dispatch metric validator ranking here") } - uTotal := uint64(0) total := num.DecimalZero() marketTotal := num.DecimalZero() returns := make([]*num.Decimal, windowSize) @@ -930,10 +1112,10 @@ func (mat *MarketActivityTracker) calculateMetricForParty(asset, party string, m continue } switch metric { - case vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION: - if t, ok := marketTracker.getPositionMetricTotal(party, windowSize); ok { + case vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL: + if t, ok := marketTracker.getNotionalMetricTotal(party, windowSize); ok { found = true - uTotal += t + total = total.Add(t) } case vega.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN: if t, ok := marketTracker.getRelativeReturnMetricTotal(party, windowSize); ok { @@ -988,9 +1170,10 @@ func (mat *MarketActivityTracker) calculateMetricForParty(asset, party string, m } switch metric { - case vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION: + case vega.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL: // descaling the total tw position metric by dividing by the scaling factor - return num.DecimalFromInt64(int64(uTotal)).Div(num.DecimalFromInt64(int64(windowSize) * scalingFactor)), found + v := total.Div(num.DecimalFromInt64(int64(windowSize) * scalingFactor)) + return v, found case vega.DispatchMetric_DISPATCH_METRIC_RELATIVE_RETURN, vega.DispatchMetric_DISPATCH_METRIC_REALISED_RETURN: return total.Div(num.DecimalFromInt64(int64(windowSize))), found case vega.DispatchMetric_DISPATCH_METRIC_RETURN_VOLATILITY: @@ -1105,7 +1288,7 @@ func (mat *MarketActivityTracker) NotionalTakerVolumeForParty(party string) *num return mat.partyTakerNotionalVolume[party].Clone() } -func updateNotional(n *twNotional, notional *num.Uint, t, tn int64, time time.Time) { +func updateNotionalOnTrade(n *twNotional, notional, price *num.Uint, t, tn int64, time time.Time) { tnOverT := num.UintZero() tnOverTComp := uScalingFactor.Clone() if t != 0 { @@ -1116,10 +1299,11 @@ func updateNotional(n *twNotional, notional *num.Uint, t, tn int64, time time.Ti p2 := num.UintZero().Mul(n.notional, tnOverT) n.currentEpochTWNotional = num.UintZero().Div(p1.AddSum(p2), uScalingFactor) n.notional = notional + n.price = price.Clone() n.t = time } -func calcNotionalAt(n *twNotional, t, tn int64) *num.Uint { +func updateNotionalOnEpochEnd(n *twNotional, notional, price *num.Uint, t, tn int64, time time.Time) { tnOverT := num.UintZero() tnOverTComp := uScalingFactor.Clone() if t != 0 { @@ -1127,26 +1311,50 @@ func calcNotionalAt(n *twNotional, t, tn int64) *num.Uint { tnOverTComp = tnOverTComp.Sub(tnOverTComp, tnOverT) } p1 := num.UintZero().Mul(n.currentEpochTWNotional, tnOverTComp) - p2 := num.UintZero().Mul(n.notional, tnOverT) + p2 := num.UintZero().Mul(notional, tnOverT) + n.currentEpochTWNotional = num.UintZero().Div(p1.AddSum(p2), uScalingFactor) + n.notional = notional + if price != nil && !price.IsZero() { + n.price = price.Clone() + } + n.t = time +} + +func calcNotionalAt(n *twNotional, t, tn int64, markPrice *num.Uint) *num.Uint { + tnOverT := num.UintZero() + tnOverTComp := uScalingFactor.Clone() + if t != 0 { + tnOverT = num.NewUint(uint64(tn / t)) + tnOverTComp = tnOverTComp.Sub(tnOverTComp, tnOverT) + } + p1 := num.UintZero().Mul(n.currentEpochTWNotional, tnOverTComp) + var notional *num.Uint + if markPrice != nil && !markPrice.IsZero() { + notional, _ = num.UintFromDecimal(n.notional.ToDecimal().Div(n.price.ToDecimal()).Mul(markPrice.ToDecimal())) + } else { + notional = n.notional + } + p2 := num.UintZero().Mul(notional, tnOverT) return num.UintZero().Div(p1.AddSum(p2), uScalingFactor) } // recordNotional tracks the time weighted average notional for the party per market. // notional = abs(position) x price / position_factor // price in asset decimals. -func (mt *marketTracker) recordNotional(party string, notional *num.Uint, time time.Time, epochStartTime time.Time) { +func (mt *marketTracker) recordNotional(party string, notional *num.Uint, price *num.Uint, time time.Time, epochStartTime time.Time) { if _, ok := mt.twNotional[party]; !ok { mt.twNotional[party] = &twNotional{ t: time, notional: notional, currentEpochTWNotional: num.UintZero(), + price: price.Clone(), } return } t := int64(time.Sub(epochStartTime).Seconds()) n := mt.twNotional[party] tn := int64(time.Sub(n.t).Seconds()) * scalingFactor - updateNotional(n, notional, t, tn, time) + updateNotionalOnTrade(n, notional, price, t, tn, time) } func (mt *marketTracker) processNotionalEndOfEpoch(epochStartTime time.Time, endEpochTime time.Time) { @@ -1154,13 +1362,25 @@ func (mt *marketTracker) processNotionalEndOfEpoch(epochStartTime time.Time, end m := make(map[string]*num.Uint, len(mt.twNotional)) for party, twNotional := range mt.twNotional { tn := int64(endEpochTime.Sub(twNotional.t).Seconds()) * scalingFactor - updateNotional(twNotional, twNotional.notional, t, tn, endEpochTime) + var notional *num.Uint + if mt.markPrice != nil && !mt.markPrice.IsZero() { + notional, _ = num.UintFromDecimal(twNotional.notional.ToDecimal().Div(twNotional.price.ToDecimal()).Mul(mt.markPrice.ToDecimal())) + } else { + notional = twNotional.notional + } + updateNotionalOnEpochEnd(twNotional, notional, mt.markPrice, t, tn, endEpochTime) m[party] = twNotional.currentEpochTWNotional.Clone() } if len(mt.epochTimeWeightedNotional) == maxWindowSize { mt.epochTimeWeightedNotional = mt.epochTimeWeightedNotional[1:] } mt.epochTimeWeightedNotional = append(mt.epochTimeWeightedNotional, m) + for p, twp := range mt.twNotional { + // if the notional at the beginning of the epoch is 0 clear it so we don't keep zero notionals`` forever + if twp.currentEpochTWNotional.IsZero() && twp.notional.IsZero() { + delete(mt.twNotional, p) + } + } } func (mt *marketTracker) processNotionalAtMilestone(epochStartTime time.Time, milestoneTime time.Time) { @@ -1168,7 +1388,7 @@ func (mt *marketTracker) processNotionalAtMilestone(epochStartTime time.Time, mi m := make(map[string]*num.Uint, len(mt.twNotional)) for party, twNotional := range mt.twNotional { tn := int64(milestoneTime.Sub(twNotional.t).Seconds()) * scalingFactor - m[party] = calcNotionalAt(twNotional, t, tn) + m[party] = calcNotionalAt(twNotional, t, tn, mt.markPrice) } mt.epochTimeWeightedNotional = append(mt.epochTimeWeightedNotional, m) } @@ -1392,6 +1612,11 @@ func (mt *marketTracker) getReturns(party string, windowSize int) ([]*num.Decima return returns, found } +// getNotionalMetricTotal returns the sum of the epoch's time weighted notional over the time window. +func (mt *marketTracker) getNotionalMetricTotal(party string, windowSize int) (num.Decimal, bool) { + return calcTotalForWindowU(party, mt.epochTimeWeightedNotional, windowSize) +} + // getPositionMetricTotal returns the sum of the epoch's time weighted position over the time window. func (mt *marketTracker) getPositionMetricTotal(party string, windowSize int) (uint64, bool) { return calcTotalForWindowUint64(party, mt.epochTimeWeightedPosition, windowSize) diff --git a/core/execution/common/market_activity_tracker_checkpoint.go b/core/execution/common/market_activity_tracker_checkpoint.go index e888569eede..aa68ee1e7ba 100644 --- a/core/execution/common/market_activity_tracker_checkpoint.go +++ b/core/execution/common/market_activity_tracker_checkpoint.go @@ -55,6 +55,7 @@ func (mat *MarketActivityTracker) Checkpoint() ([]byte, error) { TakerNotionalVolume: takerNotionalToProto(mat.partyTakerNotionalVolume), MarketToPartyTakerNotionalVolume: marketToPartyTakerNotionalToProto(mat.marketToPartyTakerNotionalVolume), EpochTakerFees: epochTakerFeesToProto(mat.takerFeesPaidInEpoch), + GameEligibilityTracker: epochEligitbilityToProto(mat.eligibilityInEpoch), } ret, err := proto.Marshal(msg) if err != nil { @@ -105,5 +106,17 @@ func (mat *MarketActivityTracker) Load(_ context.Context, data []byte) error { mat.takerFeesPaidInEpoch = append(mat.takerFeesPaidInEpoch, epochMap) } } + if b.GameEligibilityTracker != nil { + for _, get := range b.GameEligibilityTracker { + mat.eligibilityInEpoch[get.GameId] = make([]map[string]struct{}, len(get.EpochEligibility)) + for i, epoch := range get.EpochEligibility { + mat.eligibilityInEpoch[get.GameId][i] = make(map[string]struct{}, len(epoch.EligibleParties)) + for _, party := range epoch.EligibleParties { + mat.eligibilityInEpoch[get.GameId][i][party] = struct{}{} + } + } + } + } + return nil } diff --git a/core/execution/common/market_activity_tracker_internal_test.go b/core/execution/common/market_activity_tracker_internal_test.go index f187eaac070..c2194ab9417 100644 --- a/core/execution/common/market_activity_tracker_internal_test.go +++ b/core/execution/common/market_activity_tracker_internal_test.go @@ -26,7 +26,6 @@ import ( "code.vegaprotocol.io/vega/core/types" "code.vegaprotocol.io/vega/libs/num" "code.vegaprotocol.io/vega/logging" - "code.vegaprotocol.io/vega/protos/vega" vgproto "code.vegaprotocol.io/vega/protos/vega" "github.com/golang/mock/gomock" @@ -307,15 +306,15 @@ func TestPositions(t *testing.T) { func TestAverageNotional(t *testing.T) { tracker := getDefaultTracker(t) // epoch 1 - tracker.recordNotional("p1", num.NewUint(50), time.Unix(5, 0), time.Unix(0, 0)) + tracker.recordNotional("p1", num.NewUint(50), num.NewUint(1), time.Unix(5, 0), time.Unix(0, 0)) require.Equal(t, "0", tracker.twNotional["p1"].currentEpochTWNotional.String()) // (( 0 * 3333334 ) + ( 50 * 6666666 )) / 10000000 = 33 - tracker.recordNotional("p1", num.NewUint(200), time.Unix(15, 0), time.Unix(0, 0)) + tracker.recordNotional("p1", num.NewUint(200), num.NewUint(1), time.Unix(15, 0), time.Unix(0, 0)) require.Equal(t, "33", tracker.twNotional["p1"].currentEpochTWNotional.String()) // (( 33 * 5000000 ) + ( 200 * 5000000 )) / 10000000 = 116 - tracker.recordNotional("p1", num.NewUint(600), time.Unix(30, 0), time.Unix(0, 0)) + tracker.recordNotional("p1", num.NewUint(600), num.NewUint(1), time.Unix(30, 0), time.Unix(0, 0)) require.Equal(t, "116", tracker.twNotional["p1"].currentEpochTWNotional.String()) // (( 116 * 5000000 ) + ( 600 * 5000000 )) / 10000000 = 358 @@ -325,7 +324,7 @@ func TestAverageNotional(t *testing.T) { // epoch 2 // (( 358 * 0 ) + ( 600 * 10000000 )) / 10000000 = 600 - tracker.recordNotional("p1", num.NewUint(300), time.Unix(90, 0), time.Unix(60, 0)) + tracker.recordNotional("p1", num.NewUint(300), num.NewUint(1), time.Unix(90, 0), time.Unix(60, 0)) require.Equal(t, "600", tracker.twNotional["p1"].currentEpochTWNotional.String()) // (( 600 * 5000000 ) + ( 300 * 5000000 )) / 10000000 = 450 @@ -341,7 +340,7 @@ func TestAverageNotional(t *testing.T) { require.Equal(t, "300", tracker.epochTimeWeightedNotional[len(tracker.epochTimeWeightedNotional)-1]["p1"].String()) } -func TestCalculateMetricForIndividualsAvePosition(t *testing.T) { +func TestCalculateMetricForIndividualsAveNotional(t *testing.T) { ctx := context.Background() epochService := &DummyEpochEngine{} ctrl := gomock.NewController(t) @@ -350,7 +349,8 @@ func TestCalculateMetricForIndividualsAvePosition(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -379,48 +379,48 @@ func TestCalculateMetricForIndividualsAvePosition(t *testing.T) { // get metrics for market m1 with window size=1 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics := tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 1, gameID, 1) + metrics := tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 1, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "9.166666", metrics[0].Score.String()) - require.Equal(t, "75", metrics[1].Score.String()) + require.Equal(t, "0.0000009", metrics[0].Score.String()) + require.Equal(t, "0.000075", metrics[1].Score.String()) // get metrics for market m2 with window size=1 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 1, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 1, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "13.333332", metrics[0].Score.String()) - require.Equal(t, "116.66666", metrics[1].Score.String()) + require.Equal(t, "0.0000026", metrics[0].Score.String()) + require.Equal(t, "0.0002333", metrics[1].Score.String()) // get metrics for market m3 with window size=1 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 1, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 1, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "15", metrics[0].Score.String()) - require.Equal(t, "75", metrics[1].Score.String()) + require.Equal(t, "0.0000045", metrics[0].Score.String()) + require.Equal(t, "0.000225", metrics[1].Score.String()) // get metrics for market m1,m2 with window size=1 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 1, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 1, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "22.499998", metrics[0].Score.String()) - require.Equal(t, "191.66666", metrics[1].Score.String()) + require.Equal(t, "0.0000035", metrics[0].Score.String()) + require.Equal(t, "0.0003083", metrics[1].Score.String()) // get metrics for all market window size=1 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 1, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 1, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "37.499998", metrics[0].Score.String()) - require.Equal(t, "266.66666", metrics[1].Score.String()) + require.Equal(t, "0.000008", metrics[0].Score.String()) + require.Equal(t, "0.0005333", metrics[1].Score.String()) // start epoch2 epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(60, 0)}) @@ -433,99 +433,99 @@ func TestCalculateMetricForIndividualsAvePosition(t *testing.T) { // get metrics for market m1 with window size=1 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 1, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 1, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "15", metrics[0].Score.String()) - require.Equal(t, "100", metrics[1].Score.String()) + require.Equal(t, "0.0000055", metrics[0].Score.String()) + require.Equal(t, "0.0001", metrics[1].Score.String()) // get metrics for market m2 with window size=1 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 1, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 1, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "20", metrics[0].Score.String()) - require.Equal(t, "57.5", metrics[1].Score.String()) + require.Equal(t, "0.000004", metrics[0].Score.String()) + require.Equal(t, "0.0001075", metrics[1].Score.String()) // get metrics for market m2 with window size=1 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 1, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 1, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "30", metrics[0].Score.String()) - require.Equal(t, "300", metrics[1].Score.String()) + require.Equal(t, "0.000009", metrics[0].Score.String()) + require.Equal(t, "0.0009", metrics[1].Score.String()) // get metrics for market m1,m2 with window size=1 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 1, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 1, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "35", metrics[0].Score.String()) - require.Equal(t, "157.5", metrics[1].Score.String()) + require.Equal(t, "0.0000095", metrics[0].Score.String()) + require.Equal(t, "0.0002075", metrics[1].Score.String()) // get metrics for all market window size=1 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 1, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 1, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "65", metrics[0].Score.String()) - require.Equal(t, "457.5", metrics[1].Score.String()) + require.Equal(t, "0.0000185", metrics[0].Score.String()) + require.Equal(t, "0.0011075", metrics[1].Score.String()) // calc with window size = 2 // get metrics for market m1 with window size=2 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 2, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 2, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "12.083333", metrics[0].Score.String()) - require.Equal(t, "87.5", metrics[1].Score.String()) + require.Equal(t, "0.0000032", metrics[0].Score.String()) + require.Equal(t, "0.0000875", metrics[1].Score.String()) // get metrics for market m2 with window size=2 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 2, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 2, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "16.666666", metrics[0].Score.String()) - require.Equal(t, "87.08333", metrics[1].Score.String()) + require.Equal(t, "0.0000033", metrics[0].Score.String()) + require.Equal(t, "0.0001704", metrics[1].Score.String()) // get metrics for market m2 with window size=2 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 2, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 2, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "22.5", metrics[0].Score.String()) - require.Equal(t, "187.5", metrics[1].Score.String()) + require.Equal(t, "0.00000675", metrics[0].Score.String()) + require.Equal(t, "0.0005625", metrics[1].Score.String()) // get metrics for market m1,m2 with window size=2 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 2, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 2, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "28.749999", metrics[0].Score.String()) - require.Equal(t, "174.58333", metrics[1].Score.String()) + require.Equal(t, "0.0000065", metrics[0].Score.String()) + require.Equal(t, "0.0002579", metrics[1].Score.String()) // get metrics for all market window size=2 balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.NewUint(0), nil).Times(2) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintZero(), num.UintZero(), 2, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintZero(), num.UintZero(), 2, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) - require.Equal(t, "51.249999", metrics[0].Score.String()) - require.Equal(t, "362.08333", metrics[1].Score.String()) + require.Equal(t, "0.00001325", metrics[0].Score.String()) + require.Equal(t, "0.0008204", metrics[1].Score.String()) // now make p2 not eligible via not having sufficient governance token balanceChecker.EXPECT().GetAvailableBalance("p1").Return(num.NewUint(2), nil).Times(1) balanceChecker.EXPECT().GetAvailableBalance("p2").Return(nil, errors.New("some error")).Times(1) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.NewUint(1), num.UintZero(), 2, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.NewUint(1), num.UintZero(), 2, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "p2", metrics[1].Party) @@ -535,7 +535,7 @@ func TestCalculateMetricForIndividualsAvePosition(t *testing.T) { // repeat now p2 has balance just not enough balanceChecker.EXPECT().GetAvailableBalance("p1").Return(num.NewUint(2), nil).Times(1) balanceChecker.EXPECT().GetAvailableBalance("p2").Return(num.NewUint(1), nil).Times(1) - metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.NewUint(2), num.UintZero(), 2, gameID, 1) + metrics = tracker.calculateMetricForIndividuals(ctx, "a1", []string{"p1", "p2"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.NewUint(2), num.UintZero(), 2, gameID, 1) require.Equal(t, 2, len(metrics)) require.Equal(t, "p1", metrics[0].Party) require.Equal(t, "2", metrics[0].StakingBalance.String()) @@ -545,14 +545,15 @@ func TestCalculateMetricForIndividualsAvePosition(t *testing.T) { require.Equal(t, "1", metrics[1].StakingBalance.String()) } -func TestCalculateMetricForPartyAvePosition(t *testing.T) { +func TestCalculateMetricForPartyAveNotional(t *testing.T) { epochService := &DummyEpochEngine{} ctrl := gomock.NewController(t) teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -575,41 +576,41 @@ func TestCalculateMetricForPartyAvePosition(t *testing.T) { // calculate metric for p1 with scope=[m1] for window size=1 // 0*(1-0.9166666666666667)+10*0.9166666666666667 = 9.1666666667 - score, _ := tracker.calculateMetricForParty("a1", "p1", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "9.166666", score.String()) + score, _ := tracker.calculateMetricForParty("a1", "p1", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0000009", score.String()) // calculate metric for p1 with scope=[m2] for window size=1 // 0*(1-0.6666666666666667)+20*0.6666666666666667 = 13.3333333333 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "13.333332", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0000026", score.String()) // calculate metric for p1 with scope=[m3] for window size=1 // 0*(1-0.5)+30*0.5 = 15 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "15", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0000045", score.String()) // calculate metric for p1 with scope=[m1, m2] for window size=1 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "22.499998", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0000035", score.String()) // calculate metric for p1 with no scope for window size=1 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "37.499998", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.000008", score.String()) // calculate metric for p2 with scope=[m1] for window size=1 // 0*(1-0.75)+100*0.75 = 75 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "75", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.000075", score.String()) // calculate metric for p2 with scope=[m2] for window size=1 // 0*(1-0.5833333333333333)+200*0.5833333333333333 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "116.66666", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0002333", score.String()) // calculate metric for p2 with scope=[m3] for window size=1 // 0*(1-0.25)+300*0.25 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "75", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.000225", score.String()) // calculate metric for p2 with scope=[m1, m3] for window size=1 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1", "m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "150", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1", "m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0003", score.String()) // calculate metric for p2 with no scope for window size=1 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "266.66666", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0005333", score.String()) // start epoch2 epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(60, 0)}) @@ -622,81 +623,81 @@ func TestCalculateMetricForPartyAvePosition(t *testing.T) { // calculate metric for p1 with scope=[m1] for window size=1 // 10*(1-0.5)+20*0.5 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "15", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0000055", score.String()) // calculate metric for p1 with scope=[m2] for window size=1 // 13.333333333333334*(1-1)+20*1 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "20", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.000004", score.String()) // calculate metric for p1 with scope=[m3] for window size=1 // 15*(1-1)+30*1 = 30 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "30", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.000009", score.String()) // calculate metric for p1 with scope=[m1, m3] for window size=1 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "35", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0000095", score.String()) // calculate metric for p1 with no scope for window size=1 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "65", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0000185", score.String()) // calculate metric for p2 with scope=[m1] for window size=1 // 75*(1-1)+100*1 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "100", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0001", score.String()) // calculate metric for p1 with scope=[m2] for window size=1 // 200*(1-0.75)+10*0.75 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "57.5", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0001075", score.String()) // calculate metric for p1 with scope=[m3] for window size=1 // 75*(1-1)+300*1 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "300", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0009", score.String()) // calculate metric for p1 with scope=[m1, m3] for window size=1 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "157.5", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0002075", score.String()) // calculate metric for p1 with no scope for window size=1 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "457.5", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0011075", score.String()) // now calculate for window size=2 // calculate metric for p1 with scope=[m1] for window size=2 // (15 + 9.166666666666667)/2 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 2) - require.Equal(t, "12.083333", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 2) + require.Equal(t, "0.0000032", score.String()) // calculate metric for p1 with scope=[m2] for window size=2 // (13.333333333333334" + 20)/2 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 2) - require.Equal(t, "16.666666", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 2) + require.Equal(t, "0.0000033", score.String()) // calculate metric for p1 with scope=[m3] for window size=2 // (15 + 30)/2 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 2) - require.Equal(t, "22.5", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 2) + require.Equal(t, "0.00000675", score.String()) // calculate metric for p1 with scope=[m1, m3] for window size=2 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 2) - require.Equal(t, "28.749999", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 2) + require.Equal(t, "0.0000065", score.String()) // calculate metric for p1 with no scope for window size=2 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 2) - require.Equal(t, "51.249999", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 2) + require.Equal(t, "0.00001325", score.String()) // calculate metric for p2 with scope=[m1] for window size=2 // (100 + 75)/2 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 2) - require.Equal(t, "87.5", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 2) + require.Equal(t, "0.0000875", score.String()) // calculate metric for p2 with scope=[m2] for window size=2 // (116.66666666666666 + 57.5)/2 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 2) - require.Equal(t, "87.08333", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 2) + require.Equal(t, "0.0001704", score.String()) // calculate metric for p2 with scope=[m3] for window size=2 // (300 + 75)/2 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 2) - require.Equal(t, "187.5", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 2) + require.Equal(t, "0.0005625", score.String()) // calculate metric for p2 with scope=[m1, m3] for window size=2 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1", "m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 2) - require.Equal(t, "275", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1", "m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 2) + require.Equal(t, "0.00065", score.String()) // calculate metric for p2 with no scope for window size=2 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 2) - require.Equal(t, "362.08333", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 2) + require.Equal(t, "0.0008204", score.String()) // start epoch3 epochService.target(context.Background(), types.Epoch{Seq: 3, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(120, 0)}) @@ -704,81 +705,81 @@ func TestCalculateMetricForPartyAvePosition(t *testing.T) { epochService.target(context.Background(), types.Epoch{Seq: 3, Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: time.Unix(120, 0), EndTime: time.Unix(180, 0)}) // calculate metric for p1 with scope=[m1] for window size=1 // 15*(1-1)+20*1 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "20", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.00001", score.String()) // calculate metric for p1 with scope=[m2] for window size=1 // 20*(1-1)+20*1 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "20", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.000004", score.String()) // calculate metric for p1 with scope=[m3] for window size=1 // 30*(1-1)+30*1 = 30 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "30", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.000009", score.String()) // calculate metric for p1 with scope=[m1, m3] for window size=1 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "40", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.000014", score.String()) // calculate metric for p1 with no scope for window size=1 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "70", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.000023", score.String()) // calculate metric for p2 with scope=[m1] for window size=1 // 100*(1-1)+100*1 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "100", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0001", score.String()) // calculate metric for p2 with scope=[m2] for window size=1 // 57.5*(1-1)+10*1 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "10", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.00001", score.String()) // calculate metric for p2 with scope=[m3] for window size=1 // 300*(1-1)+300*1 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "300", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.0009", score.String()) // calculate metric for p2 with scope=[m1, m3] for window size=1 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "110", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.00011", score.String()) // calculate metric for p2 with no scope for window size=1 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 1) - require.Equal(t, "410", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 1) + require.Equal(t, "0.00101", score.String()) // now calculate for window size=3 // calculate metric for p1 with scope=[m1] for window size=3 // (9.166666666666667 + 15 + 20)/3 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 3) - require.Equal(t, "14.722222", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 3) + require.Equal(t, "0.0000054666666667", score.String()) // calculate metric for p1 with scope=[m2] for window size=3 // (13.333333333333334" + 20 + 20)/3 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 3) - require.Equal(t, "17.7777773333333333", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 3) + require.Equal(t, "0.0000035333333333", score.String()) // calculate metric for p1 with scope=[m3] for window size=3 // (15 + 30 + 30)/3 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 3) - require.Equal(t, "25", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 3) + require.Equal(t, "0.0000075", score.String()) // calculate metric for p1 with scope=[m1, m3] for window size=3 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 3) - require.Equal(t, "32.4999993333333333", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{"m1", "m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 3) + require.Equal(t, "0.000009", score.String()) // calculate metric for p1 with no scope for window size=3 - score, _ = tracker.calculateMetricForParty("a1", "p1", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 3) - require.Equal(t, "57.4999993333333333", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p1", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 3) + require.Equal(t, "0.0000165", score.String()) // calculate metric for p2 with scope=[m1] for window size=3 // (100 + 75+100)/3 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 3) - require.Equal(t, "91.6666666666666667", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 3) + require.Equal(t, "0.0000916666666667", score.String()) // calculate metric for p2 with scope=[m2] for window size=3 // (116.66666666666666 + 57.5 + 10)/3 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 3) - require.Equal(t, "61.3888866666666667", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m2"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 3) + require.Equal(t, "0.0001169333333333", score.String()) // calculate metric for p2 with scope=[m3] for window size=3 // (300 + 75 + 300)/3 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 3) - require.Equal(t, "225", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 3) + require.Equal(t, "0.000675", score.String()) // calculate metric for p2 with scope=[m1, m3] for window size=3 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1", "m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 3) - require.Equal(t, "316.6666666666666667", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{"m1", "m3"}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 3) + require.Equal(t, "0.0007666666666667", score.String()) // calculate metric for p2 with no scope for window size=3 - score, _ = tracker.calculateMetricForParty("a1", "p2", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, 3) - require.Equal(t, "378.0555533333333333", score.String()) + score, _ = tracker.calculateMetricForParty("a1", "p2", []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, 3) + require.Equal(t, "0.0008836", score.String()) } func TestCalculateMetricForIndividualReturnVolatility(t *testing.T) { @@ -790,7 +791,8 @@ func TestCalculateMetricForIndividualReturnVolatility(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().Send(gomock.Any()).AnyTimes() broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -996,7 +998,8 @@ func TestCalculateMetricForIndividualsRelativeReturn(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().Send(gomock.Any()).AnyTimes() broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -1217,7 +1220,8 @@ func TestCalculateMetricForPartyRelativeReturn(t *testing.T) { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -1474,7 +1478,8 @@ func TestCalculateMetricForParty(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -1510,7 +1515,7 @@ func TestCalculateMetricForTeamUtil(t *testing.T) { } return false, num.UintZero(), num.UintZero() } - calculateMetricForParty := func(asset, party string, marketsInScope []string, metric vega.DispatchMetric, windowSize int) (num.Decimal, bool) { + calculateMetricForParty := func(asset, party string, marketsInScope []string, metric vgproto.DispatchMetric, windowSize int) (num.Decimal, bool) { if party == "party1" { return num.DecimalFromFloat(1.5), true } @@ -1530,7 +1535,7 @@ func TestCalculateMetricForTeamUtil(t *testing.T) { } gameID := "game123" - teamScore, partyScores := calculateMetricForTeamUtil(ctx, "asset1", []string{"party1", "party2", "party3", "party4", "party5"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintOne(), num.UintOne(), int(5), num.DecimalFromFloat(0.5), isEligible, calculateMetricForParty, gameID, map[string]*num.Uint{}) + teamScore, partyScores := calculateMetricForTeamUtil(ctx, "asset1", []string{"party1", "party2", "party3", "party4", "party5"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintOne(), num.UintOne(), int(5), num.DecimalFromFloat(0.5), isEligible, calculateMetricForParty, gameID, map[string]*num.Uint{}, map[string][]map[string]struct{}{}) // we're indicating the the score of the team need to be the mean of the top 0.5 * number of participants = floor(0.5*5) = 2 // the top scores are 2.5 and 2 => team score should be 2.25 // 4 party scores expected (1-4) as party5 is not eligible @@ -1548,7 +1553,7 @@ func TestCalculateMetricForTeamUtil(t *testing.T) { require.Equal(t, false, partyScores[4].IsEligible) // lets repeat the check when there is no one eligible - teamScore, partyScores = calculateMetricForTeamUtil(ctx, "asset1", []string{"party5"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, num.UintOne(), num.UintOne(), 5, num.DecimalFromFloat(0.5), isEligible, calculateMetricForParty, gameID, map[string]*num.Uint{}) + teamScore, partyScores = calculateMetricForTeamUtil(ctx, "asset1", []string{"party5"}, []string{}, vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, num.UintOne(), num.UintOne(), 5, num.DecimalFromFloat(0.5), isEligible, calculateMetricForParty, gameID, map[string]*num.Uint{}, map[string][]map[string]struct{}{}) require.Equal(t, "0", teamScore.String()) require.Equal(t, 1, len(partyScores)) require.Equal(t, "party5", partyScores[0].Party) @@ -1563,6 +1568,16 @@ func (e *DummyEpochEngine) NotifyOnEpoch(f func(context.Context, types.Epoch), _ e.target = f } +type DummyCollateralEngine struct{} + +func (e DummyCollateralEngine) GetAssetQuantum(asset string) (num.Decimal, error) { + return num.DecimalOne(), nil +} + +func (e DummyCollateralEngine) GetAllParties() []string { + return []string{} +} + type DummyEligibilityChecker struct{} func (e *DummyEligibilityChecker) IsEligibleForProposerBonus(marketID string, volumeTraded *num.Uint) bool { @@ -1584,7 +1599,7 @@ func TestIntoProto(t *testing.T) { totalLpFees: num.NewUint(11), twPosition: map[string]*twPosition{"p1": {t: time.Now(), position: 200, currentEpochTWPosition: 300}}, partyM2M: map[string]num.Decimal{"p1": num.DecimalFromInt64(20)}, - twNotional: map[string]*twNotional{"p2": {t: time.Now(), notional: num.NewUint(50), currentEpochTWNotional: num.NewUint(55)}}, + twNotional: map[string]*twNotional{"p2": {t: time.Now(), notional: num.NewUint(50), currentEpochTWNotional: num.NewUint(55), price: num.NewUint(100)}}, epochTotalMakerFeesReceived: []*num.Uint{num.NewUint(3000), num.NewUint(7000)}, epochTotalMakerFeesPaid: []*num.Uint{num.NewUint(3300), num.NewUint(7700)}, epochTotalLpFees: []*num.Uint{num.NewUint(3600), num.NewUint(8400)}, diff --git a/core/execution/common/market_activity_tracker_snapshot.go b/core/execution/common/market_activity_tracker_snapshot.go index 38e92fc88f8..abd6fefc485 100644 --- a/core/execution/common/market_activity_tracker_snapshot.go +++ b/core/execution/common/market_activity_tracker_snapshot.go @@ -90,6 +90,34 @@ func epochReturnDataToProto(epochData []map[string]num.Decimal) []*checkpoint.Ep return ret } +func epochEligitbilityToProto(eligibilityData map[string][]map[string]struct{}) []*checkpoint.GameEligibilityTracker { + res := make([]*checkpoint.GameEligibilityTracker, 0, len(eligibilityData)) + gameIDs := make([]string, 0, len(eligibilityData)) + for k := range eligibilityData { + gameIDs = append(gameIDs, k) + } + sort.Strings(gameIDs) + for _, gameID := range gameIDs { + epochs := eligibilityData[gameID] + get := &checkpoint.GameEligibilityTracker{ + GameId: gameID, + EpochEligibility: make([]*checkpoint.EpochEligibility, 0, len(epochs)), + } + for _, epoch := range epochs { + epochEligibleParties := make([]string, 0, len(epoch)) + for party := range epoch { + epochEligibleParties = append(epochEligibleParties, party) + } + sort.Strings(epochEligibleParties) + get.EpochEligibility = append(get.EpochEligibility, &checkpoint.EpochEligibility{ + EligibleParties: epochEligibleParties, + }) + } + res = append(res, get) + } + return res +} + func epochTakerFeesToProto(epochData []map[string]map[string]map[string]*num.Uint) []*checkpoint.EpochPartyTakerFees { ret := make([]*checkpoint.EpochPartyTakerFees, 0, len(epochData)) for _, epoch := range epochData { @@ -150,6 +178,9 @@ func timeWeightedNotionalToProto(twNotional map[string]*twNotional) []*checkpoin pdProto.Notional = b[:] twb := pd.currentEpochTWNotional.Bytes() pdProto.TwNotional = twb[:] + + pb := pd.price.Bytes() + pdProto.Price = pb[:] data = append(data, pdProto) } return data @@ -295,6 +326,8 @@ func (mt *marketTracker) IntoProto(market string) *checkpoint.MarketActivityTrac LpFees: marketFeesToProto(mt.lpFees), InfraFees: marketFeesToProto(mt.infraFees), LpPaidFees: marketFeesToProto(mt.lpPaidFees), + BuyBackFees: marketFeesToProto(mt.buybackFeesPaid), + TreasuryFees: marketFeesToProto(mt.treasuryFeesPaid), Proposer: mt.proposer, BonusPaid: paid, ValueTraded: mt.valueTraded.String(), @@ -339,6 +372,7 @@ func (mat *MarketActivityTracker) serialiseFeesTracker() *snapshot.MarketTracker TakerNotionalVolume: takerNotionalToProto(mat.partyTakerNotionalVolume), MarketToPartyTakerNotionalVolume: marketToPartyTakerNotionalToProto(mat.marketToPartyTakerNotionalVolume), EpochTakerFees: epochTakerFeesToProto(mat.takerFeesPaidInEpoch), + GameEligibilityTracker: epochEligitbilityToProto(mat.eligibilityInEpoch), } } @@ -394,6 +428,8 @@ func marketTrackerFromProto(tracker *checkpoint.MarketActivityTracker) *marketTr makerFeesReceived: map[string]*num.Uint{}, makerFeesPaid: map[string]*num.Uint{}, lpFees: map[string]*num.Uint{}, + buybackFeesPaid: map[string]*num.Uint{}, + treasuryFeesPaid: map[string]*num.Uint{}, infraFees: map[string]*num.Uint{}, lpPaidFees: map[string]*num.Uint{}, totalMakerFeesReceived: num.UintZero(), @@ -467,6 +503,22 @@ func marketTrackerFromProto(tracker *checkpoint.MarketActivityTracker) *marketTr } } + if len(tracker.BuyBackFees) > 0 { + for _, mf := range tracker.BuyBackFees { + fee, _ := num.UintFromString(mf.Fee, 10) + mft.buybackFeesPaid[mf.Party] = fee + mft.allPartiesCache[mf.Party] = struct{}{} + } + } + + if len(tracker.TreasuryFees) > 0 { + for _, mf := range tracker.TreasuryFees { + fee, _ := num.UintFromString(mf.Fee, 10) + mft.treasuryFeesPaid[mf.Party] = fee + mft.allPartiesCache[mf.Party] = struct{}{} + } + } + if len(tracker.LpPaidFees) > 0 { for _, mf := range tracker.LpPaidFees { fee, _ := num.UintFromString(mf.Fee, 10) @@ -493,6 +545,9 @@ func marketTrackerFromProto(tracker *checkpoint.MarketActivityTracker) *marketTr t: time.Unix(0, tn.Time), currentEpochTWNotional: num.UintFromBytes(tn.TwNotional), } + if len(tn.Price) > 0 { + mft.twNotional[tn.Party].price = num.UintFromBytes(tn.Price) + } mft.allPartiesCache[tn.Party] = struct{}{} } } @@ -636,6 +691,17 @@ func (mat *MarketActivityTracker) restore(tracker *snapshot.MarketTracker) { mat.takerFeesPaidInEpoch = append(mat.takerFeesPaidInEpoch, epochMap) } } + if tracker.GameEligibilityTracker != nil { + for _, get := range tracker.GameEligibilityTracker { + mat.eligibilityInEpoch[get.GameId] = make([]map[string]struct{}, len(get.EpochEligibility)) + for i, epoch := range get.EpochEligibility { + mat.eligibilityInEpoch[get.GameId][i] = make(map[string]struct{}, len(epoch.EligibleParties)) + for _, party := range epoch.EligibleParties { + mat.eligibilityInEpoch[get.GameId][i][party] = struct{}{} + } + } + } + } } // OnEpochRestore is called when the state of the epoch changes, we only care about new epochs starting. diff --git a/core/execution/common/market_activity_tracker_test.go b/core/execution/common/market_activity_tracker_test.go index 61b677ace76..33afecc7b9a 100644 --- a/core/execution/common/market_activity_tracker_test.go +++ b/core/execution/common/market_activity_tracker_test.go @@ -62,7 +62,8 @@ func TestMarketTracker(t *testing.T) { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) tracker.SetEligibilityChecker(&EligibilityChecker{}) tracker.MarketProposed("asset1", "market1", "me") @@ -140,7 +141,7 @@ func TestMarketTracker(t *testing.T) { teams2 := mocks.NewMockTeams(ctrl) balanceChecker2 := mocks.NewMockAccountBalanceChecker(ctrl) broker = bmocks.NewMockBroker(ctrl) - trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams2, balanceChecker2, broker) + trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams2, balanceChecker2, broker, collateralService) pl := snapshotpb.Payload{} require.NoError(t, proto.Unmarshal(state1, &pl)) @@ -160,7 +161,8 @@ func TestRemoveMarket(t *testing.T) { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&EligibilityChecker{}) tracker.MarketProposed("asset1", "market1", "me") @@ -187,7 +189,8 @@ func TestAddRemoveAMM(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&EligibilityChecker{}) tracker.MarketProposed("asset1", "market1", "me") @@ -208,6 +211,105 @@ func TestAddRemoveAMM(t *testing.T) { require.Equal(t, map[string]struct{}{}, tracker.GetAllAMMParties("asset1", nil)) } +func TestCalculateTotalMakerContributionInQuantum(t *testing.T) { + // ctx := context.Background() + epochService := &TestEpochEngine{} + ctrl := gomock.NewController(t) + teams := mocks.NewMockTeams(ctrl) + balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) + balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes() + broker := bmocks.NewMockBroker(ctrl) + broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() + broker.EXPECT().Send(gomock.Any()).AnyTimes() + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) + epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) + tracker.SetEligibilityChecker(&EligibilityChecker{}) + tracker.MarketProposed("asset1", "market1", "me") + tracker.MarketProposed("asset1", "market2", "me2") + tracker.MarketProposed("asset1", "market4", "me4") + tracker.MarketProposed("asset2", "market3", "me3") + + collateralService.EXPECT().GetAssetQuantum("asset1").Return(num.DecimalOne(), nil).AnyTimes() + collateralService.EXPECT().GetAssetQuantum("asset2").Return(num.DecimalTwo(), nil).AnyTimes() + // no fees generated expect empty slice + epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START}) + epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_END}) + + // expect no makers fee contributors + contribution, fraction := tracker.CalculateTotalMakerContributionInQuantum(1) + require.Equal(t, 0, len(fraction)) + require.Equal(t, 0, len(contribution)) + + contribution, fraction = tracker.CalculateTotalMakerContributionInQuantum(2) + require.Equal(t, 0, len(fraction)) + require.Equal(t, 0, len(contribution)) + + epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START}) + + // now lets get some fees paid + // update with a few transfers + transfersM1 := []*types.Transfer{ + {Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(100)}}, + {Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(400)}}, + {Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(900)}}, + {Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(600)}}, + } + tracker.UpdateFeesFromTransfers("asset1", "market1", transfersM1) + + transfersM2 := []*types.Transfer{ + {Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(500)}}, + } + tracker.UpdateFeesFromTransfers("asset1", "market2", transfersM2) + + transfersM3 := []*types.Transfer{ + {Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(450)}}, + } + tracker.UpdateFeesFromTransfers("asset2", "market3", transfersM3) + epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_END}) + + contribution, fraction = tracker.CalculateTotalMakerContributionInQuantum(1) + // party 1 received in total: + // asset1 : 1000 + // party 2 received in total: + // asset1 : 1500 + // asset2: 450/2 = 225 + require.Equal(t, 2, len(fraction)) + require.Equal(t, 2, len(contribution)) + require.Equal(t, "1000", contribution["party1"].String()) + require.Equal(t, "1725", contribution["party2"].String()) + require.Equal(t, "0.3669724770642202", fraction["party1"].String()) + require.Equal(t, "0.6330275229357798", fraction["party2"].String()) + + epochService.target(context.Background(), types.Epoch{Seq: 3, Action: vgproto.EpochAction_EPOCH_ACTION_START}) + + transfersM4 := []*types.Transfer{ + {Owner: "party1", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset1", Amount: num.NewUint(2000)}}, + } + tracker.UpdateFeesFromTransfers("asset1", "market2", transfersM4) + + transfersM5 := []*types.Transfer{ + {Owner: "party2", Type: types.TransferTypeMakerFeeReceive, Amount: &types.FinancialAmount{Asset: "asset2", Amount: num.NewUint(550)}}, + } + tracker.UpdateFeesFromTransfers("asset2", "market3", transfersM5) + epochService.target(context.Background(), types.Epoch{Seq: 3, Action: vgproto.EpochAction_EPOCH_ACTION_END}) + + // party 1 received in total this epoch: + // asset1 : 2000 + // party 2 received in total: + // asset2: 550/2 = 275 + // in total for both epochs: + // party1 = 3000 + // party2 = 2000 + contribution, fraction = tracker.CalculateTotalMakerContributionInQuantum(2) + require.Equal(t, 2, len(fraction)) + require.Equal(t, 2, len(contribution)) + require.Equal(t, "3000", contribution["party1"].String()) + require.Equal(t, "2000", contribution["party2"].String()) + require.Equal(t, "0.6", fraction["party1"].String()) + require.Equal(t, "0.4", fraction["party2"].String()) +} + func TestGetScores(t *testing.T) { ctx := context.Background() epochService := &TestEpochEngine{} @@ -218,7 +320,8 @@ func TestGetScores(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&EligibilityChecker{}) tracker.MarketProposed("asset1", "market1", "me") @@ -368,7 +471,8 @@ func TestGetScoresIndividualsDifferentScopes(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&EligibilityChecker{}) tracker.MarketProposed("asset1", "market1", "me") @@ -530,7 +634,8 @@ func TestMarketTrackerStateChange(t *testing.T) { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) tracker.SetEligibilityChecker(&EligibilityChecker{}) state1, _, err := tracker.GetState(key) @@ -561,7 +666,8 @@ func TestFeesTrackerWith0(t *testing.T) { balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes() broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().Send(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochEngine.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START}) @@ -595,7 +701,8 @@ func TestGetLastEpochTakeFees(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochEngine.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START}) tracker.SetEligibilityChecker(&EligibilityChecker{}) @@ -659,7 +766,8 @@ func TestGetLastEpochTakeFeesMultiEpochWindow(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochEngine.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START}) tracker.SetEligibilityChecker(&EligibilityChecker{}) @@ -749,7 +857,8 @@ func TestFeesTracker(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().Send(gomock.Any()).AnyTimes() broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochEngine.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochEngine.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START}) tracker.SetEligibilityChecker(&EligibilityChecker{}) @@ -851,7 +960,7 @@ func TestFeesTracker(t *testing.T) { balanceChecker = mocks.NewMockAccountBalanceChecker(ctrl) balanceChecker.EXPECT().GetAvailableBalance(gomock.Any()).Return(num.UintZero(), nil).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() - trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochEngineLoad.NotifyOnEpoch(trackerLoad.OnEpochEvent, trackerLoad.OnEpochRestore) pl := snapshotpb.Payload{} @@ -935,7 +1044,8 @@ func TestSnapshot(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) pl := snapshotpb.Payload{} require.NoError(t, proto.Unmarshal(state1, &pl)) @@ -958,7 +1068,8 @@ func TestCheckpoint(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + trackerLoad := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) require.NoError(t, trackerLoad.Load(context.Background(), b)) @@ -1021,7 +1132,8 @@ func TestSnapshotRoundTripViaEngine(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker2 := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker2 := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) snapshotEngine2, err := snp.NewEngine(vegaPath, config, log, timeService, statsData.Blockchain) require.NoError(t, err) defer snapshotEngine2.Close() @@ -1059,7 +1171,8 @@ func TestMarketProposerBonusScenarios(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&EligibilityChecker{}) @@ -1182,7 +1295,7 @@ func TestMarketProposerBonusScenarios(t *testing.T) { require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "jeremy"))) } -func TestPositionMetric(t *testing.T) { +func TestNotionalMetric(t *testing.T) { epochService := &TestEpochEngine{} ctx := context.Background() ctrl := gomock.NewController(t) @@ -1192,7 +1305,8 @@ func TestPositionMetric(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochStartTime := time.Now() @@ -1211,29 +1325,29 @@ func TestPositionMetric(t *testing.T) { // pBar = (1 - 600/1000) * 75 + 200 * 600/1000 = 150 epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: epochStartTime, EndTime: epochStartTime.Add(1000 * time.Second)}) - scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1}) + scores := tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1}) require.Equal(t, 1, len(scores)) require.Equal(t, "p1", scores[0].Party) - require.Equal(t, "150", scores[0].Score.String()) + require.Equal(t, "0.000027", scores[0].Score.String()) - scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2}) + scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2}) require.Equal(t, 1, len(scores)) require.Equal(t, "p1", scores[0].Party) - require.Equal(t, "75", scores[0].Score.String()) + require.Equal(t, "0.0000135", scores[0].Score.String()) - scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3}) + scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3}) require.Equal(t, 1, len(scores)) require.Equal(t, "p1", scores[0].Party) - require.Equal(t, "50", scores[0].Score.String()) + require.Equal(t, "0.000009", scores[0].Score.String()) // qualifying the market to m1, expect the same result - scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3, Markets: []string{"m1"}}) + scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3, Markets: []string{"m1"}}) require.Equal(t, 1, len(scores)) require.Equal(t, "p1", scores[0].Party) - require.Equal(t, "50", scores[0].Score.String()) + require.Equal(t, "0.000009", scores[0].Score.String()) // qualifying the market to m2, expect the same result - scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3, Markets: []string{"m2"}}) + scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 3, Markets: []string{"m2"}}) require.Equal(t, 0, len(scores)) epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: epochStartTime.Add(1000 * time.Second)}) @@ -1245,29 +1359,29 @@ func TestPositionMetric(t *testing.T) { // end the epoch // pBar = 0.6 * 200 + 0.4 * 100 = 160 epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: epochStartTime.Add(1000 * time.Second), EndTime: epochStartTime.Add(2000 * time.Second)}) - scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1}) + scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1}) require.Equal(t, 1, len(scores)) require.Equal(t, "p1", scores[0].Party) - require.Equal(t, "160", scores[0].Score.String()) + require.Equal(t, "0.000036", scores[0].Score.String()) - scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2}) + scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2}) require.Equal(t, 1, len(scores)) require.Equal(t, "p1", scores[0].Party) - require.Equal(t, "155", scores[0].Score.String()) + require.Equal(t, "0.0000315", scores[0].Score.String()) - scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 5}) + scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 5}) require.Equal(t, 1, len(scores)) require.Equal(t, "p1", scores[0].Party) - require.Equal(t, "62", scores[0].Score.String()) + require.Equal(t, "0.0000126", scores[0].Score.String()) // qualify to m1 - scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 5, Markets: []string{"m1"}}) + scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 5, Markets: []string{"m1"}}) require.Equal(t, 1, len(scores)) require.Equal(t, "p1", scores[0].Party) - require.Equal(t, "62", scores[0].Score.String()) + require.Equal(t, "0.0000126", scores[0].Score.String()) // qualify to m2 - scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 5, Markets: []string{"m2"}}) + scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 5, Markets: []string{"m2"}}) require.Equal(t, 0, len(scores)) // now lets lets at an epoch with no activity @@ -1275,20 +1389,20 @@ func TestPositionMetric(t *testing.T) { // end the epoch // pBar = 0 * 160 + 1 * 100 = 100 epochService.target(context.Background(), types.Epoch{Action: vgproto.EpochAction_EPOCH_ACTION_END, StartTime: epochStartTime.Add(2000 * time.Second), EndTime: epochStartTime.Add(3000 * time.Second)}) - scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1}) + scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1}) require.Equal(t, 1, len(scores)) require.Equal(t, "p1", scores[0].Party) - require.Equal(t, "100", scores[0].Score.String()) + require.Equal(t, "0.00003", scores[0].Score.String()) - scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2}) + scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 2}) require.Equal(t, 1, len(scores)) require.Equal(t, "p1", scores[0].Party) - require.Equal(t, "130", scores[0].Score.String()) + require.Equal(t, "0.000033", scores[0].Score.String()) - scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 4}) + scores = tracker.CalculateMetricForIndividuals(ctx, &vgproto.DispatchStrategy{AssetForMetric: "a1", Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 4}) require.Equal(t, 1, len(scores)) require.Equal(t, "p1", scores[0].Party) - require.Equal(t, "102.5", scores[0].Score.String()) + require.Equal(t, "0.00002325", scores[0].Score.String()) } func TestRealisedReturnMetric(t *testing.T) { @@ -1301,7 +1415,8 @@ func TestRealisedReturnMetric(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochStartTime := time.Now() @@ -1405,7 +1520,8 @@ func TestRelativeReturnMetric(t *testing.T) { broker := bmocks.NewMockBroker(ctrl) broker.EXPECT().Send(gomock.Any()).AnyTimes() broker.EXPECT().SendBatch(gomock.Any()).AnyTimes() - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochStartTime := time.Now() @@ -1494,7 +1610,8 @@ func TestTeamStatsForMarkets(t *testing.T) { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) asset1 := vgrand.RandomStr(5) asset2 := vgrand.RandomStr(5) @@ -1606,8 +1723,8 @@ func setupDefaultTrackerForTest(t *testing.T) *common.MarketActivityTracker { teams := mocks.NewMockTeams(ctrl) balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - - tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + collateralService := mocks.NewMockCollateral(ctrl) + tracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, collateralService) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) epochStartTime := time.Now() diff --git a/core/execution/common/mat_intermediate_scores_internal_test.go b/core/execution/common/mat_intermediate_scores_internal_test.go index a60ffc9f120..babb3a5d1f8 100644 --- a/core/execution/common/mat_intermediate_scores_internal_test.go +++ b/core/execution/common/mat_intermediate_scores_internal_test.go @@ -32,7 +32,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestPublishGameMetricAveragePosition(t *testing.T) { +func TestPublishGameMetricAverageNotional(t *testing.T) { ctx := context.Background() epochService := &DummyEpochEngine{} ctrl := gomock.NewController(t) @@ -47,7 +47,7 @@ func TestPublishGameMetricAveragePosition(t *testing.T) { gameScoreEvents = append(gameScoreEvents, evt) } }).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -78,7 +78,7 @@ func TestPublishGameMetricAveragePosition(t *testing.T) { ds1 := &vgproto.DispatchStrategy{ AssetForMetric: "a1", - Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, Markets: []string{"m1"}, EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, @@ -87,7 +87,7 @@ func TestPublishGameMetricAveragePosition(t *testing.T) { } ds2 := &vgproto.DispatchStrategy{ AssetForMetric: "a1", - Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, Markets: []string{"m2"}, EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, @@ -96,7 +96,7 @@ func TestPublishGameMetricAveragePosition(t *testing.T) { } ds3 := &vgproto.DispatchStrategy{ AssetForMetric: "a1", - Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, Markets: []string{"m3"}, EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, @@ -105,7 +105,7 @@ func TestPublishGameMetricAveragePosition(t *testing.T) { } ds4 := &vgproto.DispatchStrategy{ AssetForMetric: "a1", - Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, Markets: []string{"m1", "m2"}, EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, @@ -114,7 +114,7 @@ func TestPublishGameMetricAveragePosition(t *testing.T) { } ds5 := &vgproto.DispatchStrategy{ AssetForMetric: "a1", - Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_POSITION, + Metric: vgproto.DispatchMetric_DISPATCH_METRIC_AVERAGE_NOTIONAL, EntityScope: vgproto.EntityScope_ENTITY_SCOPE_INDIVIDUALS, IndividualScope: vgproto.IndividualScope_INDIVIDUAL_SCOPE_ALL, WindowLength: 1, @@ -127,32 +127,32 @@ func TestPublishGameMetricAveragePosition(t *testing.T) { ps1 := gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps1[0].Party) require.Equal(t, "p2", ps1[1].Party) - require.Equal(t, "9.166666", ps1[0].Score) - require.Equal(t, "75", ps1[1].Score) + require.Equal(t, "0.0000009", ps1[0].Score) + require.Equal(t, "0.000075", ps1[1].Score) ps2 := gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps2[0].Party) require.Equal(t, "p2", ps2[1].Party) - require.Equal(t, "13.333332", ps2[0].Score) - require.Equal(t, "116.66666", ps2[1].Score) + require.Equal(t, "0.0000026", ps2[0].Score) + require.Equal(t, "0.0002333", ps2[1].Score) ps3 := gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps3[0].Party) require.Equal(t, "p2", ps3[1].Party) - require.Equal(t, "15", ps3[0].Score) - require.Equal(t, "75", ps3[1].Score) + require.Equal(t, "0.0000045", ps3[0].Score) + require.Equal(t, "0.000225", ps3[1].Score) ps4 := gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps4[0].Party) require.Equal(t, "p2", ps4[1].Party) - require.Equal(t, "22.499998", ps4[0].Score) - require.Equal(t, "191.66666", ps4[1].Score) + require.Equal(t, "0.0000035", ps4[0].Score) + require.Equal(t, "0.0003083", ps4[1].Score) ps5 := gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps5[0].Party) require.Equal(t, "p2", ps5[1].Party) - require.Equal(t, "37.499998", ps5[0].Score) - require.Equal(t, "266.66666", ps5[1].Score) + require.Equal(t, "0.000008", ps5[0].Score) + require.Equal(t, "0.0005333", ps5[1].Score) // now we end the epoch and make sure that we get the exact same results gameScoreEvents = []events.Event{} @@ -164,32 +164,32 @@ func TestPublishGameMetricAveragePosition(t *testing.T) { ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps1[0].Party) require.Equal(t, "p2", ps1[1].Party) - require.Equal(t, "9.166666", ps1[0].Score) - require.Equal(t, "75", ps1[1].Score) + require.Equal(t, "0.0000009", ps1[0].Score) + require.Equal(t, "0.000075", ps1[1].Score) ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps2[0].Party) require.Equal(t, "p2", ps2[1].Party) - require.Equal(t, "13.333332", ps2[0].Score) - require.Equal(t, "116.66666", ps2[1].Score) + require.Equal(t, "0.0000026", ps2[0].Score) + require.Equal(t, "0.0002333", ps2[1].Score) ps3 = gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps3[0].Party) require.Equal(t, "p2", ps3[1].Party) - require.Equal(t, "15", ps3[0].Score) - require.Equal(t, "75", ps3[1].Score) + require.Equal(t, "0.0000045", ps3[0].Score) + require.Equal(t, "0.000225", ps3[1].Score) ps4 = gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps4[0].Party) require.Equal(t, "p2", ps4[1].Party) - require.Equal(t, "22.499998", ps4[0].Score) - require.Equal(t, "191.66666", ps4[1].Score) + require.Equal(t, "0.0000035", ps4[0].Score) + require.Equal(t, "0.0003083", ps4[1].Score) ps5 = gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps5[0].Party) require.Equal(t, "p2", ps5[1].Party) - require.Equal(t, "37.499998", ps5[0].Score) - require.Equal(t, "266.66666", ps5[1].Score) + require.Equal(t, "0.000008", ps5[0].Score) + require.Equal(t, "0.0005333", ps5[1].Score) // start epoch 2 and record some activity epochService.target(context.Background(), types.Epoch{Seq: 2, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(60, 0)}) @@ -206,32 +206,32 @@ func TestPublishGameMetricAveragePosition(t *testing.T) { ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps1[0].Party) require.Equal(t, "p2", ps1[1].Party) - require.Equal(t, "15", ps1[0].Score) - require.Equal(t, "100", ps1[1].Score) + require.Equal(t, "0.0000055", ps1[0].Score) + require.Equal(t, "0.0001", ps1[1].Score) ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps2[0].Party) require.Equal(t, "p2", ps2[1].Party) - require.Equal(t, "20", ps2[0].Score) - require.Equal(t, "57.5", ps2[1].Score) + require.Equal(t, "0.000004", ps2[0].Score) + require.Equal(t, "0.0001075", ps2[1].Score) ps3 = gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps3[0].Party) require.Equal(t, "p2", ps3[1].Party) - require.Equal(t, "30", ps3[0].Score) - require.Equal(t, "300", ps3[1].Score) + require.Equal(t, "0.000009", ps3[0].Score) + require.Equal(t, "0.0009", ps3[1].Score) ps4 = gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps4[0].Party) require.Equal(t, "p2", ps4[1].Party) - require.Equal(t, "35", ps4[0].Score) - require.Equal(t, "157.5", ps4[1].Score) + require.Equal(t, "0.0000095", ps4[0].Score) + require.Equal(t, "0.0002075", ps4[1].Score) ps5 = gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps5[0].Party) require.Equal(t, "p2", ps5[1].Party) - require.Equal(t, "65", ps5[0].Score) - require.Equal(t, "457.5", ps5[1].Score) + require.Equal(t, "0.0000185", ps5[0].Score) + require.Equal(t, "0.0011075", ps5[1].Score) // now lets change the window to 2: ds1.WindowLength = 2 @@ -247,32 +247,32 @@ func TestPublishGameMetricAveragePosition(t *testing.T) { ps1 = gameScoreEvents[0].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps1[0].Party) require.Equal(t, "p2", ps1[1].Party) - require.Equal(t, "12.083333", ps1[0].Score) - require.Equal(t, "87.5", ps1[1].Score) + require.Equal(t, "0.0000032", ps1[0].Score) + require.Equal(t, "0.0000875", ps1[1].Score) ps2 = gameScoreEvents[1].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps2[0].Party) require.Equal(t, "p2", ps2[1].Party) - require.Equal(t, "16.666666", ps2[0].Score) - require.Equal(t, "87.08333", ps2[1].Score) + require.Equal(t, "0.0000033", ps2[0].Score) + require.Equal(t, "0.0001704", ps2[1].Score) ps3 = gameScoreEvents[2].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps3[0].Party) require.Equal(t, "p2", ps3[1].Party) - require.Equal(t, "22.5", ps3[0].Score) - require.Equal(t, "187.5", ps3[1].Score) + require.Equal(t, "0.00000675", ps3[0].Score) + require.Equal(t, "0.0005625", ps3[1].Score) ps4 = gameScoreEvents[3].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps4[0].Party) require.Equal(t, "p2", ps4[1].Party) - require.Equal(t, "28.749999", ps4[0].Score) - require.Equal(t, "174.58333", ps4[1].Score) + require.Equal(t, "0.0000065", ps4[0].Score) + require.Equal(t, "0.0002579", ps4[1].Score) ps5 = gameScoreEvents[4].StreamMessage().GetGameScores().PartyScores require.Equal(t, "p1", ps5[0].Party) require.Equal(t, "p2", ps5[1].Party) - require.Equal(t, "51.249999", ps5[0].Score) - require.Equal(t, "362.08333", ps5[1].Score) + require.Equal(t, "0.00001325", ps5[0].Score) + require.Equal(t, "0.0008204", ps5[1].Score) } func TestPublishGameMetricReturnVolatility(t *testing.T) { @@ -289,7 +289,7 @@ func TestPublishGameMetricReturnVolatility(t *testing.T) { gameScoreEvents = append(gameScoreEvents, evt) } }).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Time{}}) @@ -537,7 +537,7 @@ func TestPublishGameMetricRelativeReturn(t *testing.T) { gameScoreEvents = append(gameScoreEvents, evt) } }).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(0, 0)}) @@ -757,7 +757,7 @@ func TestPublishGameMetricRealisedReturn(t *testing.T) { gameScoreEvents = append(gameScoreEvents, evt) } }).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(0, 0)}) @@ -879,7 +879,7 @@ func TestPublishGameMetricFees(t *testing.T) { gameScoreEvents = append(gameScoreEvents, evt) } }).AnyTimes() - tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker) + tracker := NewMarketActivityTracker(logging.NewTestLogger(), teams, balanceChecker, broker, DummyCollateralEngine{}) epochService.NotifyOnEpoch(tracker.OnEpochEvent, tracker.OnEpochRestore) tracker.SetEligibilityChecker(&DummyEligibilityChecker{}) epochService.target(context.Background(), types.Epoch{Seq: 1, Action: vgproto.EpochAction_EPOCH_ACTION_START, StartTime: time.Unix(0, 0)}) diff --git a/core/execution/common/mocks/mocks.go b/core/execution/common/mocks/mocks.go index f64b6a4cada..d2888956520 100644 --- a/core/execution/common/mocks/mocks.go +++ b/core/execution/common/mocks/mocks.go @@ -474,6 +474,20 @@ func (mr *MockCollateralMockRecorder) FinalSettlement(arg0, arg1, arg2, arg3, ar return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinalSettlement", reflect.TypeOf((*MockCollateral)(nil).FinalSettlement), arg0, arg1, arg2, arg3, arg4) } +// GetAllParties mocks base method. +func (m *MockCollateral) GetAllParties() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllParties") + ret0, _ := ret[0].([]string) + return ret0 +} + +// GetAllParties indicates an expected call of GetAllParties. +func (mr *MockCollateralMockRecorder) GetAllParties() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllParties", reflect.TypeOf((*MockCollateral)(nil).GetAllParties)) +} + // GetAssetQuantum mocks base method. func (m *MockCollateral) GetAssetQuantum(arg0 string) (decimal.Decimal, error) { m.ctrl.T.Helper() @@ -2645,6 +2659,20 @@ func (m *MockAccountBalanceChecker) EXPECT() *MockAccountBalanceCheckerMockRecor return m.recorder } +// GetAllStakingParties mocks base method. +func (m *MockAccountBalanceChecker) GetAllStakingParties() []string { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "GetAllStakingParties") + ret0, _ := ret[0].([]string) + return ret0 +} + +// GetAllStakingParties indicates an expected call of GetAllStakingParties. +func (mr *MockAccountBalanceCheckerMockRecorder) GetAllStakingParties() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllStakingParties", reflect.TypeOf((*MockAccountBalanceChecker)(nil).GetAllStakingParties)) +} + // GetAvailableBalance mocks base method. func (m *MockAccountBalanceChecker) GetAvailableBalance(arg0 string) (*num.Uint, error) { m.ctrl.T.Helper() diff --git a/core/execution/engine.go b/core/execution/engine.go index e1b1bf5a707..37203b347a1 100644 --- a/core/execution/engine.go +++ b/core/execution/engine.go @@ -86,8 +86,10 @@ type Engine struct { assets common.Assets referralDiscountRewardService fee.ReferralDiscountRewardService volumeDiscountService fee.VolumeDiscountService - banking common.Banking - parties common.Parties + volumeRebateService fee.VolumeRebateService + + banking common.Banking + parties common.Parties broker common.Broker timeService common.TimeService @@ -137,6 +139,7 @@ func NewEngine( assets common.Assets, referralDiscountRewardService fee.ReferralDiscountRewardService, volumeDiscountService fee.VolumeDiscountService, + volumeRebateService fee.VolumeRebateService, banking common.Banking, parties common.Parties, delayTransactionsTarget common.DelayTransactionsTarget, @@ -165,9 +168,11 @@ func NewEngine( skipRestoreSuccessors: map[string]struct{}{}, referralDiscountRewardService: referralDiscountRewardService, volumeDiscountService: volumeDiscountService, - banking: banking, - parties: parties, - delayTransactionsTarget: delayTransactionsTarget, + volumeRebateService: volumeRebateService, + + banking: banking, + parties: parties, + delayTransactionsTarget: delayTransactionsTarget, } // set the eligibility for proposer bonus checker @@ -707,6 +712,7 @@ func (e *Engine) submitMarket(ctx context.Context, marketConfig *types.Market, o e.peggedOrderCountUpdated, e.referralDiscountRewardService, e.volumeDiscountService, + e.volumeRebateService, e.banking, e.parties, ) @@ -790,6 +796,7 @@ func (e *Engine) submitSpotMarket(ctx context.Context, marketConfig *types.Marke e.peggedOrderCountUpdated, e.referralDiscountRewardService, e.volumeDiscountService, + e.volumeRebateService, e.banking, ) if err != nil { @@ -1450,7 +1457,7 @@ func (e *Engine) GetMarket(market string, settled bool) (types.Market, bool) { // party in the given market. If the market doesn't exist, it returns false. func (e *Engine) GetEquityLikeShareForMarketAndParty(market, party string) (num.Decimal, bool) { if mkt, ok := e.allMarkets[market]; ok { - return mkt.GetEquityShares().SharesFromParty(party), true + return mkt.GetEquitySharesForParty(party), true } return num.DecimalZero(), false } diff --git a/core/execution/engine_netparams.go b/core/execution/engine_netparams.go index 85b3033d7ba..c0580f1c1e6 100644 --- a/core/execution/engine_netparams.go +++ b/core/execution/engine_netparams.go @@ -35,6 +35,8 @@ type netParamsValues struct { suppliedStakeToObligationFactor num.Decimal infrastructureFee num.Decimal makerFee num.Decimal + treasuryFee num.Decimal + buyBackFee num.Decimal scalingFactors *types.ScalingFactors maxLiquidityFee num.Decimal bondPenaltyFactor num.Decimal @@ -76,6 +78,8 @@ func defaultNetParamsValues() netParamsValues { suppliedStakeToObligationFactor: num.DecimalFromInt64(-1), infrastructureFee: num.DecimalFromInt64(-1), makerFee: num.DecimalFromInt64(-1), + buyBackFee: num.DecimalFromInt64(-1), + treasuryFee: num.DecimalFromInt64(-1), scalingFactors: nil, maxLiquidityFee: num.DecimalFromInt64(-1), bondPenaltyFactor: num.DecimalFromInt64(-1), @@ -329,6 +333,34 @@ func (e *Engine) OnMarketFeeFactorsMakerFeeUpdate(ctx context.Context, d num.Dec return nil } +func (e *Engine) OnMarketFeeFactorsTreasuryFeeUpdate(ctx context.Context, d num.Decimal) error { + if e.log.IsDebug() { + e.log.Debug("update treasury fee in market fee factors", + logging.Decimal("treasury-fee", d), + ) + } + + for _, mkt := range e.allMarketsCpy { + mkt.OnFeeFactorsTreasuryFeeUpdate(ctx, d) + } + e.npv.treasuryFee = d + return nil +} + +func (e *Engine) OnMarketFeeFactorsBuyBackFeeUpdate(ctx context.Context, d num.Decimal) error { + if e.log.IsDebug() { + e.log.Debug("update buy back fee in market fee factors", + logging.Decimal("buy-back-fee", d), + ) + } + + for _, mkt := range e.allMarketsCpy { + mkt.OnFeeFactorsBuyBackFeeUpdate(ctx, d) + } + e.npv.buyBackFee = d + return nil +} + func (e *Engine) OnMarketFeeFactorsInfrastructureFeeUpdate(ctx context.Context, d num.Decimal) error { if e.log.IsDebug() { e.log.Debug("update infrastructure fee in market fee factors", @@ -503,6 +535,14 @@ func (e *Engine) propagateSpotInitialNetParams(ctx context.Context, mkt *spot.Ma mkt.OnFeeFactorsMakerFeeUpdate(ctx, e.npv.makerFee) } + if !e.npv.buyBackFee.Equal(num.DecimalFromInt64(-1)) { + mkt.OnFeeFactorsBuyBackFeeUpdate(ctx, e.npv.buyBackFee) + } + + if !e.npv.treasuryFee.Equal(num.DecimalFromInt64(-1)) { + mkt.OnFeeFactorsTreasuryFeeUpdate(ctx, e.npv.treasuryFee) + } + if e.npv.marketValueWindowLength != -1 { mkt.OnMarketValueWindowLengthUpdate(e.npv.marketValueWindowLength) } @@ -567,6 +607,14 @@ func (e *Engine) propagateInitialNetParamsToFutureMarket(ctx context.Context, mk mkt.OnFeeFactorsMakerFeeUpdate(ctx, e.npv.makerFee) } + if !e.npv.buyBackFee.Equal(num.DecimalFromInt64(-1)) { + mkt.OnFeeFactorsBuyBackFeeUpdate(ctx, e.npv.buyBackFee) + } + + if !e.npv.treasuryFee.Equal(num.DecimalFromInt64(-1)) { + mkt.OnFeeFactorsTreasuryFeeUpdate(ctx, e.npv.treasuryFee) + } + if e.npv.scalingFactors != nil { if err := mkt.OnMarginScalingFactorsUpdate(ctx, e.npv.scalingFactors); err != nil { return err diff --git a/core/execution/engine_snapshot.go b/core/execution/engine_snapshot.go index 05a56317fef..3ec884cf124 100644 --- a/core/execution/engine_snapshot.go +++ b/core/execution/engine_snapshot.go @@ -138,6 +138,7 @@ func (e *Engine) restoreSpotMarket(ctx context.Context, em *types.ExecSpotMarket e.peggedOrderCountUpdated, e.referralDiscountRewardService, e.volumeDiscountService, + e.volumeRebateService, e.banking, ) if err != nil { @@ -215,6 +216,7 @@ func (e *Engine) restoreMarket(ctx context.Context, em *types.ExecMarket) (*futu e.peggedOrderCountUpdated, e.referralDiscountRewardService, e.volumeDiscountService, + e.volumeRebateService, e.banking, e.parties, ) diff --git a/core/execution/engine_snapshot_test.go b/core/execution/engine_snapshot_test.go index 9453f95d09b..ed67607aa7b 100644 --- a/core/execution/engine_snapshot_test.go +++ b/core/execution/engine_snapshot_test.go @@ -90,17 +90,18 @@ func getMockedEngine(t *testing.T) *engineFake { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl) + volumeRebate := fmock.NewMockVolumeRebateService(ctrl) - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() banking := mocks.NewMockBanking(ctrl) parties := mocks.NewMockParties(ctrl) delayTarget := mocks.NewMockDelayTransactionsTarget(ctrl) delayTarget.EXPECT().MarketDelayRequiredUpdated(gomock.Any(), gomock.Any()).AnyTimes() - mat := common.NewMarketActivityTracker(log, teams, balanceChecker, broker) - exec := execution.NewEngine(log, execConfig, timeService, collateralService, oracleService, broker, statevar, mat, asset, referralDiscountReward, volumeDiscount, banking, parties, delayTarget) + mat := common.NewMarketActivityTracker(log, teams, balanceChecker, broker, collateralService) + exec := execution.NewEngine(log, execConfig, timeService, collateralService, oracleService, broker, statevar, mat, asset, referralDiscountReward, volumeDiscount, volumeRebate, banking, parties, delayTarget) epochEngine.NotifyOnEpoch(mat.OnEpochEvent, mat.OnEpochRestore) return &engineFake{ Engine: exec, @@ -159,16 +160,17 @@ func createEngine(t *testing.T) (*execution.Engine, *gomock.Controller) { balanceChecker := mocks.NewMockAccountBalanceChecker(ctrl) referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl) - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebate := fmock.NewMockVolumeRebateService(ctrl) + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() - mat := common.NewMarketActivityTracker(log, teams, balanceChecker, broker) + mat := common.NewMarketActivityTracker(log, teams, balanceChecker, broker, collateralService) banking := mocks.NewMockBanking(ctrl) parties := mocks.NewMockParties(ctrl) delayTarget := mocks.NewMockDelayTransactionsTarget(ctrl) delayTarget.EXPECT().MarketDelayRequiredUpdated(gomock.Any(), gomock.Any()).AnyTimes() - e := execution.NewEngine(log, executionConfig, timeService, collateralService, oracleService, broker, statevar, mat, asset, referralDiscountReward, volumeDiscount, banking, parties, delayTarget) + e := execution.NewEngine(log, executionConfig, timeService, collateralService, oracleService, broker, statevar, mat, asset, referralDiscountReward, volumeDiscount, volumeRebate, banking, parties, delayTarget) epochEngine.NotifyOnEpoch(mat.OnEpochEvent, mat.OnEpochRestore) return e, ctrl } diff --git a/core/execution/future/market.go b/core/execution/future/market.go index 663b89b106d..06f60dc6222 100644 --- a/core/execution/future/market.go +++ b/core/execution/future/market.go @@ -96,6 +96,7 @@ type Market struct { fee *fee.Engine referralDiscountRewardService fee.ReferralDiscountRewardService volumeDiscountService fee.VolumeDiscountService + volumeRebateService fee.VolumeRebateService liquidity *common.MarketLiquidity liquidityEngine common.LiquidityEngine @@ -197,6 +198,7 @@ func NewMarket( peggedOrderNotify func(int64), referralDiscountRewardService fee.ReferralDiscountRewardService, volumeDiscountService fee.VolumeDiscountService, + volumeRebateService fee.VolumeRebateService, banking common.Banking, parties common.Parties, ) (*Market, error) { @@ -347,6 +349,7 @@ func NewMarket( perp: marketType == types.MarketTypePerp, referralDiscountRewardService: referralDiscountRewardService, volumeDiscountService: volumeDiscountService, + volumeRebateService: volumeRebateService, partyMarginFactor: map[string]num.Decimal{}, banking: banking, markPriceCalculator: common.NewCompositePriceCalculator(ctx, mkt.MarkPriceConfiguration, oracleEngine, timeService), @@ -583,6 +586,14 @@ func (m *Market) GetEquityShares() *common.EquityShares { return m.equityShares } +func (m *Market) GetEquitySharesForParty(partyID string) num.Decimal { + primary := m.equityShares.SharesFromParty(partyID) + if sub, err := m.amm.GetAMMParty(partyID); err == nil { + return primary.Add(m.equityShares.SharesFromParty(sub)) + } + return primary +} + func (m *Market) ResetParentIDAndInsurancePoolFraction() { m.mkt.ParentMarketID = "" m.mkt.InsurancePoolFraction = num.DecimalZero() @@ -1002,6 +1013,7 @@ func (m *Market) PostRestore(ctx context.Context) error { } } } + m.marketActivityTracker.RestoreMarkPrice(m.settlementAsset, m.mkt.ID, m.getCurrentMarkPrice()) // Disposal slippage was set as part of this upgrade, send event to ensure datanode is updated. if vegacontext.InProgressUpgradeFromMultiple(ctx, "v0.75.8", "v0.75.7") { @@ -1153,6 +1165,7 @@ func (m *Market) BlockEnd(ctx context.Context) { m.risk.GetRiskFactors().Long, true, false) m.markPriceLock.Unlock() + m.marketActivityTracker.UpdateMarkPrice(m.settlementAsset, m.mkt.ID, m.getCurrentMarkPrice()) if err != nil { // start the monitoring auction if required if m.as.AuctionStart() { @@ -1783,6 +1796,7 @@ func (m *Market) leaveAuction(ctx context.Context, now time.Time) { m.markPriceCalculator.OverridePrice(m.lastTradedPrice) m.pMonitor.ResetPriceHistory(m.lastTradedPrice) } + m.marketActivityTracker.UpdateMarkPrice(m.settlementAsset, m.mkt.ID, m.getCurrentMarkPrice()) if m.perp { if m.internalCompositePriceCalculator != nil { m.internalCompositePriceCalculator.CalculateBookMarkPriceAtTimeT(m.tradableInstrument.MarginCalculator.ScalingFactors.InitialMargin, m.mkt.LinearSlippageFactor, m.risk.GetRiskFactors().Short, m.risk.GetRiskFactors().Long, t, m.matching) @@ -2138,43 +2152,13 @@ func (m *Market) SubmitStopOrdersWithIDGeneratorAndOrderIDs( } // now check for the parties position - positions := m.position.GetPositionsByParty(party) - if len(positions) > 1 { + if positions := m.position.GetPositionsByParty(party); len(positions) > 1 { m.log.Panic("only one position expected", logging.Int("got", len(positions))) - } - - if len(positions) < 1 { + } else if len(positions) < 1 { rejectStopOrders(types.StopOrderRejectionNotAllowedWithoutAPosition, fallsBelow, risesAbove) return nil, common.ErrStopOrderSubmissionNotAllowedWithoutExistingPosition } - pos := positions[0] - - // now we will reject if the direction of order if is not - // going to close the position or potential position - potentialSize := pos.Size() - pos.Sell() + pos.Buy() - size := pos.Size() - - var stopOrderSide types.Side - if fallsBelow != nil { - stopOrderSide = fallsBelow.OrderSubmission.Side - } else { - stopOrderSide = risesAbove.OrderSubmission.Side - } - - switch stopOrderSide { - case types.SideBuy: - if potentialSize >= 0 && size >= 0 { - rejectStopOrders(types.StopOrderRejectionNotClosingThePosition, fallsBelow, risesAbove) - return nil, common.ErrStopOrderSideNotClosingThePosition - } - case types.SideSell: - if potentialSize <= 0 && size <= 0 { - rejectStopOrders(types.StopOrderRejectionNotClosingThePosition, fallsBelow, risesAbove) - return nil, common.ErrStopOrderSideNotClosingThePosition - } - } - fallsBelowTriggered, risesAboveTriggered := m.stopOrderWouldTriggerAtSubmission(fallsBelow), m.stopOrderWouldTriggerAtSubmission(risesAbove) triggered := fallsBelowTriggered || risesAboveTriggered @@ -2712,12 +2696,12 @@ func (m *Market) calcFees(trades []*types.Trade) (events.FeesTransfer, error) { ) if !m.as.InAuction() { - fees, err = m.fee.CalculateForContinuousMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForContinuousMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } else if m.as.IsMonitorAuction() { // we are in auction mode - fees, err = m.fee.CalculateForAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } else if m.as.IsFBA() { - fees, err = m.fee.CalculateForFrequentBatchesAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForFrequentBatchesAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } if err != nil { @@ -4695,6 +4679,7 @@ func (m *Market) terminateMarket(ctx context.Context, finalState types.MarketSta false, false) m.markPriceLock.Unlock() + m.marketActivityTracker.UpdateMarkPrice(m.settlementAsset, m.mkt.ID, m.getCurrentMarkPrice()) if m.internalCompositePriceCalculator != nil { m.internalCompositePriceCalculator.CalculateMarkPrice( @@ -5281,81 +5266,68 @@ func (m *Market) emitPartyMarginModeUpdated(ctx context.Context, party string, m m.broker.Send(events.NewPartyMarginModeUpdatedEvent(ctx, e)) } -func (m *Market) checkOrderAmendForSpam(order *types.Order) error { - rf := num.DecimalOne() - - factor := m.mkt.LinearSlippageFactor - if m.risk.IsRiskFactorInitialised() { - if order.Side == types.SideBuy { - rf = m.risk.GetRiskFactors().Long - } else { - rf = m.risk.GetRiskFactors().Short - } - } - var price *num.Uint - if order.PeggedOrder == nil { - price, _ = num.UintFromDecimal(order.Price.ToDecimal().Mul(m.priceFactor)) - } else { - priceInMarket, _ := num.UintFromDecimal(m.getCurrentMarkPrice().ToDecimal().Div(m.priceFactor)) - if order.Side == types.SideBuy { - priceInMarket.AddSum(order.PeggedOrder.Offset) - } else { - priceInMarket = priceInMarket.Sub(priceInMarket, order.PeggedOrder.Offset) - } - price, _ = num.UintFromDecimal(priceInMarket.ToDecimal().Mul(m.priceFactor)) - } - margins := num.UintZero().Mul(price, num.NewUint(order.TrueRemaining())).ToDecimal().Div(m.positionFactor) - assetQuantum, err := m.collateral.GetAssetQuantum(m.settlementAsset) - if err != nil { - return err - } - if margins.Mul(rf.Add(factor)).Div(assetQuantum).LessThan(m.minMaintenanceMarginQuantumMultiplier.Mul(assetQuantum)) { - return fmt.Errorf("order value is less than minimum maintenance margin for spam") - } - return nil -} - -func (m *Market) CheckOrderSubmissionForSpam(orderSubmission *types.OrderSubmission, party string, quantumMultiplier num.Decimal) error { +func (m *Market) checkOrderForSpam(side types.Side, orderPrice *num.Uint, orderSize uint64, peggedOrder *types.PeggedOrder, orderType vegapb.Order_Type, quantumMultiplier num.Decimal) error { rf := num.DecimalOne() factor := m.mkt.LinearSlippageFactor if m.risk.IsRiskFactorInitialised() { - if orderSubmission.Side == types.SideBuy { + if side == types.SideBuy { rf = m.risk.GetRiskFactors().Long } else { rf = m.risk.GetRiskFactors().Short } } - var price *num.Uint - if orderSubmission.PeggedOrder != nil || orderSubmission.Type == vega.Order_TYPE_MARKET { + if peggedOrder != nil || orderType == vega.Order_TYPE_MARKET { priceInMarket, _ := num.UintFromDecimal(m.getCurrentMarkPrice().ToDecimal().Div(m.priceFactor)) offset := num.UintZero() - if orderSubmission.PeggedOrder != nil { - offset = orderSubmission.PeggedOrder.Offset + if peggedOrder != nil { + offset = peggedOrder.Offset } - if orderSubmission.Side == types.SideBuy { + if side == types.SideBuy { priceInMarket.AddSum(offset) } else { priceInMarket = priceInMarket.Sub(priceInMarket, offset) } price, _ = num.UintFromDecimal(priceInMarket.ToDecimal().Mul(m.priceFactor)) } else { - price, _ = num.UintFromDecimal(orderSubmission.Price.ToDecimal().Mul(m.priceFactor)) + price, _ = num.UintFromDecimal(orderPrice.ToDecimal().Mul(m.priceFactor)) } - margins := num.UintZero().Mul(price, num.NewUint(orderSubmission.Size)).ToDecimal().Div(m.positionFactor) + margins := num.UintZero().Mul(price, num.NewUint(orderSize)).ToDecimal().Div(m.positionFactor) assetQuantum, err := m.collateral.GetAssetQuantum(m.settlementAsset) if err != nil { return err } - if margins.Mul(rf.Add(factor)).LessThan(quantumMultiplier.Mul(assetQuantum)) { - return fmt.Errorf("order value is less than minimum maintenance margin for spam") + value := margins.Mul(rf.Add(factor)) + required := quantumMultiplier.Mul(assetQuantum) + if value.LessThan(required) { + return fmt.Errorf(fmt.Sprintf("order value (%s) is less than minimum maintenance margin for spam (%s)", value.String(), required.String())) } return nil } +func (m *Market) checkOrderAmendForSpam(order *types.Order) error { + return m.checkOrderForSpam( + order.Side, + order.Price, + order.Size, + order.PeggedOrder, + order.Type, + m.minMaintenanceMarginQuantumMultiplier) +} + +func (m *Market) CheckOrderSubmissionForSpam(orderSubmission *types.OrderSubmission, party string, quantumMultiplier num.Decimal) error { + return m.checkOrderForSpam( + orderSubmission.Side, + orderSubmission.Price, + orderSubmission.Size, + orderSubmission.PeggedOrder, + orderSubmission.Type, + quantumMultiplier) +} + func (m *Market) GetFillPrice(volume uint64, side types.Side) (*num.Uint, error) { return m.matching.GetFillPrice(volume, side) } @@ -5368,7 +5340,8 @@ func (m *Market) getRebasingOrder( ) (*types.Order, error) { var volume uint64 fairPrice := pool.BestPrice(nil) - oneTick, _ := num.UintFromDecimal(num.DecimalOne().Mul(m.priceFactor)) + oneTick, _ := num.UintFromDecimal(m.priceFactor) + oneTick = num.Max(num.UintOne(), oneTick) var until *num.Uint switch side { @@ -5464,20 +5437,34 @@ func (m *Market) needsRebase(fairPrice *num.Uint) (bool, types.Side, *num.Uint) return false, types.SideUnspecified, nil } -func VerifyAMMBounds(baseParam *num.Uint, lowerParam *num.Uint, upperParam *num.Uint, priceFactor num.Decimal) error { - base, _ := num.UintFromDecimal(baseParam.ToDecimal().Mul(priceFactor)) - if lowerParam != nil { - lower, _ := num.UintFromDecimal(lowerParam.ToDecimal().Mul(priceFactor)) +func VerifyAMMBounds(params *types.ConcentratedLiquidityParameters, cap *num.Uint, priceFactor num.Decimal) error { + base, _ := num.UintFromDecimal(params.Base.ToDecimal().Mul(priceFactor)) + if cap != nil && base.GTE(cap) { + return common.ErrAMMBoundsOutsidePriceCap + } + + if params.LowerBound != nil { + lower, _ := num.UintFromDecimal(params.LowerBound.ToDecimal().Mul(priceFactor)) if lower.GTE(base) { - return fmt.Errorf(fmt.Sprintf("base (%s) as factored by market and asset decimals must be greater than lower bound (%s)", base.String(), lower.String())) + return fmt.Errorf("base (%s) as factored by market and asset decimals must be greater than lower bound (%s)", base.String(), lower.String()) + } + + if cap != nil && lower.GTE(cap) { + return common.ErrAMMBoundsOutsidePriceCap } } - if upperParam != nil { - upper, _ := num.UintFromDecimal(upperParam.ToDecimal().Mul(priceFactor)) + + if params.UpperBound != nil { + upper, _ := num.UintFromDecimal(params.UpperBound.ToDecimal().Mul(priceFactor)) if base.GTE(upper) { - return fmt.Errorf(fmt.Sprintf("upper bound (%s) as factored by market and asset decimals must be greater than base (%s)", upper.String(), base.String())) + return fmt.Errorf("upper bound (%s) as factored by market and asset decimals must be greater than base (%s)", upper.String(), base.String()) + } + + if cap != nil && upper.GTE(cap) { + return common.ErrAMMBoundsOutsidePriceCap } } + return nil } @@ -5491,7 +5478,7 @@ func (m *Market) SubmitAMM(ctx context.Context, submit *types.SubmitAMM, determi // create the AMM curves but do not confirm it with the engine var order *types.Order - if err := VerifyAMMBounds(submit.Parameters.Base, submit.Parameters.LowerBound, submit.Parameters.UpperBound, m.priceFactor); err != nil { + if err := VerifyAMMBounds(submit.Parameters, m.capMax, m.priceFactor); err != nil { return err } @@ -5533,6 +5520,7 @@ func (m *Market) SubmitAMM(ctx context.Context, submit *types.SubmitAMM, determi if order == nil { m.amm.Confirm(ctx, pool) m.matching.UpdateAMM(pool.AMMParty) + m.checkForReferenceMoves(ctx, nil, false) return nil } @@ -5558,6 +5546,7 @@ func (m *Market) SubmitAMM(ctx context.Context, submit *types.SubmitAMM, determi m.amm.Confirm(ctx, pool) // now tell the matching engine something new has appeared incase it needs to update its auction IPV cache m.matching.UpdateAMM(pool.AMMParty) + m.checkForReferenceMoves(ctx, nil, false) return nil } @@ -5570,7 +5559,7 @@ func (m *Market) AmendAMM(ctx context.Context, amend *types.AmendAMM, determinis defer func() { m.idgen = nil }() if amend.Parameters != nil { - if err := VerifyAMMBounds(amend.Parameters.Base, amend.Parameters.LowerBound, amend.Parameters.UpperBound, m.priceFactor); err != nil { + if err := VerifyAMMBounds(amend.Parameters, m.capMax, m.priceFactor); err != nil { return err } } @@ -5580,7 +5569,6 @@ func (m *Market) AmendAMM(ctx context.Context, amend *types.AmendAMM, determinis if err != nil { return err } - // if we failed to rebase the amended pool be sure to reinstante the old one defer func() { if err != nil { @@ -5608,6 +5596,7 @@ func (m *Market) AmendAMM(ctx context.Context, amend *types.AmendAMM, determinis if order == nil { m.amm.Confirm(ctx, pool) m.matching.UpdateAMM(pool.AMMParty) + m.checkForReferenceMoves(ctx, nil, false) return nil } @@ -5618,13 +5607,17 @@ func (m *Market) AmendAMM(ctx context.Context, amend *types.AmendAMM, determinis logging.Error(err), ) if amend.CommitmentAmount != nil { - _, err = m.amm.UpdateSubAccountBalance(ctx, pool.Owner(), pool.AMMParty, prevCommitment) + if _, err := m.amm.UpdateSubAccountBalance(ctx, pool.Owner(), pool.AMMParty, prevCommitment); err != nil { + m.log.Panic("unable to restore AMM balances after failed amend", logging.Error(err)) + } } + err = common.ErrAMMCannotRebase // set it to err so that the defer runs return err } m.amm.Confirm(ctx, pool) m.matching.UpdateAMM(pool.AMMParty) + m.checkForReferenceMoves(ctx, nil, false) return nil } @@ -5646,6 +5639,9 @@ func (m *Market) CancelAMM(ctx context.Context, cancel *types.CancelAMM, determi // tell matching incase it needs to remove the AMM's contribution to the IPV cache m.matching.UpdateAMM(ammParty) + // rejig any pegged orders that might need re-pricing now an AMM is not longer there, or is no longer quoting one side + m.checkForReferenceMoves(ctx, nil, false) + if closeout == nil { return nil } diff --git a/core/execution/future/market_callbacks.go b/core/execution/future/market_callbacks.go index ee8e9d9e0ae..362c6571747 100644 --- a/core/execution/future/market_callbacks.go +++ b/core/execution/future/market_callbacks.go @@ -58,6 +58,18 @@ func (m *Market) OnFeeFactorsMakerFeeUpdate(ctx context.Context, d num.Decimal) m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt)) } +func (m *Market) OnFeeFactorsTreasuryFeeUpdate(ctx context.Context, d num.Decimal) { + m.fee.OnFeeFactorsTreasuryFeeUpdate(d) + m.mkt.Fees.Factors.TreasuryFee = d + m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt)) +} + +func (m *Market) OnFeeFactorsBuyBackFeeUpdate(ctx context.Context, d num.Decimal) { + m.fee.OnFeeFactorsBuyBackFeeUpdate(d) + m.mkt.Fees.Factors.BuyBackFee = d + m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt)) +} + func (m *Market) OnFeeFactorsInfrastructureFeeUpdate(ctx context.Context, d num.Decimal) { m.fee.OnFeeFactorsInfrastructureFeeUpdate(d) m.mkt.Fees.Factors.InfrastructureFee = d diff --git a/core/execution/future/market_snapshot.go b/core/execution/future/market_snapshot.go index cbded574128..bec64150d2f 100644 --- a/core/execution/future/market_snapshot.go +++ b/core/execution/future/market_snapshot.go @@ -67,6 +67,7 @@ func NewMarketFromSnapshot( peggedOrderNotify func(int64), referralDiscountRewardService fee.ReferralDiscountRewardService, volumeDiscountService fee.VolumeDiscountService, + volumeRebateService fee.VolumeRebateService, banking common.Banking, parties common.Parties, ) (*Market, error) { @@ -234,6 +235,7 @@ func NewMarketFromSnapshot( fee: feeEngine, referralDiscountRewardService: referralDiscountRewardService, volumeDiscountService: volumeDiscountService, + volumeRebateService: volumeRebateService, liquidityEngine: liquidityEngine, liquidity: marketLiquidity, parties: map[string]struct{}{}, @@ -317,7 +319,6 @@ func NewMarketFromSnapshot( } stateVarEngine.UnregisterStateVariable(asset, mkt.ID) } - return market, nil } diff --git a/core/execution/future/market_snapshot_test.go b/core/execution/future/market_snapshot_test.go index 239a7008213..1e422c5d1f4 100644 --- a/core/execution/future/market_snapshot_test.go +++ b/core/execution/future/market_snapshot_test.go @@ -242,8 +242,6 @@ func newMarketFromSnapshot(t *testing.T, ctx context.Context, ctrl *gomock.Contr teams := mocks.NewMockTeams(ctrl) bc := mocks.NewMockAccountBalanceChecker(ctrl) broker := bmocks.NewMockBroker(ctrl) - marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker) - epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore) broker.EXPECT().Stage(gomock.Any()).AnyTimes() broker.EXPECT().Send(gomock.Any()).AnyTimes() @@ -251,16 +249,20 @@ func newMarketFromSnapshot(t *testing.T, ctx context.Context, ctrl *gomock.Contr timeService.EXPECT().GetTimeNow().AnyTimes() collateralEngine := collateral.New(log, collateral.NewDefaultConfig(), timeService, broker) + marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine) + epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore) + positionConfig.StreamPositionVerbose = true referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl) - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebate := fmock.NewMockVolumeRebateService(ctrl) + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() banking := mocks.NewMockBanking(ctrl) parties := mocks.NewMockParties(ctrl) return future.NewMarketFromSnapshot(ctx, log, em, riskConfig, positionConfig, settlementConfig, matchingConfig, feeConfig, liquidityConfig, collateralEngine, oracleEngine, timeService, broker, stubs.NewStateVar(), cfgAsset, marketActivityTracker, - peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, banking, parties) + peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, volumeRebate, banking, parties) } diff --git a/core/execution/future/market_test.go b/core/execution/future/market_test.go index 8e62e199c84..446c6108ccc 100644 --- a/core/execution/future/market_test.go +++ b/core/execution/future/market_test.go @@ -235,22 +235,24 @@ func (tm *testMarket) Run(ctx context.Context, mktCfg types.Market) *testMarket teams := mocks.NewMockTeams(tm.ctrl) bc := mocks.NewMockAccountBalanceChecker(tm.ctrl) broker := bmocks.NewMockBroker(tm.ctrl) - marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker) + marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine) epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore) referralDiscountReward := fmocks.NewMockReferralDiscountRewardService(tm.ctrl) volumeDiscount := fmocks.NewMockVolumeDiscountService(tm.ctrl) + volumeRebate := fmocks.NewMockVolumeRebateService(tm.ctrl) referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("no referrer")).AnyTimes() - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeRebate.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() banking := mocks.NewMockBanking(tm.ctrl) parties := mocks.NewMockParties(tm.ctrl) mktEngine, err := future.NewMarket(ctx, tm.log, riskConfig, positionConfig, settlementConfig, matchingConfig, feeConfig, liquidityConfig, collateralEngine, oracleEngine, &mktCfg, tm.timeService, tm.broker, mas, statevarEngine, marketActivityTracker, cfgAsset, - peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, banking, parties, + peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, volumeRebate, banking, parties, ) require.NoError(tm.t, err) @@ -648,22 +650,25 @@ func getTestMarket2WithDP( epoch.EXPECT().NotifyOnEpoch(gomock.Any(), gomock.Any()).Times(1) teams := mocks.NewMockTeams(tm.ctrl) bc := mocks.NewMockAccountBalanceChecker(tm.ctrl) - marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker) + marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine) epoch.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore) referralDiscountReward := fmocks.NewMockReferralDiscountRewardService(tm.ctrl) volumeDiscount := fmocks.NewMockVolumeDiscountService(tm.ctrl) + volumeRebate := fmocks.NewMockVolumeRebateService(tm.ctrl) + referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("no referrer")).AnyTimes() - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeRebate.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() banking := mocks.NewMockBanking(ctrl) parties := mocks.NewMockParties(ctrl) mktEngine, err := future.NewMarket(context.Background(), log, riskConfig, positionConfig, settlementConfig, matchingConfig, feeConfig, liquidityConfig, collateralEngine, oracleEngine, mktCfg, timeService, broker, mas, statevar, marketActivityTracker, cfgAsset, - peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, banking, parties) + peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, volumeRebate, banking, parties) if err != nil { t.Fatalf("couldn't create a market: %v", err) } @@ -6717,13 +6722,100 @@ func TestLiquidityFeeSettingsConstantFee(t *testing.T) { } func TestVerifyAMMBounds(t *testing.T) { - require.Equal(t, "base (8) as factored by market and asset decimals must be greater than lower bound (8)", future.VerifyAMMBounds(num.NewUint(85), num.NewUint(82), num.NewUint(88), num.NewDecimalFromFloat(0.1)).Error()) - require.Equal(t, "upper bound (8) as factored by market and asset decimals must be greater than base (8)", future.VerifyAMMBounds(num.NewUint(85), num.NewUint(78), num.NewUint(88), num.NewDecimalFromFloat(0.1)).Error()) - require.Equal(t, "base (8) as factored by market and asset decimals must be greater than lower bound (8)", future.VerifyAMMBounds(num.NewUint(85), num.NewUint(80), num.NewUint(90), num.NewDecimalFromFloat(0.1)).Error()) - require.NoError(t, future.VerifyAMMBounds(num.NewUint(85), num.NewUint(78), num.NewUint(90), num.NewDecimalFromFloat(0.1))) - - require.NoError(t, future.VerifyAMMBounds(num.NewUint(85), num.NewUint(82), num.NewUint(88), num.NewDecimalFromFloat(1.1))) - require.NoError(t, future.VerifyAMMBounds(num.NewUint(85), num.NewUint(78), num.NewUint(88), num.NewDecimalFromFloat(1.1))) - require.NoError(t, future.VerifyAMMBounds(num.NewUint(85), num.NewUint(80), num.NewUint(90), num.NewDecimalFromFloat(1.1))) - require.NoError(t, future.VerifyAMMBounds(num.NewUint(85), num.NewUint(78), num.NewUint(90), num.NewDecimalFromFloat(1.1))) + tests := []struct { + name string + params *types.ConcentratedLiquidityParameters + maxCap *num.Uint + priceFactor num.Decimal + expectedErr error + }{ + { + name: "normal valid bounds", + params: &types.ConcentratedLiquidityParameters{ + LowerBound: num.NewUint(82), + Base: num.NewUint(85), + UpperBound: num.NewUint(88), + }, + priceFactor: num.NewDecimalFromFloat(1.1), + }, + { + name: "lower greater than base with fewer decimals", + params: &types.ConcentratedLiquidityParameters{ + LowerBound: num.NewUint(80), + Base: num.NewUint(85), + UpperBound: num.NewUint(90), + }, + priceFactor: num.NewDecimalFromFloat(0.1), + expectedErr: fmt.Errorf("base (8) as factored by market and asset decimals must be greater than lower bound (8)"), + }, + { + name: "base greater than base with fewer decimals", + params: &types.ConcentratedLiquidityParameters{ + LowerBound: num.NewUint(80), + Base: num.NewUint(85), + UpperBound: num.NewUint(88), + }, + priceFactor: num.NewDecimalFromFloat(0.1), + expectedErr: fmt.Errorf("base (8) as factored by market and asset decimals must be greater than lower bound (8)"), + }, + { + name: "both bounds too close with fewer decimals", + params: &types.ConcentratedLiquidityParameters{ + LowerBound: num.NewUint(82), + Base: num.NewUint(85), + UpperBound: num.NewUint(88), + }, + priceFactor: num.NewDecimalFromFloat(0.1), + expectedErr: fmt.Errorf("base (8) as factored by market and asset decimals must be greater than lower bound (8)"), + }, + { + name: "upper bound higher than cap", + params: &types.ConcentratedLiquidityParameters{ + LowerBound: num.NewUint(82), + Base: num.NewUint(85), + UpperBound: num.NewUint(88), + }, + priceFactor: num.NewDecimalFromFloat(1), + maxCap: num.NewUint(86), + expectedErr: common.ErrAMMBoundsOutsidePriceCap, + }, + { + name: "upper bound equal cap", + params: &types.ConcentratedLiquidityParameters{ + LowerBound: num.NewUint(82), + Base: num.NewUint(85), + UpperBound: num.NewUint(88), + }, + priceFactor: num.NewDecimalFromFloat(1), + maxCap: num.NewUint(88), + expectedErr: common.ErrAMMBoundsOutsidePriceCap, + }, + { + name: "base higher than cap", + params: &types.ConcentratedLiquidityParameters{ + LowerBound: num.NewUint(82), + Base: num.NewUint(100), + }, + priceFactor: num.NewDecimalFromFloat(1), + maxCap: num.NewUint(86), + expectedErr: common.ErrAMMBoundsOutsidePriceCap, + }, + { + name: "base equal cap", + params: &types.ConcentratedLiquidityParameters{ + LowerBound: num.NewUint(82), + Base: num.NewUint(88), + }, + priceFactor: num.NewDecimalFromFloat(1), + maxCap: num.NewUint(88), + expectedErr: common.ErrAMMBoundsOutsidePriceCap, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := future.VerifyAMMBounds(tt.params, tt.maxCap, tt.priceFactor) + assert.Equal(t, tt.expectedErr, err) + }) + } } diff --git a/core/execution/snapshot_test.go b/core/execution/snapshot_test.go index 6bb7f899871..359475abe5b 100644 --- a/core/execution/snapshot_test.go +++ b/core/execution/snapshot_test.go @@ -610,7 +610,7 @@ func getEngine(t *testing.T, vegaPath paths.Paths, now time.Time) *snapshotTestD ctrl := gomock.NewController(t) teams := mocks.NewMockTeams(ctrl) bc := mocks.NewMockAccountBalanceChecker(ctrl) - marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker) + marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine) epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore) ethAsset := types.Asset{ @@ -624,9 +624,10 @@ func getEngine(t *testing.T, vegaPath paths.Paths, now time.Time) *snapshotTestD require.NoError(t, collateralEngine.EnableAsset(context.Background(), ethAsset)) referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl) - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebate := fmock.NewMockVolumeRebateService(ctrl) + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() banking := mocks.NewMockBanking(ctrl) parties := mocks.NewMockParties(ctrl) @@ -644,6 +645,7 @@ func getEngine(t *testing.T, vegaPath paths.Paths, now time.Time) *snapshotTestD stubs.NewAssetStub(), referralDiscountReward, volumeDiscount, + volumeRebate, banking, parties, delayTarget, @@ -680,7 +682,7 @@ func getEngineWithParties(t *testing.T, now time.Time, balance *num.Uint, partie ctrl := gomock.NewController(t) teams := mocks.NewMockTeams(ctrl) bc := mocks.NewMockAccountBalanceChecker(ctrl) - marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker) + marketActivityTracker := common.NewMarketActivityTracker(logging.NewTestLogger(), teams, bc, broker, collateralEngine) epochEngine.NotifyOnEpoch(marketActivityTracker.OnEpochEvent, marketActivityTracker.OnEpochRestore) ethAsset := types.Asset{ @@ -697,9 +699,11 @@ func getEngineWithParties(t *testing.T, now time.Time, balance *num.Uint, partie } referralDiscountReward := fmock.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmock.NewMockVolumeDiscountService(ctrl) - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebate := fmock.NewMockVolumeRebateService(ctrl) + + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() banking := mocks.NewMockBanking(ctrl) partiesMock := mocks.NewMockParties(ctrl) @@ -717,6 +721,7 @@ func getEngineWithParties(t *testing.T, now time.Time, balance *num.Uint, partie stubs.NewAssetStub(), referralDiscountReward, volumeDiscount, + volumeRebate, banking, partiesMock, delayTarget, diff --git a/core/execution/spot/market.go b/core/execution/spot/market.go index 8fdcb2a8207..8199c063d26 100644 --- a/core/execution/spot/market.go +++ b/core/execution/spot/market.go @@ -82,6 +82,7 @@ type Market struct { fee *fee.Engine referralDiscountRewardService fee.ReferralDiscountRewardService volumeDiscountService fee.VolumeDiscountService + volumeRebateService fee.VolumeRebateService liquidity *common.MarketLiquidity liquidityEngine common.LiquidityEngine @@ -159,6 +160,7 @@ func NewMarket( peggedOrderNotify func(int64), referralDiscountRewardService fee.ReferralDiscountRewardService, volumeDiscountService fee.VolumeDiscountService, + volumeRebateService fee.VolumeRebateService, banking common.Banking, ) (*Market, error) { if len(mkt.ID) == 0 { @@ -232,6 +234,7 @@ func NewMarket( fee: feeEngine, referralDiscountRewardService: referralDiscountRewardService, volumeDiscountService: volumeDiscountService, + volumeRebateService: volumeRebateService, parties: map[string]struct{}{}, as: as, pMonitor: pMonitor, @@ -334,6 +337,15 @@ func (m *Market) GetEquityShares() *common.EquityShares { return m.equityShares } +func (m *Market) GetEquitySharesForParty(partyID string) num.Decimal { + primary := m.equityShares.SharesFromParty(partyID) + // AMM for spot has not been implemented yet + // if sub, err := m.amm.GetAMMParty(partyID); err == nil { + // return primary.Add(m.equityShares.SharesFromParty(sub)) + // } + return primary +} + func (m *Market) SetNextMTM(tm time.Time) { m.nextMTM = tm } @@ -2377,13 +2389,13 @@ func (m *Market) validateOrderAmendment(order *types.Order, amendment *types.Ord existingHoldingQty, existingHoldingFee := m.orderHoldingTracker.GetCurrentHolding(order.ID) oldHoldingRequirement := num.Sum(existingHoldingQty, existingHoldingFee) newFeesRequirement := num.UintZero() - if m.as.InAuction() { - newFeesRequirement, _ = m.calculateFees(order.Party, remaining, amendment.Price, order.Side) - } price := order.Price if amendment.Price != nil { price, _ = num.UintFromDecimal(amendment.Price.ToDecimal().Mul(m.priceFactor)) } + if m.as.InAuction() { + newFeesRequirement, _ = m.calculateFees(order.Party, remaining, price, order.Side) + } if order.PeggedOrder != nil { p, err := m.getNewPeggedPrice(order) if err != nil { @@ -3177,15 +3189,15 @@ func (m *Market) calculateFeesForTrades(trades []*types.Trade) (events.FeesTrans err error ) if !m.as.InAuction() { - fees, err = m.fee.CalculateForContinuousMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForContinuousMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } else if m.as.IsMonitorAuction() { // we are in auction mode - fees, err = m.fee.CalculateForAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } else if m.as.IsFBA() { - fees, err = m.fee.CalculateForFrequentBatchesAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForFrequentBatchesAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } else { if !m.as.IsOpeningAuction() { - fees, err = m.fee.CalculateForAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService) + fees, err = m.fee.CalculateForAuctionMode(trades, m.referralDiscountRewardService, m.volumeDiscountService, m.volumeRebateService) } } return fees, err @@ -3323,62 +3335,54 @@ type IDGen interface { NextID() string } -func (m *Market) checkOrderAmendForSpam(order *types.Order) error { - assetQuantum, err := m.collateral.GetAssetQuantum(m.quoteAsset) - if err != nil { - return err - } - - var price *num.Uint - if order.PeggedOrder == nil { - price, _ = num.UintFromDecimal(order.Price.ToDecimal().Mul(m.priceFactor)) - } else { - priceInMarket, _ := num.UintFromDecimal(m.getCurrentMarkPrice().ToDecimal().Div(m.priceFactor)) - if order.Side == types.SideBuy { - priceInMarket.AddSum(order.PeggedOrder.Offset) - } else { - priceInMarket = priceInMarket.Sub(priceInMarket, order.PeggedOrder.Offset) - } - price, _ = num.UintFromDecimal(priceInMarket.ToDecimal().Mul(m.priceFactor)) - } - - minQuantum := assetQuantum.Mul(m.minHoldingQuantumMultiplier) - value := num.UintZero().Mul(num.NewUint(order.Size), price).ToDecimal() - value = value.Div(m.positionFactor).Div(assetQuantum) - if value.LessThan(minQuantum.Mul(assetQuantum)) { - return fmt.Errorf("order value is less than minimum holding requirement for spam") - } - return nil -} - -func (m *Market) CheckOrderSubmissionForSpam(orderSubmission *types.OrderSubmission, party string, quantumMultiplier num.Decimal) error { +func (m *Market) checkOrderForSpam(side types.Side, orderPrice *num.Uint, orderSize uint64, peggedOrder *types.PeggedOrder, orderType vega.Order_Type, quantumMultiplier num.Decimal) error { assetQuantum, err := m.collateral.GetAssetQuantum(m.quoteAsset) if err != nil { return err } var price *num.Uint - if orderSubmission.PeggedOrder != nil || orderSubmission.Type == vega.Order_TYPE_MARKET { + if peggedOrder != nil || orderType == vega.Order_TYPE_MARKET { priceInMarket, _ := num.UintFromDecimal(m.getCurrentMarkPrice().ToDecimal().Div(m.priceFactor)) offset := num.UintZero() - if orderSubmission.PeggedOrder != nil { - offset = orderSubmission.PeggedOrder.Offset + if peggedOrder != nil { + offset = peggedOrder.Offset } - if orderSubmission.Side == types.SideBuy { + if side == types.SideBuy { priceInMarket.AddSum(offset) } else { priceInMarket = priceInMarket.Sub(priceInMarket, offset) } price, _ = num.UintFromDecimal(priceInMarket.ToDecimal().Mul(m.priceFactor)) } else { - price, _ = num.UintFromDecimal(orderSubmission.Price.ToDecimal().Mul(m.priceFactor)) + price, _ = num.UintFromDecimal(orderPrice.ToDecimal().Mul(m.priceFactor)) } - minQuantum := assetQuantum.Mul(quantumMultiplier) - value := num.UintZero().Mul(num.NewUint(orderSubmission.Size), price).ToDecimal() + value := num.UintZero().Mul(num.NewUint(orderSize), price).ToDecimal() value = value.Div(m.positionFactor) - if value.LessThan(minQuantum.Mul(assetQuantum)) { - return fmt.Errorf("order value is less than minimum holding requirement for spam") + required := assetQuantum.Mul(quantumMultiplier) + if value.LessThan(required) { + return fmt.Errorf(fmt.Sprintf("order value (%s) is less than minimum holding requirement for spam (%s)", value.String(), required.String())) } return nil } + +func (m *Market) checkOrderAmendForSpam(order *types.Order) error { + return m.checkOrderForSpam( + order.Side, + order.Price, + order.Size, + order.PeggedOrder, + order.Type, + m.minHoldingQuantumMultiplier) +} + +func (m *Market) CheckOrderSubmissionForSpam(orderSubmission *types.OrderSubmission, party string, quantumMultiplier num.Decimal) error { + return m.checkOrderForSpam( + orderSubmission.Side, + orderSubmission.Price, + orderSubmission.Size, + orderSubmission.PeggedOrder, + orderSubmission.Type, + quantumMultiplier) +} diff --git a/core/execution/spot/market_callbacks.go b/core/execution/spot/market_callbacks.go index 77cc9fa13de..436bc46e2ad 100644 --- a/core/execution/spot/market_callbacks.go +++ b/core/execution/spot/market_callbacks.go @@ -46,6 +46,18 @@ func (m *Market) OnMarketProbabilityOfTradingTauScalingUpdate(_ context.Context, m.liquidity.OnProbabilityOfTradingTauScalingUpdate(d) } +func (m *Market) OnFeeFactorsTreasuryFeeUpdate(ctx context.Context, d num.Decimal) { + m.fee.OnFeeFactorsTreasuryFeeUpdate(d) + m.mkt.Fees.Factors.TreasuryFee = d + m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt)) +} + +func (m *Market) OnFeeFactorsBuyBackFeeUpdate(ctx context.Context, d num.Decimal) { + m.fee.OnFeeFactorsBuyBackFeeUpdate(d) + m.mkt.Fees.Factors.BuyBackFee = d + m.broker.Send(events.NewMarketUpdatedEvent(ctx, *m.mkt)) +} + func (m *Market) OnFeeFactorsMakerFeeUpdate(ctx context.Context, d num.Decimal) { m.fee.OnFeeFactorsMakerFeeUpdate(d) m.mkt.Fees.Factors.MakerFee = d diff --git a/core/execution/spot/market_snapshot.go b/core/execution/spot/market_snapshot.go index 3c27306163a..252963c965e 100644 --- a/core/execution/spot/market_snapshot.go +++ b/core/execution/spot/market_snapshot.go @@ -64,6 +64,7 @@ func NewMarketFromSnapshot( peggedOrderNotify func(int64), referralDiscountRewardService fee.ReferralDiscountRewardService, volumeDiscountService fee.VolumeDiscountService, + volumeRebateService fee.VolumeRebateService, banking common.Banking, ) (*Market, error) { mkt := em.Market @@ -158,6 +159,7 @@ func NewMarketFromSnapshot( fee: feeEngine, referralDiscountRewardService: referralDiscountRewardService, volumeDiscountService: volumeDiscountService, + volumeRebateService: volumeRebateService, liquidity: marketLiquidity, liquidityEngine: liquidity, parties: map[string]struct{}{}, diff --git a/core/execution/spot/market_test.go b/core/execution/spot/market_test.go index c8b84aa9289..41d3393b039 100644 --- a/core/execution/spot/market_test.go +++ b/core/execution/spot/market_test.go @@ -210,7 +210,7 @@ func newTestMarket( teams := mocks.NewMockTeams(ctrl) bc := mocks.NewMockAccountBalanceChecker(ctrl) broker.EXPECT().SendBatch(gomock.Any()).Times(1) - mat := common.NewMarketActivityTracker(log, teams, bc, broker) + mat := common.NewMarketActivityTracker(log, teams, bc, broker, collateral) epoch.NotifyOnEpoch(mat.OnEpochEvent, mat.OnEpochRestore) baseAsset := NewAssetStub(base, baseDP) @@ -218,13 +218,15 @@ func newTestMarket( referralDiscountReward := fmocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmocks.NewMockVolumeDiscountService(ctrl) + volumeRebate := fmocks.NewMockVolumeRebateService(ctrl) referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("no referrer")).AnyTimes() - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeRebate.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() banking := mocks.NewMockBanking(ctrl) - market, _ := spot.NewMarket(log, matching.NewDefaultConfig(), fee.NewDefaultConfig(), liquidity.NewDefaultConfig(), collateral, &mkt, ts, broker, as, statevarEngine, mat, baseAsset, quoteAsset, peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, banking) + market, _ := spot.NewMarket(log, matching.NewDefaultConfig(), fee.NewDefaultConfig(), liquidity.NewDefaultConfig(), collateral, &mkt, ts, broker, as, statevarEngine, mat, baseAsset, quoteAsset, peggedOrderCounterForTest, referralDiscountReward, volumeDiscount, volumeRebate, banking) tm := &testMarket{ market: market, diff --git a/core/fee/engine.go b/core/fee/engine.go index 516988a724e..652070abf87 100644 --- a/core/fee/engine.go +++ b/core/fee/engine.go @@ -31,15 +31,19 @@ var ( ErrInvalidFeeFactor = errors.New("fee factors must be positive") ) -//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/fee ReferralDiscountRewardService,VolumeDiscountService +//go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/fee ReferralDiscountRewardService,VolumeDiscountService,VolumeRebateService type ReferralDiscountRewardService interface { - ReferralDiscountFactorForParty(party types.PartyID) num.Decimal - RewardsFactorMultiplierAppliedForParty(party types.PartyID) num.Decimal + ReferralDiscountFactorsForParty(party types.PartyID) types.Factors + RewardsFactorsMultiplierAppliedForParty(party types.PartyID) types.Factors GetReferrer(referee types.PartyID) (types.PartyID, error) } type VolumeDiscountService interface { - VolumeDiscountFactorForParty(party types.PartyID) num.Decimal + VolumeDiscountFactorForParty(party types.PartyID) types.Factors +} + +type VolumeRebateService interface { + VolumeRebateFactorForParty(party types.PartyID) num.Decimal } type Engine struct { @@ -58,6 +62,8 @@ type factors struct { makerFee num.Decimal infrastructureFee num.Decimal liquidityFee num.Decimal + treasuryFee num.Decimal + buyBackFee num.Decimal } func New( @@ -128,7 +134,7 @@ func (e *Engine) ReloadConf(cfg Config) { } func (e *Engine) UpdateFeeFactors(fees types.Fees) error { - if fees.Factors.MakerFee.IsNegative() || fees.Factors.InfrastructureFee.IsNegative() || fees.Factors.LiquidityFee.IsNegative() { + if fees.Factors.MakerFee.IsNegative() || fees.Factors.InfrastructureFee.IsNegative() || fees.Factors.LiquidityFee.IsNegative() || fees.Factors.BuyBackFee.IsNegative() || fees.Factors.TreasuryFee.IsNegative() { return ErrInvalidFeeFactor } e.f.makerFee = fees.Factors.MakerFee @@ -137,6 +143,8 @@ func (e *Engine) UpdateFeeFactors(fees types.Fees) error { if !fees.Factors.LiquidityFee.IsZero() && fees.Factors.LiquidityFee.IsPositive() { e.f.liquidityFee = fees.Factors.LiquidityFee } + e.f.treasuryFee = fees.Factors.TreasuryFee + e.f.buyBackFee = fees.Factors.BuyBackFee e.feeCfg = fees return nil @@ -155,6 +163,7 @@ func (e *Engine) CalculateForContinuousMode( trades []*types.Trade, referral ReferralDiscountRewardService, volumeDiscountService VolumeDiscountService, + volumeRebateService VolumeRebateService, ) (events.FeesTransfer, error) { if len(trades) <= 0 { return nil, ErrEmptyTrades @@ -179,7 +188,10 @@ func (e *Engine) CalculateForContinuousMode( taker = trade.Seller maker = trade.Buyer } - fee, reward := e.applyDiscountsAndRewards(taker, e.calculateContinuousModeFees(trade), referral, volumeDiscountService) + size := num.NewUint(trade.Size) + // multiply by size + tradeValueForFee := size.Mul(trade.Price, size).ToDecimal().Div(e.positionFactor) + fee, reward := e.applyDiscountsAndRewards(taker, maker, tradeValueForFee, e.calculateContinuousModeFees(trade), referral, volumeDiscountService, volumeRebateService) e.feesStats.RegisterMakerFee(maker, taker, fee.MakerFee) @@ -221,6 +233,47 @@ func (e *Engine) CalculateForContinuousMode( Type: types.TransferTypeMakerFeeReceive, }) + if !fee.HighVolumeMakerFee.IsZero() { + // create a transfer for the aggressor + transfers = append(transfers, &types.Transfer{ + Owner: taker, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fee.HighVolumeMakerFee.Clone(), + }, + Type: types.TransferTypeHighMakerRebatePay, + }) + // create a transfer for the maker + transfersRecv = append(transfersRecv, &types.Transfer{ + Owner: maker, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fee.HighVolumeMakerFee.Clone(), + }, + Type: types.TransferTypeHighMakerRebateReceive, + }) + } + + // create a transfer for the aggressor + transfers = append(transfers, &types.Transfer{ + Owner: taker, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fee.BuyBackFee.Clone(), + }, + Type: types.TransferTypeBuyBackFeePay, + }) + + // create a transfer for the aggressor + transfers = append(transfers, &types.Transfer{ + Owner: taker, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fee.TreasuryFee.Clone(), + }, + Type: types.TransferTypeTreasuryPay, + }) + if reward == nil { continue } @@ -284,6 +337,7 @@ func (e *Engine) CalculateForAuctionMode( trades []*types.Trade, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService, + volumeRebate VolumeRebateService, ) (events.FeesTransfer, error) { if len(trades) <= 0 { return nil, ErrEmptyTrades @@ -299,7 +353,7 @@ func (e *Engine) CalculateForAuctionMode( // for each trades both party needs to pay half of the fees // no maker fees are to be paid here. for _, v := range trades { - buyerFess, sellerFees, newTransfers := e.getAuctionModeFeesAndTransfers(v, referral, volumeDiscount) + buyerFess, sellerFees, newTransfers := e.getAuctionModeFeesAndTransfers(v, referral, volumeDiscount, volumeRebate) transfers = append(transfers, newTransfers...) // increase the total fee for the parties @@ -333,6 +387,7 @@ func (e *Engine) CalculateForFrequentBatchesAuctionMode( trades []*types.Trade, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService, + volumeRebate VolumeRebateService, ) (events.FeesTransfer, error) { if len(trades) <= 0 { return nil, ErrEmptyTrades @@ -357,7 +412,7 @@ func (e *Engine) CalculateForFrequentBatchesAuctionMode( ) // we are in the same auction, normal auction fees applies if v.BuyerAuctionBatch == v.SellerAuctionBatch { - v.BuyerFee, v.SellerFee, newTransfers = e.getAuctionModeFeesAndTransfers(v, referral, volumeDiscount) + v.BuyerFee, v.SellerFee, newTransfers = e.getAuctionModeFeesAndTransfers(v, referral, volumeDiscount, volumeRebate) sellerTotalFee = num.Sum(v.BuyerFee.InfrastructureFee, v.BuyerFee.LiquidityFee) buyerTotalFee = num.Sum(v.SellerFee.InfrastructureFee, v.SellerFee.LiquidityFee) } else { @@ -369,7 +424,7 @@ func (e *Engine) CalculateForFrequentBatchesAuctionMode( } // fees are being assign to the trade directly // no need to do add them there as well - ftrnsfr, _ := e.CalculateForContinuousMode([]*types.Trade{v}, referral, volumeDiscount) + ftrnsfr, _ := e.CalculateForContinuousMode([]*types.Trade{v}, referral, volumeDiscount, volumeRebate) newTransfers = ftrnsfr.Transfers() buyerTotalFee = ftrnsfr.TotalFeesAmountPerParty()[v.Buyer] sellerTotalFee = ftrnsfr.TotalFeesAmountPerParty()[v.Seller] @@ -510,6 +565,8 @@ func (e *Engine) getNetworkFeeWithMakerTransfer(fees *types.Fee, current *types. current.MakerFee.AddSum(fees.MakerFee) current.LiquidityFee.AddSum(fees.LiquidityFee) current.InfrastructureFee.AddSum(fees.InfrastructureFee) + current.BuyBackFee.AddSum(fees.BuyBackFee) + current.TreasuryFee.AddSum(fees.TreasuryFee) return current, transfer } @@ -542,21 +599,41 @@ func (e *Engine) getNetworkFeeTransfers(fees *types.Fee) ([]*types.Transfer, *nu MinAmount: num.UintZero(), Type: types.TransferTypeLiquidityFeePay, }, + { + Owner: types.NetworkParty, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fees.BuyBackFee.Clone(), + }, + MinAmount: num.UintZero(), + Type: types.TransferTypeBuyBackFeePay, + }, + { + Owner: types.NetworkParty, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: fees.TreasuryFee.Clone(), + }, + MinAmount: num.UintZero(), + Type: types.TransferTypeTreasuryPay, + }, }, num.Sum(fees.MakerFee, fees.InfrastructureFee, fees.LiquidityFee) } -func (e *Engine) applyDiscountsAndRewards(taker string, fees *types.Fee, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService) (*types.Fee, *types.ReferrerReward) { - referralDiscountFactor := referral.ReferralDiscountFactorForParty(types.PartyID(taker)) - volumeDiscountFactor := volumeDiscount.VolumeDiscountFactorForParty(types.PartyID(taker)) +func (e *Engine) applyDiscountsAndRewards(taker string, maker string, tradeValueForFeePurposes num.Decimal, fees *types.Fee, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService, volumeRebate VolumeRebateService) (*types.Fee, *types.ReferrerReward) { + referralDiscountFactors := referral.ReferralDiscountFactorsForParty(types.PartyID(taker)) + volumeDiscountFactors := volumeDiscount.VolumeDiscountFactorForParty(types.PartyID(taker)) + highVolumeMakerFee := volumeRebate.VolumeRebateFactorForParty(types.PartyID(maker)).Mul(tradeValueForFeePurposes) + highVolumeMakerFeeI, _ := num.UintFromDecimal(highVolumeMakerFee) mf := fees.MakerFee.Clone() inf := fees.InfrastructureFee.Clone() lf := fees.LiquidityFee.Clone() // calculate referral discounts - referralMakerDiscount, _ := num.UintFromDecimal(mf.ToDecimal().Mul(referralDiscountFactor).Floor()) - referralInfDiscount, _ := num.UintFromDecimal(inf.ToDecimal().Mul(referralDiscountFactor).Floor()) - referralLfDiscount, _ := num.UintFromDecimal(lf.ToDecimal().Mul(referralDiscountFactor).Floor()) + referralMakerDiscount, _ := num.UintFromDecimal(mf.ToDecimal().Mul(referralDiscountFactors.Maker).Floor()) + referralInfDiscount, _ := num.UintFromDecimal(inf.ToDecimal().Mul(referralDiscountFactors.Infra).Floor()) + referralLfDiscount, _ := num.UintFromDecimal(lf.ToDecimal().Mul(referralDiscountFactors.Liquidity).Floor()) // apply referral discounts mf = mf.Sub(mf, referralMakerDiscount) @@ -564,9 +641,18 @@ func (e *Engine) applyDiscountsAndRewards(taker string, fees *types.Fee, referra lf = lf.Sub(lf, referralLfDiscount) // calculate volume discounts - volumeMakerDiscount, _ := num.UintFromDecimal(mf.ToDecimal().Mul(volumeDiscountFactor).Floor()) - volumeInfDiscount, _ := num.UintFromDecimal(inf.ToDecimal().Mul(volumeDiscountFactor).Floor()) - volumeLfDiscount, _ := num.UintFromDecimal(lf.ToDecimal().Mul(volumeDiscountFactor).Floor()) + volumeMakerDiscount, _ := num.UintFromDecimal(mf.ToDecimal().Mul(volumeDiscountFactors.Maker).Floor()) + volumeInfDiscount, _ := num.UintFromDecimal(inf.ToDecimal().Mul(volumeDiscountFactors.Infra).Floor()) + volumeLfDiscount, _ := num.UintFromDecimal(lf.ToDecimal().Mul(volumeDiscountFactors.Liquidity).Floor()) + + var rebateDiscountFactor num.Decimal + bbAndTreasury := num.Sum(fees.BuyBackFee, fees.TreasuryFee).ToDecimal() + if !bbAndTreasury.IsZero() { + rebateDiscountFactor = num.DecimalOne().Sub(highVolumeMakerFee.Div(bbAndTreasury)) + } + + treasuryFee, _ := num.UintFromDecimal(fees.TreasuryFee.ToDecimal().Mul(rebateDiscountFactor)) + buyBackFee, _ := num.UintFromDecimal(fees.BuyBackFee.ToDecimal().Mul(rebateDiscountFactor)) // apply volume discounts mf = mf.Sub(mf, volumeMakerDiscount) @@ -574,9 +660,12 @@ func (e *Engine) applyDiscountsAndRewards(taker string, fees *types.Fee, referra lf = lf.Sub(lf, volumeLfDiscount) f := &types.Fee{ + HighVolumeMakerFee: highVolumeMakerFeeI, MakerFee: mf, LiquidityFee: lf, InfrastructureFee: inf, + BuyBackFee: buyBackFee.Clone(), + TreasuryFee: treasuryFee.Clone(), MakerFeeVolumeDiscount: volumeMakerDiscount, InfrastructureFeeVolumeDiscount: volumeInfDiscount, LiquidityFeeVolumeDiscount: volumeLfDiscount, @@ -604,16 +693,16 @@ func (e *Engine) applyDiscountsAndRewards(taker string, fees *types.Fee, referra ) // calculate rewards - factor := referral.RewardsFactorMultiplierAppliedForParty(types.PartyID(taker)) - if factor.IsZero() { + factors := referral.RewardsFactorsMultiplierAppliedForParty(types.PartyID(taker)) + if factors.IsEmpty() { return f, nil } referrerReward := types.NewReferrerReward() - referrerReward.MakerFeeReferrerReward, _ = num.UintFromDecimal(factor.Mul(mf.ToDecimal()).Floor()) - referrerReward.InfrastructureFeeReferrerReward, _ = num.UintFromDecimal(factor.Mul(inf.ToDecimal()).Floor()) - referrerReward.LiquidityFeeReferrerReward, _ = num.UintFromDecimal(factor.Mul(lf.ToDecimal()).Floor()) + referrerReward.MakerFeeReferrerReward, _ = num.UintFromDecimal(factors.Maker.Mul(mf.ToDecimal()).Floor()) + referrerReward.InfrastructureFeeReferrerReward, _ = num.UintFromDecimal(factors.Infra.Mul(inf.ToDecimal()).Floor()) + referrerReward.LiquidityFeeReferrerReward, _ = num.UintFromDecimal(factors.Liquidity.Mul(lf.ToDecimal()).Floor()) mf = mf.Sub(mf, referrerReward.MakerFeeReferrerReward) inf = inf.Sub(inf, referrerReward.InfrastructureFeeReferrerReward) @@ -640,18 +729,19 @@ func (e *Engine) applyDiscountsAndRewards(taker string, fees *types.Fee, referra return f, referrerReward } -func (e *Engine) getAuctionModeFeesAndTransfers(t *types.Trade, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService) (*types.Fee, *types.Fee, []*types.Transfer) { +func (e *Engine) getAuctionModeFeesAndTransfers(t *types.Trade, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService, volumeRebate VolumeRebateService) (*types.Fee, *types.Fee, []*types.Transfer) { fee := e.calculateAuctionModeFees(t) - buyerFeers, buyerReferrerRewards := e.applyDiscountsAndRewards(t.Buyer, fee, referral, volumeDiscount) - sellerFeers, sellerReferrerRewards := e.applyDiscountsAndRewards(t.Seller, fee, referral, volumeDiscount) + // in auction there is no maker so there is no rebate, so passing 0 as the trade value + buyerFees, buyerReferrerRewards := e.applyDiscountsAndRewards(t.Buyer, t.Buyer, num.DecimalZero(), fee, referral, volumeDiscount, volumeRebate) + sellerFees, sellerReferrerRewards := e.applyDiscountsAndRewards(t.Seller, t.Seller, num.DecimalZero(), fee, referral, volumeDiscount, volumeRebate) transfers := make([]*types.Transfer, 0, 12) transfers = append(transfers, e.getAuctionModeFeeTransfers( - sellerFeers.InfrastructureFee, sellerFeers.LiquidityFee, t.Seller)...) + sellerFees.InfrastructureFee, sellerFees.LiquidityFee, sellerFees.BuyBackFee, sellerFees.TreasuryFee, t.Seller)...) transfers = append(transfers, e.getAuctionModeFeeTransfers( - buyerFeers.InfrastructureFee, buyerFeers.LiquidityFee, t.Buyer)...) + buyerFees.InfrastructureFee, buyerFees.LiquidityFee, buyerFees.BuyBackFee, buyerFees.TreasuryFee, t.Buyer)...) if buyerReferrerRewards != nil { referrerParty, _ := referral.GetReferrer(types.PartyID(t.Buyer)) @@ -667,7 +757,7 @@ func (e *Engine) getAuctionModeFeesAndTransfers(t *types.Trade, referral Referra num.Sum(sellerReferrerRewards.InfrastructureFeeReferrerReward, sellerReferrerRewards.LiquidityFeeReferrerReward), t.Seller, string(referrerParty))...) } - return buyerFeers, sellerFeers, transfers + return buyerFees, sellerFees, transfers } func (e *Engine) calculateContinuousModeFees(trade *types.Trade) *types.Fee { @@ -677,10 +767,15 @@ func (e *Engine) calculateContinuousModeFees(trade *types.Trade) *types.Fee { mf, _ := num.UintFromDecimal(total.Mul(e.f.makerFee).Ceil()) inf, _ := num.UintFromDecimal(total.Mul(e.f.infrastructureFee).Ceil()) lf, _ := num.UintFromDecimal(total.Mul(e.f.liquidityFee).Ceil()) + bbf, _ := num.UintFromDecimal(total.Mul(e.f.buyBackFee).Ceil()) + tf, _ := num.UintFromDecimal(total.Mul(e.f.treasuryFee).Ceil()) return &types.Fee{ - MakerFee: mf, - InfrastructureFee: inf, - LiquidityFee: lf, + MakerFee: mf, + InfrastructureFee: inf, + LiquidityFee: lf, + BuyBackFee: bbf, + TreasuryFee: tf, + HighVolumeMakerFee: num.UintZero(), } } @@ -689,10 +784,14 @@ func (e *Engine) calculateAuctionModeFees(trade *types.Trade) *types.Fee { two := num.DecimalFromInt64(2) inf, _ := num.UintFromDecimal(fee.InfrastructureFee.ToDecimal().Div(two).Ceil()) lf, _ := num.UintFromDecimal(fee.LiquidityFee.ToDecimal().Div(two).Ceil()) + bbf, _ := num.UintFromDecimal(fee.BuyBackFee.ToDecimal().Div(two).Ceil()) + tf, _ := num.UintFromDecimal(fee.TreasuryFee.ToDecimal().Div(two).Ceil()) return &types.Fee{ MakerFee: num.UintZero(), InfrastructureFee: inf, LiquidityFee: lf, + TreasuryFee: tf, + BuyBackFee: bbf, } } @@ -718,7 +817,7 @@ func (e *Engine) getAuctionModeFeeReferrerRewardTransfers(reward *num.Uint, p, r } } -func (e *Engine) getAuctionModeFeeTransfers(infraFee, liquiFee *num.Uint, p string) []*types.Transfer { +func (e *Engine) getAuctionModeFeeTransfers(infraFee, liquiFee, buyBackFee, treasuryFee *num.Uint, p string) []*types.Transfer { // we return both transfer for the party in a slice // always the infrastructure fee first return []*types.Transfer{ @@ -738,6 +837,22 @@ func (e *Engine) getAuctionModeFeeTransfers(infraFee, liquiFee *num.Uint, p stri }, Type: types.TransferTypeLiquidityFeePay, }, + { + Owner: p, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: buyBackFee.Clone(), + }, + Type: types.TransferTypeBuyBackFeePay, + }, + { + Owner: p, + Amount: &types.FinancialAmount{ + Asset: e.asset, + Amount: treasuryFee.Clone(), + }, + Type: types.TransferTypeTreasuryPay, + }, } } @@ -760,6 +875,16 @@ func (e *Engine) OnFeeFactorsMakerFeeUpdate(f num.Decimal) { e.f.makerFee = f } +func (e *Engine) OnFeeFactorsBuyBackFeeUpdate(f num.Decimal) { + e.feeCfg.Factors.BuyBackFee = f + e.f.buyBackFee = f +} + +func (e *Engine) OnFeeFactorsTreasuryFeeUpdate(f num.Decimal) { + e.feeCfg.Factors.TreasuryFee = f + e.f.treasuryFee = f +} + func (e *Engine) OnFeeFactorsInfrastructureFeeUpdate(f num.Decimal) { e.feeCfg.Factors.InfrastructureFee = f e.f.infrastructureFee = f diff --git a/core/fee/engine_test.go b/core/fee/engine_test.go index 30c0889aceb..e9b145ceb54 100644 --- a/core/fee/engine_test.go +++ b/core/fee/engine_test.go @@ -35,13 +35,24 @@ const ( testAsset = "ETH" ) -var testFees = types.Fees{ - Factors: &types.FeeFactors{ - LiquidityFee: num.DecimalFromFloat(0.1), - InfrastructureFee: num.DecimalFromFloat(0.05), - MakerFee: num.DecimalFromFloat(0.02), - }, -} +var ( + testFees = types.Fees{ + Factors: &types.FeeFactors{ + LiquidityFee: num.DecimalFromFloat(0.1), + InfrastructureFee: num.DecimalFromFloat(0.05), + MakerFee: num.DecimalFromFloat(0.02), + }, + } + extendedTestFees = types.Fees{ + Factors: &types.FeeFactors{ + LiquidityFee: num.DecimalFromFloat(0.1), + InfrastructureFee: num.DecimalFromFloat(0.05), + MakerFee: num.DecimalFromFloat(0.02), + BuyBackFee: num.DecimalFromFloat(0.002), + TreasuryFee: num.DecimalFromFloat(0.003), + }, + } +) type testFee struct { *fee.Engine @@ -60,27 +71,47 @@ func getTestFee(t *testing.T) *testFee { return &testFee{eng} } +func getExtendedTestFee(t *testing.T) *testFee { + t.Helper() + eng, err := fee.New( + logging.NewTestLogger(), + fee.NewDefaultConfig(), + extendedTestFees, + testAsset, + num.DecimalFromInt64(1), + ) + assert.NoError(t, err) + return &testFee{eng} +} + func TestFeeEngine(t *testing.T) { t.Run("update fee factors with invalid input", testUpdateFeeFactorsError) t.Run("update fee factors with valid input", testUpdateFeeFactors) t.Run("calculate continuous trading fee empty trade", testCalcContinuousTradingErrorEmptyTrade) t.Run("calculate continuous trading fee", testCalcContinuousTrading) t.Run("calculate continuous trading fee + check amounts", testCalcContinuousTradingAndCheckAmounts) - t.Run("calculate continuous trading fee + check amounts with discounts and rewards", testCalcContinuousTradingAndCheckAmountsWithDiscount) - - t.Run("calculate continuous trading fee empty trade", testCalcContinuousTradingErrorEmptyTrade) t.Run("calculate auction trading fee empty trade", testCalcAuctionTradingErrorEmptyTrade) t.Run("calculate auction trading fee", testCalcAuctionTrading) - t.Run("calculate batch auction trading fee empty trade", testCalcBatchAuctionTradingErrorEmptyTrade) t.Run("calculate batch auction trading fee same batch", testCalcBatchAuctionTradingSameBatch) t.Run("calculate batch auction trading fee different batches", testCalcBatchAuctionTradingDifferentBatches) - t.Run("Build liquidity fee transfers with remainder", testBuildLiquidityFeesRemainder) t.Run("calculate closeout fees", testCloseoutFees) } +func TestFeeEngineWithBuyBackAndTreasury(t *testing.T) { + t.Run("update fee factors with invalid input", testUpdateExtendedFeeFactorsError) + t.Run("update fee factors with valid input", testUpdateExtendedFeeFactors) + t.Run("calculate continuous trading fee empty trade", testCalcContinuousTradingErrorEmptyTrade) + t.Run("calculate continuous trading fee", testCalcContinuousTradingExtended) + t.Run("calculate continuous trading fee + check amounts", testCalcContinuousTradingAndCheckAmountsExtended) + t.Run("calculate continuous trading fee + check amounts with discounts and rewards", testCalcContinuousTradingAndCheckAmountsWithDiscountExtended) + t.Run("calculate auction trading fee empty trade", testCalcAuctionTradingErrorEmptyTrade) + t.Run("calculate auction trading fee", testCalcAuctionTradingExtended) + t.Run("calculate batch auction trading fee empty trade", testCalcBatchAuctionTradingErrorEmptyTrade) +} + func testUpdateFeeFactors(t *testing.T) { eng := getTestFee(t) okFees := types.Fees{ @@ -94,6 +125,21 @@ func testUpdateFeeFactors(t *testing.T) { assert.NoError(t, err) } +func testUpdateExtendedFeeFactors(t *testing.T) { + eng := getExtendedTestFee(t) + okFees := types.Fees{ + Factors: &types.FeeFactors{ + LiquidityFee: num.DecimalFromFloat(0.1), + InfrastructureFee: num.DecimalFromFloat(0.5), + MakerFee: num.DecimalFromFloat(0.25), + BuyBackFee: num.DecimalFromFloat(0.3), + TreasuryFee: num.DecimalFromFloat(0.4), + }, + } + err := eng.UpdateFeeFactors(okFees) + assert.NoError(t, err) +} + func testUpdateFeeFactorsError(t *testing.T) { eng := getTestFee(t) koFees := types.Fees{ @@ -126,13 +172,46 @@ func testUpdateFeeFactorsError(t *testing.T) { assert.Error(t, err) } +func testUpdateExtendedFeeFactorsError(t *testing.T) { + eng := getExtendedTestFee(t) + koFees := types.Fees{ + Factors: &types.FeeFactors{ + LiquidityFee: num.DecimalFromFloat(0.1), + InfrastructureFee: num.DecimalFromFloat(0.5), + MakerFee: num.DecimalFromFloat(0.25), + BuyBackFee: num.DecimalFromFloat(-1), + TreasuryFee: num.DecimalFromFloat(0.4), + }, + } + err := eng.UpdateFeeFactors(koFees) + assert.Error(t, err) + + koFees = types.Fees{ + Factors: &types.FeeFactors{ + LiquidityFee: num.DecimalFromFloat(0.1), + InfrastructureFee: num.DecimalFromFloat(0.11), + MakerFee: num.DecimalFromFloat(0.25), + BuyBackFee: num.DecimalFromFloat(0.41), + TreasuryFee: num.DecimalFromFloat(-1), + }, + } + err = eng.UpdateFeeFactors(koFees) + assert.Error(t, err) +} + func testCalcContinuousTradingErrorEmptyTrade(t *testing.T) { eng := getTestFee(t) ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - _, err := eng.CalculateForContinuousMode([]*types.Trade{}, discountRewardService, volumeDiscountService) + _, err := eng.CalculateForContinuousMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) + assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) + + eng = getExtendedTestFee(t) + _, err = eng.CalculateForContinuousMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) } @@ -141,9 +220,11 @@ func testCalcContinuousTradingAndCheckAmounts(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() require.NoError(t, eng.UpdateFeeFactors(types.Fees{ Factors: &types.FeeFactors{ @@ -162,7 +243,7 @@ func testCalcContinuousTradingAndCheckAmounts(t *testing.T) { }, } - ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) transfers := ft.Transfers() @@ -244,14 +325,162 @@ func testCalcContinuousTradingAndCheckAmounts(t *testing.T) { }, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1))) } +func testCalcContinuousTradingAndCheckAmountsExtended(t *testing.T) { + eng := getExtendedTestFee(t) + ctrl := gomock.NewController(t) + discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) + volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party2")).Return(num.DecimalFromFloat(0.0025)).AnyTimes() + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() + require.NoError(t, eng.UpdateFeeFactors(types.Fees{ + Factors: &types.FeeFactors{ + MakerFee: num.DecimalFromFloat(.000250), + InfrastructureFee: num.DecimalFromFloat(0.0005), + LiquidityFee: num.DecimalFromFloat(0.001), + BuyBackFee: num.DecimalFromFloat(0.002), + TreasuryFee: num.DecimalFromFloat(0.003), + }, + })) + trades := []*types.Trade{ + { + Aggressor: types.SideSell, + Seller: "party1", + Buyer: "party2", + Size: 5, + Price: num.NewUint(100000), + }, + } + + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) + assert.NotNil(t, ft) + assert.Nil(t, err) + transfers := ft.Transfers() + var pay, recv, infra, liquidity, bb, treasury, hmp, hmr int + for _, v := range transfers { + if v.Type == types.TransferTypeLiquidityFeePay { + liquidity++ + assert.Equal(t, num.NewUint(500), v.Amount.Amount) + } + if v.Type == types.TransferTypeInfrastructureFeePay { + infra++ + assert.Equal(t, num.NewUint(250), v.Amount.Amount) + } + if v.Type == types.TransferTypeMakerFeeReceive { + recv++ + assert.Equal(t, num.NewUint(125), v.Amount.Amount) + } + if v.Type == types.TransferTypeMakerFeePay { + pay++ + assert.Equal(t, num.NewUint(125), v.Amount.Amount) + } + if v.Type == types.TransferTypeBuyBackFeePay { + bb++ + assert.Equal(t, num.NewUint(500), v.Amount.Amount) + } + if v.Type == types.TransferTypeTreasuryPay { + treasury++ + assert.Equal(t, num.NewUint(750), v.Amount.Amount) + } + if v.Type == types.TransferTypeHighMakerRebatePay { + hmp++ + assert.Equal(t, num.NewUint(1250), v.Amount.Amount) + } + if v.Type == types.TransferTypeHighMakerRebateReceive { + hmr++ + assert.Equal(t, num.NewUint(1250), v.Amount.Amount) + } + } + + assert.Equal(t, liquidity, 1) + assert.Equal(t, infra, 1) + assert.Equal(t, bb, 1) + assert.Equal(t, treasury, 1) + assert.Equal(t, hmp, 1) + assert.Equal(t, hmr, 1) + assert.Equal(t, recv, len(trades)) + assert.Equal(t, pay, len(trades)) + assert.Equal(t, &eventspb.FeesStats{ + Market: "", + Asset: testAsset, + EpochSeq: 0, + TotalRewardsReceived: []*eventspb.PartyAmount{}, + ReferrerRewardsGenerated: []*eventspb.ReferrerRewardsGenerated{}, + RefereesDiscountApplied: []*eventspb.PartyAmount{ + { + Party: "party1", + Amount: "0", + QuantumAmount: "0", + }, + }, + VolumeDiscountApplied: []*eventspb.PartyAmount{ + { + Party: "party1", + Amount: "0", + QuantumAmount: "0", + }, + }, + TotalMakerFeesReceived: []*eventspb.PartyAmount{ + { + Party: "party2", + Amount: "125", + QuantumAmount: "125", + }, + }, + MakerFeesGenerated: []*eventspb.MakerFeesGenerated{ + { + Taker: "party1", + MakerFeesPaid: []*eventspb.PartyAmount{ + { + Party: "party2", + Amount: "125", + QuantumAmount: "125", + }, + }, + }, + }, + TotalFeesPaidAndReceived: []*eventspb.PartyAmount{ + { + Party: "party1", + Amount: "875", + QuantumAmount: "875", + }, + { + Party: "party2", + Amount: "125", + QuantumAmount: "125", + }, + }, + }, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1))) +} + func testCalcContinuousTradingAndCheckAmountsWithDiscount(t *testing.T) { eng := getTestFee(t) ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.3)).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalFromFloat(0.2)).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.1)).AnyTimes() + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + }).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.2), + Maker: num.NewDecimalFromFloat(0.2), + Liquidity: num.NewDecimalFromFloat(0.2), + }).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return( + types.Factors{ + Infra: num.NewDecimalFromFloat(0.1), + Maker: num.NewDecimalFromFloat(0.1), + Liquidity: num.NewDecimalFromFloat(0.1), + }) discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party3"), nil).AnyTimes() require.NoError(t, eng.UpdateFeeFactors(types.Fees{ Factors: &types.FeeFactors{ @@ -270,7 +499,7 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscount(t *testing.T) { }, } - ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) transfers := ft.Transfers() @@ -367,6 +596,166 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscount(t *testing.T) { }, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1))) } +func testCalcContinuousTradingAndCheckAmountsWithDiscountExtended(t *testing.T) { + eng := getExtendedTestFee(t) + ctrl := gomock.NewController(t) + discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) + volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.0025)).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + }).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.2), + Maker: num.NewDecimalFromFloat(0.2), + Liquidity: num.NewDecimalFromFloat(0.2), + }).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return( + types.Factors{ + Infra: num.NewDecimalFromFloat(0.1), + Maker: num.NewDecimalFromFloat(0.1), + Liquidity: num.NewDecimalFromFloat(0.1), + }) + discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party3"), nil).AnyTimes() + require.NoError(t, eng.UpdateFeeFactors(types.Fees{ + Factors: &types.FeeFactors{ + MakerFee: num.DecimalFromFloat(.000250), + InfrastructureFee: num.DecimalFromFloat(0.0005), + LiquidityFee: num.DecimalFromFloat(0.001), + BuyBackFee: num.DecimalFromFloat(0.002), + TreasuryFee: num.DecimalFromFloat(0.003), + }, + })) + trades := []*types.Trade{ + { + Aggressor: types.SideSell, + Seller: "party1", + Buyer: "party2", + Size: 5, + Price: num.NewUint(100000), + }, + } + + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) + assert.NotNil(t, ft) + assert.Nil(t, err) + transfers := ft.Transfers() + var pay, recv, infra, liquidity, bb, treasury, hmp, hmr int + for _, v := range transfers { + if v.Type == types.TransferTypeLiquidityFeePay { + liquidity++ + assert.Equal(t, num.NewUint(252), v.Amount.Amount) + } + if v.Type == types.TransferTypeInfrastructureFeePay { + infra++ + assert.Equal(t, num.NewUint(127), v.Amount.Amount) + } + if v.Type == types.TransferTypeMakerFeeReceive { + recv++ + assert.Equal(t, num.NewUint(64), v.Amount.Amount) + } + if v.Type == types.TransferTypeMakerFeePay { + pay++ + assert.Equal(t, num.NewUint(64), v.Amount.Amount) + } + if v.Type == types.TransferTypeBuyBackFeePay { + bb++ + assert.Equal(t, num.NewUint(500), v.Amount.Amount) + } + if v.Type == types.TransferTypeTreasuryPay { + treasury++ + assert.Equal(t, num.NewUint(750), v.Amount.Amount) + } + if v.Type == types.TransferTypeHighMakerRebatePay { + hmp++ + assert.Equal(t, num.NewUint(1250), v.Amount.Amount) + } + if v.Type == types.TransferTypeHighMakerRebateReceive { + hmr++ + assert.Equal(t, num.NewUint(1250), v.Amount.Amount) + } + } + + assert.Equal(t, liquidity, 1) + assert.Equal(t, infra, 1) + assert.Equal(t, bb, 1) + assert.Equal(t, treasury, 1) + assert.Equal(t, hmp, 1) + assert.Equal(t, hmr, 1) + assert.Equal(t, recv, len(trades)) + assert.Equal(t, pay, len(trades)) + assert.Equal(t, &eventspb.FeesStats{ + Asset: testAsset, + TotalRewardsReceived: []*eventspb.PartyAmount{ + { + Party: "party3", + Amount: "110", + QuantumAmount: "110", + }, + }, + ReferrerRewardsGenerated: []*eventspb.ReferrerRewardsGenerated{ + { + Referrer: "party3", + GeneratedReward: []*eventspb.PartyAmount{ + { + Party: "party1", + Amount: "110", + QuantumAmount: "110", + }, + }, + }, + }, + RefereesDiscountApplied: []*eventspb.PartyAmount{ + { + Party: "party1", + Amount: "262", + QuantumAmount: "262", + }, + }, + VolumeDiscountApplied: []*eventspb.PartyAmount{ + { + Party: "party1", + Amount: "60", + QuantumAmount: "60", + }, + }, + TotalMakerFeesReceived: []*eventspb.PartyAmount{ + { + Party: "party2", + Amount: "64", + QuantumAmount: "64", + }, + }, + MakerFeesGenerated: []*eventspb.MakerFeesGenerated{ + { + Taker: "party1", + MakerFeesPaid: []*eventspb.PartyAmount{ + { + Party: "party2", + Amount: "64", + QuantumAmount: "64", + }, + }, + }, + }, + TotalFeesPaidAndReceived: []*eventspb.PartyAmount{ + { + Party: "party1", + Amount: "443", + QuantumAmount: "443", + }, + { + Party: "party2", + Amount: "64", + QuantumAmount: "64", + }, + }, + }, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1))) +} + func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySide(t *testing.T, aggressorSide types.Side) { t.Helper() eng := getTestFee(t) @@ -374,6 +763,8 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySide(t *te discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() eng.UpdateFeeFactors(types.Fees{ Factors: &types.FeeFactors{ @@ -398,13 +789,29 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySide(t *te aggressor = "party2" } - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.5)).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.25)).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.5), + Maker: num.NewDecimalFromFloat(0.5), + Liquidity: num.NewDecimalFromFloat(0.5), + }).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.25), + Maker: num.NewDecimalFromFloat(0.25), + Liquidity: num.NewDecimalFromFloat(0.25), + }) discountRewardService.EXPECT().GetReferrer(types.PartyID(aggressor)).Return(types.PartyID("referrer"), nil).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(types.PartyID("party1")).Return(num.DecimalFromFloat(0.3)).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(types.PartyID("party2")).Return(num.DecimalFromFloat(0.3)).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party1")).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + }).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party2")).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + }).AnyTimes() - ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) transfers := ft.Transfers() @@ -466,6 +873,8 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySideMultip discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() eng.UpdateFeeFactors(types.Fees{ Factors: &types.FeeFactors{ @@ -504,14 +913,30 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySideMultip aggressor = "party2" } - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.5)).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.25)).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.5), + Maker: num.NewDecimalFromFloat(0.5), + Liquidity: num.NewDecimalFromFloat(0.5), + }).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.25), + Maker: num.NewDecimalFromFloat(0.25), + Liquidity: num.NewDecimalFromFloat(0.25), + }).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("referrer"), nil).AnyTimes() discountRewardService.EXPECT().GetReferrer(types.PartyID(aggressor)).Return(types.PartyID("referrer"), nil).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(aggressor).Return(num.DecimalFromFloat(0.3)).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalFromFloat(0.3)).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(aggressor).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + }).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + }).AnyTimes() - ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) transfers := ft.Transfers() @@ -639,9 +1064,11 @@ func testCalcContinuousTrading(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party1"), errors.New("not a referrer")).AnyTimes() trades := []*types.Trade{ @@ -682,7 +1109,7 @@ func testCalcContinuousTrading(t *testing.T) { }, } - ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) @@ -716,12 +1143,121 @@ func testCalcContinuousTrading(t *testing.T) { assert.Equal(t, pay, len(trades)) } +func testCalcContinuousTradingExtended(t *testing.T) { + eng := getExtendedTestFee(t) + ctrl := gomock.NewController(t) + discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) + volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party2")).Return(num.DecimalFromFloat(0.00005)).AnyTimes() + volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party3")).Return(num.DecimalZero()).AnyTimes() + volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party4")).Return(num.DecimalZero()).AnyTimes() + volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party5")).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party1"), errors.New("not a referrer")).AnyTimes() + + trades := []*types.Trade{ + { + Aggressor: types.SideSell, + Seller: "party1", + Buyer: "party2", + Size: 10, + Price: num.NewUint(10000), + }, + { + Aggressor: types.SideSell, + Seller: "party1", + Buyer: "party3", + Size: 1, + Price: num.NewUint(10300), + }, + { + Aggressor: types.SideSell, + Seller: "party1", + Buyer: "party4", + Size: 7, + Price: num.NewUint(10300), + }, + { + Aggressor: types.SideSell, + Seller: "party1", + Buyer: "party2", + Size: 2, + Price: num.NewUint(10500), + }, + { + Aggressor: types.SideSell, + Seller: "party1", + Buyer: "party5", + Size: 5, + Price: num.NewUint(11000), + }, + } + + ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) + assert.NotNil(t, ft) + assert.Nil(t, err) + + // get the amounts map + feeAmounts := ft.TotalFeesAmountPerParty() + party1Amount, ok := feeAmounts["party1"] + assert.True(t, ok) + assert.Equal(t, num.NewUint(43928), party1Amount) + + // get the transfer and check we have enough of each types + transfers := ft.Transfers() + var pay, recv, infra, liquidity, highMakerPay, highMakerReceive, bb, treasury int + for _, v := range transfers { + if v.Type == types.TransferTypeLiquidityFeePay { + liquidity++ + } + if v.Type == types.TransferTypeInfrastructureFeePay { + infra++ + } + if v.Type == types.TransferTypeMakerFeeReceive { + recv++ + } + if v.Type == types.TransferTypeMakerFeePay { + pay++ + } + if v.Type == types.TransferTypeHighMakerRebatePay { + highMakerPay++ + } + if v.Type == types.TransferTypeHighMakerRebateReceive { + highMakerReceive++ + } + if v.Type == types.TransferTypeBuyBackFeePay { + bb++ + } + if v.Type == types.TransferTypeTreasuryPay { + treasury++ + } + } + + assert.Equal(t, liquidity, 1) + assert.Equal(t, infra, 1) + assert.Equal(t, highMakerPay, 2) + assert.Equal(t, highMakerReceive, 2) + assert.Equal(t, recv, len(trades)) + assert.Equal(t, pay, len(trades)) + assert.Equal(t, bb, len(trades)) + assert.Equal(t, treasury, len(trades)) +} + func testCalcAuctionTradingErrorEmptyTrade(t *testing.T) { eng := getTestFee(t) ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - _, err := eng.CalculateForAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + _, err := eng.CalculateForAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) + assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) + + eng = getExtendedTestFee(t) + _, err = eng.CalculateForAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) } @@ -730,9 +1266,11 @@ func testCalcAuctionTrading(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() trades := []*types.Trade{ { @@ -744,7 +1282,7 @@ func testCalcAuctionTrading(t *testing.T) { }, } - ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) @@ -785,23 +1323,115 @@ func testCalcAuctionTrading(t *testing.T) { assert.Equal(t, pay, 0) } +func testCalcAuctionTradingExtended(t *testing.T) { + eng := getExtendedTestFee(t) + ctrl := gomock.NewController(t) + discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) + volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.0025)).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() + trades := []*types.Trade{ + { + Aggressor: types.SideSell, + Seller: "party1", + Buyer: "party2", + Size: 1, + Price: num.NewUint(100), + }, + } + + ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) + assert.NotNil(t, ft) + assert.Nil(t, err) + + // get the amounts map + feeAmounts := ft.TotalFeesAmountPerParty() + // fees are (100 * 0.1 + 100 * 0.05) = 15 + // 15 / 2 = 7.5 + // internally the engine Ceil all fees. + // so here we will expect 8 for each + party1Amount, ok := feeAmounts["party1"] + assert.True(t, ok) + assert.Equal(t, num.NewUint(8), party1Amount) + party2Amount, ok := feeAmounts["party2"] + assert.True(t, ok) + assert.Equal(t, num.NewUint(8), party2Amount) + + // get the transfer and check we have enough of each types + transfers := ft.Transfers() + var pay, recv, infra, liquidity, hmp, hmr, bb, treasury int + for _, v := range transfers { + if v.Type == types.TransferTypeLiquidityFeePay { + liquidity++ + } + if v.Type == types.TransferTypeInfrastructureFeePay { + infra++ + } + if v.Type == types.TransferTypeMakerFeeReceive { + recv++ + } + if v.Type == types.TransferTypeMakerFeePay { + pay++ + } + if v.Type == types.TransferTypeHighMakerRebatePay { + hmp++ + } + if v.Type == types.TransferTypeHighMakerRebateReceive { + hmr++ + } + if v.Type == types.TransferTypeBuyBackFeePay { + bb++ + } + if v.Type == types.TransferTypeTreasuryPay { + treasury++ + } + } + + assert.Equal(t, 2, liquidity) + assert.Equal(t, 2, infra) + assert.Equal(t, 0, recv) + assert.Equal(t, 0, pay) + assert.Equal(t, 0, hmp) + assert.Equal(t, 0, hmr) + assert.Equal(t, 2, bb) + assert.Equal(t, 2, treasury) +} + func TestCalcAuctionTradingWithDiscountsAndRewards(t *testing.T) { eng := getTestFee(t) ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).DoAndReturn(func(p types.PartyID) num.Decimal { + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).DoAndReturn(func(p types.PartyID) types.Factors { if p == types.PartyID("party1") { - return num.DecimalZero() + return types.EmptyFactors } else { - return num.NewDecimalFromFloat(0.5) + return types.Factors{ + Infra: num.NewDecimalFromFloat(0.5), + Maker: num.NewDecimalFromFloat(0.5), + Liquidity: num.NewDecimalFromFloat(0.5), + } } }).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).DoAndReturn(func(p types.PartyID) num.Decimal { + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).DoAndReturn(func(p types.PartyID) types.Factors { if p == types.PartyID("party1") { - return num.NewDecimalFromFloat(0.2) + return types.Factors{ + Infra: num.NewDecimalFromFloat(0.2), + Maker: num.NewDecimalFromFloat(0.2), + Liquidity: num.NewDecimalFromFloat(0.2), + } } else { - return num.NewDecimalFromFloat(0.3) + return types.Factors{ + Infra: num.NewDecimalFromFloat(0.3), + Maker: num.NewDecimalFromFloat(0.3), + Liquidity: num.NewDecimalFromFloat(0.3), + } } }).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).DoAndReturn(func(p types.PartyID) (types.PartyID, error) { @@ -811,8 +1441,12 @@ func TestCalcAuctionTradingWithDiscountsAndRewards(t *testing.T) { return types.PartyID(""), errors.New("No referrer") } }).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(types.PartyID("party1")).Return(num.DecimalFromFloat(0.5)).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(types.PartyID("party2")).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party1")).Return(types.Factors{ + Infra: num.NewDecimalFromFloat(0.5), + Maker: num.NewDecimalFromFloat(0.5), + Liquidity: num.NewDecimalFromFloat(0.5), + }).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party2")).Return(types.EmptyFactors).AnyTimes() trades := []*types.Trade{ { @@ -824,7 +1458,7 @@ func TestCalcAuctionTradingWithDiscountsAndRewards(t *testing.T) { }, } - ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) @@ -898,7 +1532,13 @@ func testCalcBatchAuctionTradingErrorEmptyTrade(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - _, err := eng.CalculateForFrequentBatchesAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService) + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + _, err := eng.CalculateForFrequentBatchesAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) + assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) + + eng = getExtendedTestFee(t) + _, err = eng.CalculateForFrequentBatchesAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) } @@ -907,9 +1547,11 @@ func testCalcBatchAuctionTradingSameBatch(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() trades := []*types.Trade{ { @@ -923,7 +1565,7 @@ func testCalcBatchAuctionTradingSameBatch(t *testing.T) { }, } - ft, err := eng.CalculateForFrequentBatchesAuctionMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForFrequentBatchesAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) @@ -969,9 +1611,11 @@ func testCalcBatchAuctionTradingDifferentBatches(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) + volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() trades := []*types.Trade{ { @@ -985,7 +1629,7 @@ func testCalcBatchAuctionTradingDifferentBatches(t *testing.T) { }, } - ft, err := eng.CalculateForFrequentBatchesAuctionMode(trades, discountRewardService, volumeDiscountService) + ft, err := eng.CalculateForFrequentBatchesAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) assert.NotNil(t, ft) assert.Nil(t, err) @@ -1028,9 +1672,9 @@ func testCloseoutFees(t *testing.T) { ctrl := gomock.NewController(t) discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) - discountRewardService.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - discountRewardService.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() trades := []*types.Trade{ { @@ -1067,9 +1711,10 @@ func testCloseoutFees(t *testing.T) { assert.NotNil(t, fee) allTransfers := ft.Transfers() // first we have the network -> pay transfers, then 1 transfer per good party - assert.Equal(t, len(trades), len(allTransfers)-3) - goodPartyTransfers := allTransfers[3:] - networkTransfers := allTransfers[:3] + // two additional transfers for the buy back and treasury fees + assert.Equal(t, len(trades), len(allTransfers)-5) + goodPartyTransfers := allTransfers[5:] + networkTransfers := allTransfers[:5] numTrades := num.NewUint(uint64(len(trades))) // maker fee is 100 * 0.02 == 2 diff --git a/core/fee/mocks/mocks.go b/core/fee/mocks/mocks.go index 8b4f5a3a07f..b6cbae9005c 100644 --- a/core/fee/mocks/mocks.go +++ b/core/fee/mocks/mocks.go @@ -1,5 +1,5 @@ // Code generated by MockGen. DO NOT EDIT. -// Source: code.vegaprotocol.io/vega/core/fee (interfaces: ReferralDiscountRewardService,VolumeDiscountService) +// Source: code.vegaprotocol.io/vega/core/fee (interfaces: ReferralDiscountRewardService,VolumeDiscountService,VolumeRebateService) // Package mocks is a generated GoMock package. package mocks @@ -50,32 +50,32 @@ func (mr *MockReferralDiscountRewardServiceMockRecorder) GetReferrer(arg0 interf return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetReferrer", reflect.TypeOf((*MockReferralDiscountRewardService)(nil).GetReferrer), arg0) } -// ReferralDiscountFactorForParty mocks base method. -func (m *MockReferralDiscountRewardService) ReferralDiscountFactorForParty(arg0 types.PartyID) decimal.Decimal { +// ReferralDiscountFactorsForParty mocks base method. +func (m *MockReferralDiscountRewardService) ReferralDiscountFactorsForParty(arg0 types.PartyID) types.Factors { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "ReferralDiscountFactorForParty", arg0) - ret0, _ := ret[0].(decimal.Decimal) + ret := m.ctrl.Call(m, "ReferralDiscountFactorsForParty", arg0) + ret0, _ := ret[0].(types.Factors) return ret0 } -// ReferralDiscountFactorForParty indicates an expected call of ReferralDiscountFactorForParty. -func (mr *MockReferralDiscountRewardServiceMockRecorder) ReferralDiscountFactorForParty(arg0 interface{}) *gomock.Call { +// ReferralDiscountFactorsForParty indicates an expected call of ReferralDiscountFactorsForParty. +func (mr *MockReferralDiscountRewardServiceMockRecorder) ReferralDiscountFactorsForParty(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReferralDiscountFactorForParty", reflect.TypeOf((*MockReferralDiscountRewardService)(nil).ReferralDiscountFactorForParty), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReferralDiscountFactorsForParty", reflect.TypeOf((*MockReferralDiscountRewardService)(nil).ReferralDiscountFactorsForParty), arg0) } -// RewardsFactorMultiplierAppliedForParty mocks base method. -func (m *MockReferralDiscountRewardService) RewardsFactorMultiplierAppliedForParty(arg0 types.PartyID) decimal.Decimal { +// RewardsFactorsMultiplierAppliedForParty mocks base method. +func (m *MockReferralDiscountRewardService) RewardsFactorsMultiplierAppliedForParty(arg0 types.PartyID) types.Factors { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "RewardsFactorMultiplierAppliedForParty", arg0) - ret0, _ := ret[0].(decimal.Decimal) + ret := m.ctrl.Call(m, "RewardsFactorsMultiplierAppliedForParty", arg0) + ret0, _ := ret[0].(types.Factors) return ret0 } -// RewardsFactorMultiplierAppliedForParty indicates an expected call of RewardsFactorMultiplierAppliedForParty. -func (mr *MockReferralDiscountRewardServiceMockRecorder) RewardsFactorMultiplierAppliedForParty(arg0 interface{}) *gomock.Call { +// RewardsFactorsMultiplierAppliedForParty indicates an expected call of RewardsFactorsMultiplierAppliedForParty. +func (mr *MockReferralDiscountRewardServiceMockRecorder) RewardsFactorsMultiplierAppliedForParty(arg0 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RewardsFactorMultiplierAppliedForParty", reflect.TypeOf((*MockReferralDiscountRewardService)(nil).RewardsFactorMultiplierAppliedForParty), arg0) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RewardsFactorsMultiplierAppliedForParty", reflect.TypeOf((*MockReferralDiscountRewardService)(nil).RewardsFactorsMultiplierAppliedForParty), arg0) } // MockVolumeDiscountService is a mock of VolumeDiscountService interface. @@ -102,10 +102,10 @@ func (m *MockVolumeDiscountService) EXPECT() *MockVolumeDiscountServiceMockRecor } // VolumeDiscountFactorForParty mocks base method. -func (m *MockVolumeDiscountService) VolumeDiscountFactorForParty(arg0 types.PartyID) decimal.Decimal { +func (m *MockVolumeDiscountService) VolumeDiscountFactorForParty(arg0 types.PartyID) types.Factors { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "VolumeDiscountFactorForParty", arg0) - ret0, _ := ret[0].(decimal.Decimal) + ret0, _ := ret[0].(types.Factors) return ret0 } @@ -114,3 +114,40 @@ func (mr *MockVolumeDiscountServiceMockRecorder) VolumeDiscountFactorForParty(ar mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeDiscountFactorForParty", reflect.TypeOf((*MockVolumeDiscountService)(nil).VolumeDiscountFactorForParty), arg0) } + +// MockVolumeRebateService is a mock of VolumeRebateService interface. +type MockVolumeRebateService struct { + ctrl *gomock.Controller + recorder *MockVolumeRebateServiceMockRecorder +} + +// MockVolumeRebateServiceMockRecorder is the mock recorder for MockVolumeRebateService. +type MockVolumeRebateServiceMockRecorder struct { + mock *MockVolumeRebateService +} + +// NewMockVolumeRebateService creates a new mock instance. +func NewMockVolumeRebateService(ctrl *gomock.Controller) *MockVolumeRebateService { + mock := &MockVolumeRebateService{ctrl: ctrl} + mock.recorder = &MockVolumeRebateServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockVolumeRebateService) EXPECT() *MockVolumeRebateServiceMockRecorder { + return m.recorder +} + +// VolumeRebateFactorForParty mocks base method. +func (m *MockVolumeRebateService) VolumeRebateFactorForParty(arg0 types.PartyID) decimal.Decimal { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "VolumeRebateFactorForParty", arg0) + ret0, _ := ret[0].(decimal.Decimal) + return ret0 +} + +// VolumeRebateFactorForParty indicates an expected call of VolumeRebateFactorForParty. +func (mr *MockVolumeRebateServiceMockRecorder) VolumeRebateFactorForParty(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "VolumeRebateFactorForParty", reflect.TypeOf((*MockVolumeRebateService)(nil).VolumeRebateFactorForParty), arg0) +} diff --git a/core/governance/engine.go b/core/governance/engine.go index 87c8fc57446..ece08bc28aa 100644 --- a/core/governance/engine.go +++ b/core/governance/engine.go @@ -296,6 +296,8 @@ func (e *Engine) preEnactProposal(ctx context.Context, p *proposal) (te *ToEnact te.referralProgramChanges = updatedReferralProgramFromProposal(p) case types.ProposalTermsTypeUpdateVolumeDiscountProgram: te.volumeDiscountProgram = updatedVolumeDiscountProgramFromProposal(p) + case types.ProposalTermsTypeUpdateVolumeRebateProgram: + te.volumeRebateProgram = updatedVolumeRebateProgramFromProposal(p) } return //nolint:nakedret } @@ -741,6 +743,8 @@ func (e *Engine) getProposalParams(proposalTerm types.ProposalTerm) (*types.Prop return e.getReferralProgramNetworkParameters(), nil case types.ProposalTermsTypeUpdateVolumeDiscountProgram: return e.getVolumeDiscountProgramNetworkParameters(), nil + case types.ProposalTermsTypeUpdateVolumeRebateProgram: + return e.getVolumeRebateProgramNetworkParameters(), nil default: return nil, ErrUnsupportedProposalType } @@ -1091,6 +1095,8 @@ func (e *Engine) validateChange(terms *types.ProposalTerms) (types.ProposalError return validateUpdateReferralProgram(e.netp, terms.GetUpdateReferralProgram(), terms.EnactmentTimestamp) case types.ProposalTermsTypeUpdateVolumeDiscountProgram: return validateUpdateVolumeDiscountProgram(e.netp, terms.GetUpdateVolumeDiscountProgram()) + case types.ProposalTermsTypeUpdateVolumeRebateProgram: + return validateUpdateVolumeRebateProgram(e.netp, terms.GetUpdateVolumeRebateProgram()) default: return types.ProposalErrorUnspecified, nil } diff --git a/core/governance/engine_test.go b/core/governance/engine_test.go index 04bf7958afa..0485dc06452 100644 --- a/core/governance/engine_test.go +++ b/core/governance/engine_test.go @@ -2207,6 +2207,30 @@ func (e *tstEngine) newProposalForReferralProgramUpdate(partyID string, now time return prop } +func (e *tstEngine) newProposalForVolumeRebateProgramUpdate(partyID string, now time.Time, configuration *types.VolumeRebateProgramChanges) types.Proposal { + id := e.newProposalID() + prop := types.Proposal{ + ID: id, + Reference: "ref-" + id, + Party: partyID, + State: types.ProposalStateOpen, + Terms: &types.ProposalTerms{ + ClosingTimestamp: now.Add(96 * time.Hour).Unix(), + EnactmentTimestamp: now.Add(4 * 48 * time.Hour).Unix(), + ValidationTimestamp: now.Add(2 * time.Hour).Unix(), + Change: &types.ProposalTermsUpdateVolumeRebateProgram{ + UpdateVolumeRebateProgram: &types.UpdateVolumeRebateProgram{ + Changes: configuration, + }, + }, + }, + Rationale: &types.ProposalRationale{ + Description: "some description", + }, + } + return prop +} + func (e *tstEngine) newProposalForVolumeDiscountProgramUpdate(partyID string, now time.Time, configuration *types.VolumeDiscountProgramChanges) types.Proposal { id := e.newProposalID() prop := types.Proposal{ diff --git a/core/governance/engine_update_discount_volume_program_test.go b/core/governance/engine_update_discount_volume_program_test.go index e6c82993b93..b97ebfbd4a9 100644 --- a/core/governance/engine_update_discount_volume_program_test.go +++ b/core/governance/engine_update_discount_volume_program_test.go @@ -61,10 +61,18 @@ func testSubmittingProposalForVolumeDiscountProgramUpdateSucceeds(t *testing.T) VolumeBenefitTiers: []*types.VolumeBenefitTier{ { MinimumRunningNotionalTakerVolume: num.NewUint(10000), - VolumeDiscountFactor: num.DecimalFromFloat(0.001), + VolumeDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumRunningNotionalTakerVolume: num.NewUint(20000), - VolumeDiscountFactor: num.DecimalFromFloat(0.005), + VolumeDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, }, }, }) @@ -108,10 +116,18 @@ func testSubmittingProposalForVolumeDiscountProgramUpdateWithTooManyTiersFails(t VolumeBenefitTiers: []*types.VolumeBenefitTier{ { MinimumRunningNotionalTakerVolume: num.NewUint(10000), - VolumeDiscountFactor: num.DecimalFromFloat(0.001), + VolumeDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumRunningNotionalTakerVolume: num.NewUint(20000), - VolumeDiscountFactor: num.DecimalFromFloat(0.005), + VolumeDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, }, }, }) @@ -155,10 +171,18 @@ func testSubmittingProposalForVolumeDiscountProgramUpdateWithTooHighDiscountFact VolumeBenefitTiers: []*types.VolumeBenefitTier{ { MinimumRunningNotionalTakerVolume: num.NewUint(10000), - VolumeDiscountFactor: num.DecimalFromFloat(0.001), + VolumeDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumRunningNotionalTakerVolume: num.NewUint(20000), - VolumeDiscountFactor: num.DecimalFromFloat(0.015), + VolumeDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.015), + Maker: num.DecimalFromFloat(0.015), + Liquidity: num.DecimalFromFloat(0.015), + }, }, }, }) @@ -175,7 +199,7 @@ func testSubmittingProposalForVolumeDiscountProgramUpdateWithTooHighDiscountFact // then require.EqualError(t, err, - "tier 2 defines a volume discount factor higher than the maximum allowed by the network parameter \"volumeDiscountProgram.maxVolumeDiscountFactor\": maximum is 0.01, but got 0.015", + "tier 2 defines a volume discount infrastructure factor higher than the maximum allowed by the network parameter \"volumeDiscountProgram.maxVolumeDiscountFactor\": maximum is 0.01, but got 0.015", ) require.Nil(t, toSubmit) } diff --git a/core/governance/engine_update_referral_program_test.go b/core/governance/engine_update_referral_program_test.go index d5550a37a20..4bf5f1fea1f 100644 --- a/core/governance/engine_update_referral_program_test.go +++ b/core/governance/engine_update_referral_program_test.go @@ -67,13 +67,29 @@ func testSubmittingProposalForReferralProgramUpdateSucceeds(t *testing.T) { { MinimumEpochs: num.NewUint(1), MinimumRunningNotionalTakerVolume: num.NewUint(10000), - ReferralRewardFactor: num.DecimalFromFloat(0.001), - ReferralDiscountFactor: num.DecimalFromFloat(0.001), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumEpochs: num.NewUint(7), MinimumRunningNotionalTakerVolume: num.NewUint(20000), - ReferralRewardFactor: num.DecimalFromFloat(0.005), - ReferralDiscountFactor: num.DecimalFromFloat(0.005), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, }, }, }) @@ -121,13 +137,29 @@ func testSubmittingProposalForReferralProgramUpdateWithTooManyTiersFails(t *test { MinimumEpochs: num.NewUint(1), MinimumRunningNotionalTakerVolume: num.NewUint(10000), - ReferralRewardFactor: num.DecimalFromFloat(0.001), - ReferralDiscountFactor: num.DecimalFromFloat(0.001), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumEpochs: num.NewUint(7), MinimumRunningNotionalTakerVolume: num.NewUint(20000), - ReferralRewardFactor: num.DecimalFromFloat(0.005), - ReferralDiscountFactor: num.DecimalFromFloat(0.005), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, }, }, }) @@ -175,13 +207,29 @@ func testSubmittingProposalForReferralProgramUpdateWithTooHighRewardFactorFails( { MinimumEpochs: num.NewUint(1), MinimumRunningNotionalTakerVolume: num.NewUint(10000), - ReferralRewardFactor: num.DecimalFromFloat(0.001), - ReferralDiscountFactor: num.DecimalFromFloat(0.001), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumEpochs: num.NewUint(7), MinimumRunningNotionalTakerVolume: num.NewUint(20000), - ReferralRewardFactor: num.DecimalFromFloat(0.015), - ReferralDiscountFactor: num.DecimalFromFloat(0.005), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.015), + Maker: num.DecimalFromFloat(0.015), + Liquidity: num.DecimalFromFloat(0.015), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.005), + Maker: num.DecimalFromFloat(0.005), + Liquidity: num.DecimalFromFloat(0.005), + }, }, }, }) @@ -198,7 +246,7 @@ func testSubmittingProposalForReferralProgramUpdateWithTooHighRewardFactorFails( // then require.EqualError(t, err, - "tier 2 defines a referral reward factor higher than the maximum allowed by the network parameter \"referralProgram.maxReferralRewardFactor\": maximum is 0.01, but got 0.015", + "tier 2 defines a referral reward infrastructure factor higher than the maximum allowed by the network parameter \"referralProgram.maxReferralRewardFactor\": maximum is 0.01, but got 0.015", ) require.Nil(t, toSubmit) } @@ -232,13 +280,29 @@ func testSubmittingProposalForReferralProgramUpdateWithTooHighDiscountFactorFail { MinimumEpochs: num.NewUint(1), MinimumRunningNotionalTakerVolume: num.NewUint(10000), - ReferralRewardFactor: num.DecimalFromFloat(0.001), - ReferralDiscountFactor: num.DecimalFromFloat(0.001), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumEpochs: num.NewUint(7), MinimumRunningNotionalTakerVolume: num.NewUint(20000), - ReferralRewardFactor: num.DecimalFromFloat(0.010), - ReferralDiscountFactor: num.DecimalFromFloat(0.015), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.01), + Maker: num.DecimalFromFloat(0.01), + Liquidity: num.DecimalFromFloat(0.01), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.015), + Maker: num.DecimalFromFloat(0.015), + Liquidity: num.DecimalFromFloat(0.015), + }, }, }, }) @@ -255,7 +319,7 @@ func testSubmittingProposalForReferralProgramUpdateWithTooHighDiscountFactorFail // then require.EqualError(t, err, - "tier 2 defines a referral discount factor higher than the maximum allowed by the network parameter \"referralProgram.maxReferralDiscountFactor\": maximum is 0.01, but got 0.015", + "tier 2 defines a referral discount infrastructure factor higher than the maximum allowed by the network parameter \"referralProgram.maxReferralDiscountFactor\": maximum is 0.01, but got 0.015", ) require.Nil(t, toSubmit) } @@ -291,13 +355,29 @@ func testSubmittingProposalForReferralProgramUpdateEndsBeforeEnactsFails(t *test { MinimumEpochs: num.NewUint(1), MinimumRunningNotionalTakerVolume: num.NewUint(10000), - ReferralRewardFactor: num.DecimalFromFloat(0.001), - ReferralDiscountFactor: num.DecimalFromFloat(0.001), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.001), + Maker: num.DecimalFromFloat(0.001), + Liquidity: num.DecimalFromFloat(0.001), + }, }, { MinimumEpochs: num.NewUint(7), MinimumRunningNotionalTakerVolume: num.NewUint(20000), - ReferralRewardFactor: num.DecimalFromFloat(0.010), - ReferralDiscountFactor: num.DecimalFromFloat(0.015), + ReferralRewardFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.01), + Maker: num.DecimalFromFloat(0.01), + Liquidity: num.DecimalFromFloat(0.01), + }, + ReferralDiscountFactors: types.Factors{ + Infra: num.DecimalFromFloat(0.015), + Maker: num.DecimalFromFloat(0.015), + Liquidity: num.DecimalFromFloat(0.015), + }, }, }, }, diff --git a/core/governance/engine_update_volume_rebate_program_test.go b/core/governance/engine_update_volume_rebate_program_test.go new file mode 100644 index 00000000000..9e0ee1f0dc2 --- /dev/null +++ b/core/governance/engine_update_volume_rebate_program_test.go @@ -0,0 +1,124 @@ +// Copyright (C) 2023 Gobalsky Labs Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package governance_test + +import ( + "testing" + "time" + + "code.vegaprotocol.io/vega/core/events" + "code.vegaprotocol.io/vega/core/netparams" + "code.vegaprotocol.io/vega/core/types" + "code.vegaprotocol.io/vega/libs/num" + vgrand "code.vegaprotocol.io/vega/libs/rand" + vgtest "code.vegaprotocol.io/vega/libs/test" + + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func TestProposalForUpdateVolumeRebateProgram(t *testing.T) { + t.Run("Submitting a proposal for referral program update succeeds", testSubmittingProposalForVolumeRebateProgramUpdateSucceeds) + t.Run("Submitting a proposal for referral program update with too many tiers fails", testSubmittingProposalForVolumeRebateProgramUpdateWithTooManyTiersFails) +} + +func testSubmittingProposalForVolumeRebateProgramUpdateSucceeds(t *testing.T) { + now := time.Now() + ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomPositiveI64()) + eng := getTestEngine(t, now) + + // setup + eng.broker.EXPECT().Send(gomock.Any()).Times(3) + eng.netp.Update(ctx, netparams.GovernanceProposalVolumeRebateProgramMinClose, "48h") + eng.netp.Update(ctx, netparams.GovernanceProposalVolumeRebateProgramMinEnact, "48h") + eng.netp.Update(ctx, netparams.GovernanceProposalVolumeRebateProgramMinProposerBalance, "1000") + + eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.VolumeRebateProgramMaxBenefitTiers, "2")).Times(1) + require.NoError(t, eng.netp.Update(ctx, netparams.VolumeRebateProgramMaxBenefitTiers, "2")) + + // given + proposer := vgrand.RandomStr(5) + proposal := eng.newProposalForVolumeRebateProgramUpdate(proposer, now, &types.VolumeRebateProgramChanges{ + EndOfProgramTimestamp: now.Add(4 * 48 * time.Hour), + WindowLength: 15, + VolumeRebateBenefitTiers: []*types.VolumeRebateBenefitTier{ + { + MinimumPartyMakerVolumeFraction: num.DecimalFromFloat(0.1), + AdditionalMakerRebate: num.DecimalFromFloat(0.00001), + }, { + MinimumPartyMakerVolumeFraction: num.DecimalFromFloat(0.2), + AdditionalMakerRebate: num.DecimalFromFloat(0.00002), + }, + }, + }) + + // setup + eng.ensureTokenBalanceForParty(t, proposer, 1000) + + // expect + eng.expectOpenProposalEvent(t, proposer, proposal.ID) + + // when + toSubmit, err := eng.submitProposal(t, proposal) + + // then + require.NoError(t, err) + require.NotNil(t, toSubmit) +} + +func testSubmittingProposalForVolumeRebateProgramUpdateWithTooManyTiersFails(t *testing.T) { + now := time.Now() + ctx := vgtest.VegaContext(vgrand.RandomStr(5), vgtest.RandomPositiveI64()) + eng := getTestEngine(t, now) + + // setup + eng.broker.EXPECT().Send(gomock.Any()).Times(3) + eng.netp.Update(ctx, netparams.GovernanceProposalVolumeRebateProgramMinClose, "48h") + eng.netp.Update(ctx, netparams.GovernanceProposalVolumeRebateProgramMinEnact, "48h") + eng.netp.Update(ctx, netparams.GovernanceProposalVolumeRebateProgramMinProposerBalance, "1000") + + eng.broker.EXPECT().Send(events.NewNetworkParameterEvent(ctx, netparams.VolumeRebateProgramMaxBenefitTiers, "1")).Times(1) + require.NoError(t, eng.netp.Update(ctx, netparams.VolumeRebateProgramMaxBenefitTiers, "1")) + + // given + proposer := vgrand.RandomStr(5) + proposal := eng.newProposalForVolumeRebateProgramUpdate(proposer, now, &types.VolumeRebateProgramChanges{ + EndOfProgramTimestamp: now.Add(4 * 48 * time.Hour), + WindowLength: 15, + VolumeRebateBenefitTiers: []*types.VolumeRebateBenefitTier{ + { + MinimumPartyMakerVolumeFraction: num.DecimalFromFloat(0.1), + AdditionalMakerRebate: num.DecimalFromFloat(0.001), + }, { + MinimumPartyMakerVolumeFraction: num.DecimalFromFloat(0.2), + AdditionalMakerRebate: num.DecimalFromFloat(0.002), + }, + }, + }) + + // setup + eng.ensureTokenBalanceForParty(t, proposer, 1000) + + // expect + eng.expectRejectedProposalEvent(t, proposer, proposal.ID, types.ProposalErrorInvalidVolumeRebateProgram) + + // when + toSubmit, err := eng.submitProposal(t, proposal) + + // then + require.Error(t, err) + require.Nil(t, toSubmit) +} diff --git a/core/governance/market.go b/core/governance/market.go index cbead25e66a..a173c484838 100644 --- a/core/governance/market.go +++ b/core/governance/market.go @@ -226,6 +226,9 @@ func buildMarketFromProposal( // get factors for the market makerFee, _ := netp.Get(netparams.MarketFeeFactorsMakerFee) infraFee, _ := netp.Get(netparams.MarketFeeFactorsInfrastructureFee) + buybackFee, _ := netp.Get(netparams.MarketFeeFactorsBuyBackFee) + treasuryFee, _ := netp.Get(netparams.MarketFeeFactorsTreasuryFee) + // get the margin scaling factors scalingFactors := proto.ScalingFactors{} _ = netp.GetJSONStruct(netparams.MarketMarginScalingFactors, &scalingFactors) @@ -250,6 +253,8 @@ func buildMarketFromProposal( } makerFeeDec, _ := num.DecimalFromString(makerFee) infraFeeDec, _ := num.DecimalFromString(infraFee) + buybackFeeDec, _ := num.DecimalFromString(buybackFee) + treasuryFeeDec, _ := num.DecimalFromString(treasuryFee) // assign here, we want to update this after assigning market variable marginCalc := &types.MarginCalculator{ ScalingFactors: types.ScalingFactorsFromProto(&scalingFactors), @@ -262,6 +267,8 @@ func buildMarketFromProposal( Factors: &types.FeeFactors{ MakerFee: makerFeeDec, InfrastructureFee: infraFeeDec, + TreasuryFee: treasuryFeeDec, + BuyBackFee: buybackFeeDec, }, LiquidityFeeSettings: definition.Changes.LiquidityFeeSettings, }, @@ -312,6 +319,8 @@ func buildSpotMarketFromProposal( // get factors for the market makerFee, _ := netp.Get(netparams.MarketFeeFactorsMakerFee) infraFee, _ := netp.Get(netparams.MarketFeeFactorsInfrastructureFee) + buybackFee, _ := netp.Get(netparams.MarketFeeFactorsBuyBackFee) + treasuryFee, _ := netp.Get(netparams.MarketFeeFactorsTreasuryFee) // get price monitoring parameters if definition.Changes.PriceMonitoringParameters == nil { pmParams := &proto.PriceMonitoringParameters{} @@ -332,6 +341,8 @@ func buildSpotMarketFromProposal( makerFeeDec, _ := num.DecimalFromString(makerFee) infraFeeDec, _ := num.DecimalFromString(infraFee) + buybackFeeDec, _ := num.DecimalFromString(buybackFee) + treasuryFeeDec, _ := num.DecimalFromString(treasuryFee) market := &types.Market{ ID: marketID, DecimalPlaces: definition.Changes.PriceDecimalPlaces, @@ -340,6 +351,8 @@ func buildSpotMarketFromProposal( Factors: &types.FeeFactors{ MakerFee: makerFeeDec, InfrastructureFee: infraFeeDec, + TreasuryFee: treasuryFeeDec, + BuyBackFee: buybackFeeDec, }, LiquidityFeeSettings: definition.Changes.LiquidityFeeSettings, }, diff --git a/core/governance/market_cp_restore_test.go b/core/governance/market_cp_restore_test.go index 15ed96971ce..8b1db22757a 100644 --- a/core/governance/market_cp_restore_test.go +++ b/core/governance/market_cp_restore_test.go @@ -201,19 +201,20 @@ func createExecutionEngine(t *testing.T, tm time.Time) (*execution.Engine, *gove asset, _ := assets.New(context.Background(), log, assets.NewDefaultConfig(), getNodeWallet().Ethereum, nil, nil, broker, primaryBridgeView, secondaryBridgeView, notary, false) teams := emocks.NewMockTeams(ctrl) bc := emocks.NewMockAccountBalanceChecker(ctrl) - marketTracker := common.NewMarketActivityTracker(log, teams, bc, broker) + marketTracker := common.NewMarketActivityTracker(log, teams, bc, broker, collateralService) epochEngine.NotifyOnEpoch(marketTracker.OnEpochEvent, marketTracker.OnEpochRestore) referralDiscountReward := fmocks.NewMockReferralDiscountRewardService(ctrl) volumeDiscount := fmocks.NewMockVolumeDiscountService(ctrl) + volumeRebateService := fmocks.NewMockVolumeRebateService(ctrl) referralDiscountReward.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("no referrer")).AnyTimes() - referralDiscountReward.EXPECT().ReferralDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() - referralDiscountReward.EXPECT().RewardsFactorMultiplierAppliedForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() + referralDiscountReward.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() + referralDiscountReward.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() volumeDiscount.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() execBanking := emocks.NewMockBanking(ctrl) parties := emocks.NewMockParties(ctrl) delayTarget := emocks.NewMockDelayTransactionsTarget(ctrl) delayTarget.EXPECT().MarketDelayRequiredUpdated(gomock.Any(), gomock.Any()).AnyTimes() - exec := execution.NewEngine(log, executionConfig, timeService, collateralService, oracleService, broker, statevar, marketTracker, asset, referralDiscountReward, volumeDiscount, execBanking, parties, delayTarget) + exec := execution.NewEngine(log, executionConfig, timeService, collateralService, oracleService, broker, statevar, marketTracker, asset, referralDiscountReward, volumeDiscount, volumeRebateService, execBanking, parties, delayTarget) accounts := mocks.NewMockStakingAccounts(ctrl) witness := mocks.NewMockWitness(ctrl) diff --git a/core/governance/netparams.go b/core/governance/netparams.go index 8075af2561a..2b5426496bb 100644 --- a/core/governance/netparams.go +++ b/core/governance/netparams.go @@ -139,6 +139,22 @@ func (e *Engine) getVolumeDiscountProgramNetworkParameters() *types.ProposalPara ) } +func (e *Engine) getVolumeRebateProgramNetworkParameters() *types.ProposalParameters { + return e.getProposalParametersFromNetParams( + netparams.GovernanceProposalVolumeRebateProgramMinClose, + netparams.GovernanceProposalVolumeRebateProgramMaxClose, + netparams.GovernanceProposalVolumeRebateProgramMinEnact, + netparams.GovernanceProposalVolumeRebateProgramMaxEnact, + netparams.GovernanceProposalVolumeRebateProgramRequiredParticipation, + netparams.GovernanceProposalVolumeRebateProgramRequiredMajority, + netparams.GovernanceProposalVolumeRebateProgramMinProposerBalance, + netparams.GovernanceProposalVolumeRebateProgramMinVoterBalance, + "0", + "0", + "0", + ) +} + func (e *Engine) getNewAssetProposalParameters() *types.ProposalParameters { return e.getProposalParametersFromNetParams( netparams.GovernanceProposalAssetMinClose, diff --git a/core/governance/proposal.go b/core/governance/proposal.go index 3ab67c7c7b1..fd2a1339afb 100644 --- a/core/governance/proposal.go +++ b/core/governance/proposal.go @@ -255,6 +255,7 @@ type ToEnact struct { msu *ToEnactMarketStateUpdate referralProgramChanges *types.ReferralProgram volumeDiscountProgram *types.VolumeDiscountProgram + volumeRebateProgram *types.VolumeRebateProgram } type ToEnactMarketStateUpdate struct{} @@ -278,6 +279,10 @@ func (t ToEnact) IsVolumeDiscountProgramUpdate() bool { return t.volumeDiscountProgram != nil } +func (t ToEnact) IsVolumeRebateProgramUpdate() bool { + return t.volumeRebateProgram != nil +} + func (t ToEnact) IsReferralProgramUpdate() bool { return t.referralProgramChanges != nil } @@ -363,6 +368,10 @@ func (t *ToEnact) VolumeDiscountProgramUpdate() *types.VolumeDiscountProgram { return t.volumeDiscountProgram } +func (t *ToEnact) VolumeRebateProgramUpdate() *types.VolumeRebateProgram { + return t.volumeRebateProgram +} + func (t *ToEnact) UpdateMarket() *types.Market { return t.updatedMarket } diff --git a/core/governance/referral_program.go b/core/governance/referral_program.go index 81812c72011..5685a5ed020 100644 --- a/core/governance/referral_program.go +++ b/core/governance/referral_program.go @@ -39,11 +39,23 @@ func validateUpdateReferralProgram(netp NetParams, p *types.UpdateReferralProgra maxRewardFactor, _ := netp.GetDecimal(netparams.ReferralProgramMaxReferralRewardFactor) maxDiscountFactor, _ := netp.GetDecimal(netparams.ReferralProgramMaxReferralDiscountFactor) for i, tier := range p.Changes.BenefitTiers { - if tier.ReferralRewardFactor.GreaterThan(maxRewardFactor) { - return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral reward factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralRewardFactor, maxRewardFactor.String(), tier.ReferralRewardFactor.String()) + if tier.ReferralRewardFactors.Infra.GreaterThan(maxRewardFactor) { + return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral reward infrastructure factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralRewardFactor, maxRewardFactor.String(), tier.ReferralRewardFactors.Infra.String()) } - if tier.ReferralDiscountFactor.GreaterThan(maxDiscountFactor) { - return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral discount factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralDiscountFactor, maxDiscountFactor.String(), tier.ReferralDiscountFactor.String()) + if tier.ReferralRewardFactors.Maker.GreaterThan(maxRewardFactor) { + return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral reward maker factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralRewardFactor, maxRewardFactor.String(), tier.ReferralRewardFactors.Maker.String()) + } + if tier.ReferralRewardFactors.Liquidity.GreaterThan(maxRewardFactor) { + return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral reward liquidity factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralRewardFactor, maxRewardFactor.String(), tier.ReferralRewardFactors.Liquidity.String()) + } + if tier.ReferralDiscountFactors.Infra.GreaterThan(maxDiscountFactor) { + return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral discount infrastructure factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralDiscountFactor, maxDiscountFactor.String(), tier.ReferralDiscountFactors.Infra.String()) + } + if tier.ReferralDiscountFactors.Maker.GreaterThan(maxDiscountFactor) { + return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral discount maker factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralDiscountFactor, maxDiscountFactor.String(), tier.ReferralDiscountFactors.Maker.String()) + } + if tier.ReferralDiscountFactors.Liquidity.GreaterThan(maxDiscountFactor) { + return types.ProposalErrorInvalidReferralProgram, fmt.Errorf("tier %d defines a referral discount liquidity factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.ReferralProgramMaxReferralDiscountFactor, maxDiscountFactor.String(), tier.ReferralDiscountFactors.Liquidity.String()) } } return types.ProposalErrorUnspecified, nil diff --git a/core/governance/volume_discount_program.go b/core/governance/volume_discount_program.go index b7359bff4d9..1c2465d5555 100644 --- a/core/governance/volume_discount_program.go +++ b/core/governance/volume_discount_program.go @@ -30,8 +30,14 @@ func validateUpdateVolumeDiscountProgram(netp NetParams, p *types.UpdateVolumeDi maxDiscountFactor, _ := netp.GetDecimal(netparams.VolumeDiscountProgramMaxVolumeDiscountFactor) for i, tier := range p.Changes.VolumeBenefitTiers { - if tier.VolumeDiscountFactor.GreaterThan(maxDiscountFactor) { - return types.ProposalErrorInvalidVolumeDiscountProgram, fmt.Errorf("tier %d defines a volume discount factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.VolumeDiscountProgramMaxVolumeDiscountFactor, maxDiscountFactor.String(), tier.VolumeDiscountFactor.String()) + if tier.VolumeDiscountFactors.Infra.GreaterThan(maxDiscountFactor) { + return types.ProposalErrorInvalidVolumeDiscountProgram, fmt.Errorf("tier %d defines a volume discount infrastructure factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.VolumeDiscountProgramMaxVolumeDiscountFactor, maxDiscountFactor.String(), tier.VolumeDiscountFactors.Infra.String()) + } + if tier.VolumeDiscountFactors.Maker.GreaterThan(maxDiscountFactor) { + return types.ProposalErrorInvalidVolumeDiscountProgram, fmt.Errorf("tier %d defines a volume discount maker factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.VolumeDiscountProgramMaxVolumeDiscountFactor, maxDiscountFactor.String(), tier.VolumeDiscountFactors.Maker.String()) + } + if tier.VolumeDiscountFactors.Liquidity.GreaterThan(maxDiscountFactor) { + return types.ProposalErrorInvalidVolumeDiscountProgram, fmt.Errorf("tier %d defines a volume discount liquidity factor higher than the maximum allowed by the network parameter %q: maximum is %s, but got %s", i+1, netparams.VolumeDiscountProgramMaxVolumeDiscountFactor, maxDiscountFactor.String(), tier.VolumeDiscountFactors.Liquidity.String()) } } return 0, nil diff --git a/core/governance/volume_rebate_program.go b/core/governance/volume_rebate_program.go new file mode 100644 index 00000000000..677cca3e2dc --- /dev/null +++ b/core/governance/volume_rebate_program.go @@ -0,0 +1,43 @@ +// Copyright (C) 2023 Gobalsky Labs Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +package governance + +import ( + "fmt" + + "code.vegaprotocol.io/vega/core/netparams" + "code.vegaprotocol.io/vega/core/types" +) + +func validateUpdateVolumeRebateProgram(netp NetParams, p *types.UpdateVolumeRebateProgram) (types.ProposalError, error) { + maxTiers, _ := netp.GetUint(netparams.VolumeRebateProgramMaxBenefitTiers) + if len(p.Changes.VolumeRebateBenefitTiers) > int(maxTiers.Uint64()) { + return types.ProposalErrorInvalidVolumeRebateProgram, fmt.Errorf("the number of tiers in the proposal is higher than the maximum allowed by the network parameter %q: maximum is %s, but got %d", netparams.VolumeRebateProgramMaxBenefitTiers, maxTiers.String(), len(p.Changes.VolumeRebateBenefitTiers)) + } + return 0, nil +} + +func updatedVolumeRebateProgramFromProposal(p *proposal) *types.VolumeRebateProgram { + terms := p.Terms.GetUpdateVolumeRebateProgram() + + return &types.VolumeRebateProgram{ + ID: p.ID, + Version: terms.Changes.Version, + EndOfProgramTimestamp: terms.Changes.EndOfProgramTimestamp, + WindowLength: terms.Changes.WindowLength, + VolumeRebateBenefitTiers: terms.Changes.VolumeRebateBenefitTiers, + } +} diff --git a/core/integration/features/0084-VDPR-012.feature b/core/integration/features/0084-VDPR-012.feature index 2eebd9dcce1..1eb0fcd6583 100644 --- a/core/integration/features/0084-VDPR-012.feature +++ b/core/integration/features/0084-VDPR-012.feature @@ -19,16 +19,15 @@ Feature: At the start of an epoch, each parties volume_discount_factor is reeval | network.markPriceUpdateMaximumFrequency | 0s | | limits.markets.maxPeggedOrders | 6 | | market.auction.minimumDuration | 1 | - | market.fee.factors.infrastructureFee | 0.001 | - | market.fee.factors.makerFee | 0.004 | + #risk factor short:3.5569036 #risk factor long:0.801225765 And the volume discount program tiers named "VDP-01": - | volume | factor | - | 1000 | 0.001 | - | 2000 | 0.005 | - | 3000 | 0.010 | + | volume | infra factor | liquidity factor | maker factor | + | 1000 | 0.001 | 0.002 | 0.003 | + | 2000 | 0.005 | 0.006 | 0.007 | + | 3000 | 0.010 | 0.012 | 0.014 | And the volume discount program: | id | tiers | closing timestamp | window length | | id1 | VDP-01 | 0 | 4 | @@ -37,8 +36,13 @@ Feature: At the start of an epoch, each parties volume_discount_factor is reeval | id | decimal places | | ETH | 0 | And the fees configuration named "fees-config-1": - | maker fee | infrastructure fee | - | 0.0004 | 0.001 | + | maker fee | infrastructure fee | liquidity fee method | liquidity fee constant | + | 0.0004 | 0.001 | METHOD_CONSTANT | 0 | + + And the fees configuration named "fees-config-2": + | maker fee | infrastructure fee | liquidity fee method | liquidity fee constant | buy back fee | treasury fee | + | 0 | 0 | METHOD_CONSTANT | 0.1 | 0.001 | 0.002 | + And the price monitoring named "price-monitoring": | horizon | probability | auction extension | | 3600 | 0.99 | 3 | @@ -47,10 +51,6 @@ Feature: At the start of an epoch, each parties volume_discount_factor is reeval | price range | commitment min time fraction | performance hysteresis epochs | sla competition factor | | 0.5 | 0.6 | 1 | 1.0 | - And the markets: - | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | - | ETH/MAR24 | ETH | ETH | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | price-monitoring | default-eth-for-future | 1e0 | 0 | SLA-22 | - And the following network parameters are set: | name | value | | market.liquidity.bondPenaltyParameter | 0.2 | @@ -65,6 +65,10 @@ Feature: At the start of an epoch, each parties volume_discount_factor is reeval Given the average block duration is "1" @Now Scenario: 001: Check that the volume discount factor is updated after each epoch + Given the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | + | ETH/MAR24 | ETH | ETH | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | price-monitoring | default-eth-for-future | 1e0 | 0 | SLA-22 | + Given the parties deposit on asset's general account the following amount: | party | asset | amount | | lp1 | ETH | 10000000 | @@ -92,18 +96,144 @@ Feature: At the start of an epoch, each parties volume_discount_factor is reeval And the market data for the market "ETH/MAR24" should be: | mark price | trading mode | horizon | min bound | max bound | target stake | supplied stake | open interest | | 1000 | TRADING_MODE_CONTINUOUS | 3600 | 973 | 1027 | 3556 | 100000 | 1 | - And the party "party3" has the following discount factor "0" + And the party "party3" has the following discount infra factor "0" Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | | party3 | ETH/MAR24 | buy | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | When the network moves ahead "1" epochs - And the party "party3" has the following discount factor "0.005" + And the party "party3" has the following discount infra factor "0.005" + And the party "party3" has the following discount liquidity factor "0.006" + And the party "party3" has the following discount maker factor "0.007" Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | | party3 | ETH/MAR24 | buy | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | | party3 | ETH/MAR24 | sell | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | When the network moves ahead "1" epochs - And the party "party3" has the following discount factor "0.01" + And the party "party3" has the following discount infra factor "0.01" + And the party "party3" has the following discount liquidity factor "0.012" + And the party "party3" has the following discount maker factor "0.014" + + # when trade_value_for_fee_purposes>0, then total fee should be maker_fee_after_referral_discount+ treasury_fee + buyback_fee when fee_factor[infrastructure] = 0, fee_factor[liquidity] = 0 (0083-RFPR-053) + # now lets reset the infra fee to 0 and do a trade with party 3: + And the following network parameters are set: + | name | value | + | market.fee.factors.makerFee | 0.1 | + | market.fee.factors.infrastructureFee | 0 | + | market.fee.factors.buybackFee | 0.001 | + | market.fee.factors.treasuryFee | 0.002 | + + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR24 | sell | 11 | 0 | 1 | TYPE_MARKET | TIF_IOC | + + # trade value is 11*990 = 10,890 + # infra fee is set to 0 + # liquidity fee is set to 0 + # maker fee before discount = 10,890 * 0.1 => 1089 + # maker fee discount = 1089*0.014 => 15 + # maker fee after discount = 1089-15=1074 + # buyback = 11 + # treasury = 22 + # total = 1074 + 33 + And the following trades should be executed: + | seller | price | size | buyer | seller fee | seller infrastructure fee | seller liquidity fee | seller maker fee | seller infrastructure fee volume discount | seller liquidity fee volume discount | seller maker fee volume discount | + | party3 | 990 | 11 | lp1 | 1107 | 0 | 0 | 1074 | 0 | 0 | 15 | + + # when trade_value_for_fee_purposes>0, then total fee should be infrastructure_fee_after_referral_discount+ treasury_fee + buyback_fee when fee_factor[maker] = 0, fee_factor[liquidity] = 0 (0083-RFPR-055) + # now lets reset the maker fee to 0 and do a trade with party 3: + And the following network parameters are set: + | name | value | + | market.fee.factors.makerFee | 0 | + | market.fee.factors.infrastructureFee | 0.2 | + + # trade value is 11*990 = 10,890 + # maker fee is set to 0 + # liquidity fee is set to 0 + # infra fee before discount = 10,890 * 0.2 => 2178 + # infra fee discount = 2178*0.01 => 21 + # infra fee after discount = 2178-21=2157 + # buyback = 11 + # treasury = 22 + # total = 2157 + 33 + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR24 | sell | 11 | 0 | 1 | TYPE_MARKET | TIF_IOC | + + And the following trades should be executed: + | seller | price | size | buyer | seller fee | seller infrastructure fee | seller liquidity fee | seller maker fee | seller infrastructure fee volume discount | seller liquidity fee volume discount | seller maker fee volume discount | + | party3 | 990 | 11 | lp1 | 2190 | 2157 | 0 | 0 | 21 | 0 | 0 | + + + @Now + Scenario: when trade_value_for_fee_purposes>0, then total fee should be liquidity_fee_after_referral_discount+ treasury_fee + buyback_fee when fee_factor[maker] = 0, fee_factor[infrastructure] = 0 (0083-RFPR-054) + Given the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | + | ETH/MAR24 | ETH | ETH | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-2 | price-monitoring | default-eth-for-future | 1e0 | 0 | SLA-22 | + + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lp1 | ETH | 10000000 | + | party1 | ETH | 10000000 | + | party2 | ETH | 10000000 | + | party3 | ETH | 10000000 | + + And the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR24 | 100000 | 0.02 | submission | + + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party1 | ETH/MAR24 | buy | 10 | 900 | 0 | TYPE_LIMIT | TIF_GTC | + | party1 | ETH/MAR24 | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | lp1 | ETH/MAR24 | buy | 100 | 990 | 0 | TYPE_LIMIT | TIF_GTC | + | lp1 | ETH/MAR24 | sell | 100 | 1010 | 0 | TYPE_LIMIT | TIF_GTC | + | party2 | ETH/MAR24 | sell | 10 | 1100 | 0 | TYPE_LIMIT | TIF_GTC | + | party2 | ETH/MAR24 | sell | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + + Then the opening auction period ends for market "ETH/MAR24" + And the following trades should be executed: + | buyer | price | size | seller | + | party1 | 1000 | 1 | party2 | + And the market data for the market "ETH/MAR24" should be: + | mark price | trading mode | horizon | min bound | max bound | target stake | supplied stake | open interest | + | 1000 | TRADING_MODE_CONTINUOUS | 3600 | 973 | 1027 | 3556 | 100000 | 1 | + And the party "party3" has the following discount infra factor "0" + + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR24 | buy | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | + When the network moves ahead "1" epochs + And the party "party3" has the following discount infra factor "0.005" + And the party "party3" has the following discount liquidity factor "0.006" + And the party "party3" has the following discount maker factor "0.007" + + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR24 | buy | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party3 | ETH/MAR24 | sell | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | + When the network moves ahead "1" epochs + And the party "party3" has the following discount infra factor "0.01" + And the party "party3" has the following discount liquidity factor "0.012" + And the party "party3" has the following discount maker factor "0.014" + + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR24 | sell | 11 | 0 | 1 | TYPE_MARKET | TIF_IOC | + + # trade value is 11*990 = 10,890 + # infra fee is set to 0 + # maker fee is set to 0 + # liquidity fee before discount = 10,890 * 0.1 => 1089 + # liquidity fee discount = 1089*0.012 => 13 + # liquidity fee after discount = 1089-15=1076 + # buyback = 11 + # treasury = 22 + # total = 1076 + 33 + And the following trades should be executed: + | seller | price | size | buyer | seller fee | seller infrastructure fee | seller liquidity fee | seller maker fee | seller infrastructure fee volume discount | seller liquidity fee volume discount | seller maker fee volume discount | + | party3 | 990 | 11 | lp1 | 1109 | 0 | 1076 | 0 | 0 | 13 | 0 | + diff --git a/core/integration/features/0084-VDPR-013.feature b/core/integration/features/0084-VDPR-013.feature index e9e4ca4f88d..bd908aaf635 100644 --- a/core/integration/features/0084-VDPR-013.feature +++ b/core/integration/features/0084-VDPR-013.feature @@ -26,10 +26,10 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig #risk factor short:3.5569036 #risk factor long:0.801225765 And the volume discount program tiers named "VDP-01": - | volume | factor | - | 1000 | 0.01 | - | 2000 | 0.02 | - | 3000 | 0.03 | + | volume | infra factor | liquidity factor | maker factor | + | 1000 | 0.011 | 0.012 | 0.013 | + | 2000 | 0.021 | 0.022 | 0.023 | + | 3000 | 0.031 | 0.032 | 0.033 | And the volume discount program: | id | tiers | closing timestamp | window length | | id1 | VDP-01 | 0 | 4 | @@ -95,7 +95,7 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig | mark price | trading mode | horizon | min bound | max bound | target stake | supplied stake | open interest | | 1000 | TRADING_MODE_CONTINUOUS | 3600 | 973 | 1027 | 3556 | 100000 | 1 | And the party "party3" has the following taker notional "0" - And the party "party3" has the following discount factor "0" + And the party "party3" has the following discount infra factor "0" Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | @@ -103,7 +103,9 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | When the network moves ahead "1" epochs And the party "party3" has the following taker notional "2000" - And the party "party3" has the following discount factor "0.02" + And the party "party3" has the following discount infra factor "0.021" + And the party "party3" has the following discount liquidity factor "0.022" + And the party "party3" has the following discount maker factor "0.023" Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | @@ -111,7 +113,9 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | When the network moves ahead "1" epochs And the party "party3" has the following taker notional "4000" - And the party "party3" has the following discount factor "0.03" + And the party "party3" has the following discount infra factor "0.031" + And the party "party3" has the following discount liquidity factor "0.032" + And the party "party3" has the following discount maker factor "0.033" # now that party3 has a discount, lets do a trade with fees # Volume discount rewards are correctly calculated and transferred for each taker fee component during continuous trading. (0029-FEES-027) @@ -119,13 +123,13 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig | party | market id | side | volume | price | resulting trades | type | tif | | party3 | ETH/MAR24 | buy | 50 | 1000 | 1 | TYPE_MARKET | TIF_IOC | - # maker fee discount = floor(202 * 0.03) = 6 - # infra fee discount - floor(51 *0.03) = 1 - # lp fee discount - floor(1010 * 0.03) = 30 + # maker fee discount = floor(202 * 0.033) = 6 + # infra fee discount - floor(51 *0.031) = 1 + # lp fee discount - floor(1010 * 0.032) = 32 Then the following transfers should happen: | from | to | from account | to account | market id | amount | asset | | party3 | market | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_FEES_MAKER | ETH/MAR24 | 196 | ETH | - | party3 | market | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_FEES_LIQUIDITY | ETH/MAR24 | 980 | ETH | + | party3 | market | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_FEES_LIQUIDITY | ETH/MAR24 | 978 | ETH | | party3 | | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_FEES_INFRASTRUCTURE | | 50 | ETH | Given the parties submit the following liquidity provision: @@ -134,7 +138,9 @@ Feature: A parties volume_discount_factor is set equal to the factors in the hig And the network moves ahead "1" epochs And the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH/MAR24" - And the party "party3" has the following discount factor "0.03" + And the party "party3" has the following discount infra factor "0.031" + And the party "party3" has the following discount liquidity factor "0.032" + And the party "party3" has the following discount maker factor "0.033" Given the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | diff --git a/core/integration/features/0084-VDPR-014.feature b/core/integration/features/0084-VDPR-014.feature index 08263740bbc..087f642da45 100644 --- a/core/integration/features/0084-VDPR-014.feature +++ b/core/integration/features/0084-VDPR-014.feature @@ -23,10 +23,10 @@ Feature: If a party does not qualify for the lowest tier, their volume_discount_ #risk factor short:3.5569036 #risk factor long:0.801225765 And the volume discount program tiers named "VDP-01": - | volume | factor | - | 3000 | 0.01 | - | 4000 | 0.02 | - | 5000 | 0.03 | + | volume | infra factor | liquidity factor | maker factor | + | 3000 | 0.011 | 0.012 | 0.013 | + | 4000 | 0.021 | 0.022 | 0.023 | + | 5000 | 0.031 | 0.032 | 0.033 | And the volume discount program: | id | tiers | closing timestamp | window length | | id1 | VDP-01 | 0 | 4 | @@ -91,7 +91,7 @@ Feature: If a party does not qualify for the lowest tier, their volume_discount_ | mark price | trading mode | horizon | min bound | max bound | target stake | supplied stake | open interest | | 1000 | TRADING_MODE_CONTINUOUS | 3600 | 973 | 1027 | 3556 | 100000 | 1 | And the party "party3" has the following taker notional "0" - And the party "party3" has the following discount factor "0" + And the party "party3" has the following discount infra factor "0" Then the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | @@ -104,4 +104,4 @@ Feature: If a party does not qualify for the lowest tier, their volume_discount_ # The taker trades above are not enough for party3 to hit the first level of the discount tier # so we get a zero for the discount factor And the party "party3" has the following taker notional "2000" - And the party "party3" has the following discount factor "0" + And the party "party3" has the following discount infra factor "0" diff --git a/core/integration/features/0084-VDPR-018.feature b/core/integration/features/0084-VDPR-018.feature new file mode 100644 index 00000000000..2db72af1df5 --- /dev/null +++ b/core/integration/features/0084-VDPR-018.feature @@ -0,0 +1,222 @@ +Feature: A volume_discount_factors tier with differing factors across the three options has each factor set correctly (0084-VDPR-018) + + Background: + + Given the margin calculator named "margin-calculator-1": + | search factor | initial factor | release factor | + | 1.2 | 1.5 | 1.7 | + Given the log normal risk model named "log-normal-risk-model": + | risk aversion | tau | mu | r | sigma | + | 0.000001 | 0.1 | 0 | 0 | 1.0 | + + Given the liquidity monitoring parameters: + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.0 | 20s | 1.0 | + + And the following network parameters are set: + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 0s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + + + #risk factor short:3.5569036 + #risk factor long:0.801225765 + And the volume discount program tiers named "VDP-01": + | volume | infra factor | liquidity factor | maker factor | + | 1000 | 0.001 | 0.002 | 0.003 | + | 2000 | 0.005 | 0.006 | 0.007 | + | 3000 | 0.010 | 0.012 | 0.014 | + And the volume discount program: + | id | tiers | closing timestamp | window length | + | id1 | VDP-01 | 0 | 4 | + + And the following assets are registered: + | id | decimal places | + | ETH | 0 | + And the fees configuration named "fees-config-1": + | maker fee | infrastructure fee | liquidity fee method | liquidity fee constant | + | 0.0004 | 0.001 | METHOD_CONSTANT | 0 | + + And the fees configuration named "fees-config-2": + | maker fee | infrastructure fee | liquidity fee method | liquidity fee constant | buy back fee | treasury fee | + | 0 | 0 | METHOD_CONSTANT | 0.1 | 0.001 | 0.002 | + + And the price monitoring named "price-monitoring": + | horizon | probability | auction extension | + | 3600 | 0.99 | 3 | + + And the liquidity sla params named "SLA-22": + | price range | commitment min time fraction | performance hysteresis epochs | sla competition factor | + | 0.5 | 0.6 | 1 | 1.0 | + + And the following network parameters are set: + | name | value | + | market.liquidity.bondPenaltyParameter | 0.2 | + | validators.epoch.length | 5s | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.7 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | + + Given the average block duration is "1" + + Scenario: Check the factors after each epoch, basically same as 0084-VDPR-012. + Given the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | + | ETH/MAR24 | ETH | ETH | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | price-monitoring | default-eth-for-future | 1e0 | 0 | SLA-22 | + + And the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lp1 | ETH | 10000000 | + | party1 | ETH | 10000000 | + | party2 | ETH | 10000000 | + | party3 | ETH | 10000000 | + + And the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR24 | 100000 | 0.02 | submission | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party1 | ETH/MAR24 | buy | 10 | 900 | 0 | TYPE_LIMIT | TIF_GTC | + | party1 | ETH/MAR24 | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | lp1 | ETH/MAR24 | buy | 100 | 990 | 0 | TYPE_LIMIT | TIF_GTC | + | lp1 | ETH/MAR24 | sell | 100 | 1010 | 0 | TYPE_LIMIT | TIF_GTC | + | party2 | ETH/MAR24 | sell | 10 | 1100 | 0 | TYPE_LIMIT | TIF_GTC | + | party2 | ETH/MAR24 | sell | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + + Then the opening auction period ends for market "ETH/MAR24" + And the following trades should be executed: + | buyer | price | size | seller | + | party1 | 1000 | 1 | party2 | + And the market data for the market "ETH/MAR24" should be: + | mark price | trading mode | horizon | min bound | max bound | target stake | supplied stake | open interest | + | 1000 | TRADING_MODE_CONTINUOUS | 3600 | 973 | 1027 | 3556 | 100000 | 1 | + And the parties have the following discount factors: + | party | maker factor | liquidity factor | infra factor | + | party3 | 0 | 0 | 0 | + | party1 | 0 | 0 | 0 | + | party2 | 0 | 0 | 0 | + | lp1 | 0 | 0 | 0 | + + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR24 | buy | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party3 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | + When the network moves ahead "1" epochs + Then the parties have the following discount factors: + | party | maker factor | liquidity factor | infra factor | + | party3 | 0.007 | 0.006 | 0.005 | + | party1 | 0 | 0 | 0 | + | party2 | 0 | 0 | 0 | + | lp1 | 0 | 0 | 0 | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR24 | buy | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party3 | ETH/MAR24 | sell | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party1 | ETH/MAR24 | buy | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | + | party1 | ETH/MAR24 | sell | 1 | 0 | 1 | TYPE_MARKET | TIF_IOC | + And the network moves ahead "1" epochs + Then the parties have the following discount factors: + | party | maker factor | liquidity factor | infra factor | + | party3 | 0.014 | 0.012 | 0.01 | + | party1 | 0.007 | 0.006 | 0.005 | + | party2 | 0 | 0 | 0 | + | lp1 | 0 | 0 | 0 | + + # when trade_value_for_fee_purposes>0, then total fee should be maker_fee_after_referral_discount+ treasury_fee + buyback_fee when fee_factor[infrastructure] = 0, fee_factor[liquidity] = 0 (0083-RFPR-053) + # now lets reset the infra fee to 0 and do a trade with party 3: + When the following network parameters are set: + | name | value | + | market.fee.factors.makerFee | 0.1 | + | market.fee.factors.infrastructureFee | 0 | + | market.fee.factors.buybackFee | 0.001 | + | market.fee.factors.treasuryFee | 0.002 | + + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR24 | sell | 11 | 0 | 1 | TYPE_MARKET | TIF_IOC | + + # trade value is 11*990 = 10,890 + # infra fee is set to 0 + # liquidity fee is set to 0 + # maker fee before discount = 10,890 * 0.1 => 1089 + # maker fee discount = 1089*0.014 => 15 + # maker fee after discount = 1089-15=1074 + # buyback = 11 + # treasury = 22 + # total = 1074 + 33 + Then the following trades should be executed: + | seller | price | size | buyer | seller fee | seller infrastructure fee | seller liquidity fee | seller maker fee | seller infrastructure fee volume discount | seller liquidity fee volume discount | seller maker fee volume discount | + | party3 | 990 | 11 | lp1 | 1107 | 0 | 0 | 1074 | 0 | 0 | 15 | + + # when trade_value_for_fee_purposes>0, then total fee should be infrastructure_fee_after_referral_discount+ treasury_fee + buyback_fee when fee_factor[maker] = 0, fee_factor[liquidity] = 0 (0083-RFPR-055) + # now lets reset the maker fee to 0 and do a trade with party 3: + When the following network parameters are set: + | name | value | + | market.fee.factors.makerFee | 0 | + | market.fee.factors.infrastructureFee | 0.2 | + + # trade value is 11*990 = 10,890 + # maker fee is set to 0 + # liquidity fee is set to 0 + # infra fee before discount = 10,890 * 0.2 => 2178 + # infra fee discount = 2178*0.01 => 21 + # infra fee after discount = 2178-21=2157 + # buyback = 11 + # treasury = 22 + # total = 2157 + 33 + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR24 | sell | 11 | 0 | 1 | TYPE_MARKET | TIF_IOC | + + Then the following trades should be executed: + | seller | price | size | buyer | seller fee | seller infrastructure fee | seller liquidity fee | seller maker fee | seller infrastructure fee volume discount | seller liquidity fee volume discount | seller maker fee volume discount | + | party3 | 990 | 11 | lp1 | 2190 | 2157 | 0 | 0 | 21 | 0 | 0 | + And the parties have the following discount factors: + | party | maker factor | liquidity factor | infra factor | + | party3 | 0.014 | 0.012 | 0.01 | + | party1 | 0.007 | 0.006 | 0.005 | + | party2 | 0 | 0 | 0 | + | lp1 | 0 | 0 | 0 | + + # check if the tiers carry over in to the next epochs + When the network moves ahead "1" epochs + Then the parties have the following discount factors: + | party | maker factor | liquidity factor | infra factor | + | party3 | 0.014 | 0.012 | 0.01 | + | party1 | 0.007 | 0.006 | 0.005 | + | party2 | 0 | 0 | 0 | + | lp1 | 0 | 0 | 0 | + + # 2 epochs later, nothing has changed + When the network moves ahead "2" epochs + Then the parties have the following discount factors: + | party | maker factor | liquidity factor | infra factor | + | party3 | 0.014 | 0.012 | 0.01 | + | party1 | 0.007 | 0.006 | 0.005 | + | party2 | 0 | 0 | 0 | + | lp1 | 0 | 0 | 0 | + + # one epoch later, party 1 lost their benefits, party3 traded later on, they keep their benefits one more epoch. + When the network moves ahead "1" epochs + Then the parties have the following discount factors: + | party | maker factor | liquidity factor | infra factor | + | party3 | 0.014 | 0.012 | 0.01 | + | party1 | 0 | 0 | 0 | + | party2 | 0 | 0 | 0 | + | lp1 | 0 | 0 | 0 | + + # next epoch, the benefits should have expired + When the network moves ahead "1" epochs + Then the parties have the following discount factors: + | party | maker factor | liquidity factor | infra factor | + | party3 | 0 | 0 | 0 | + | party1 | 0 | 0 | 0 | + | party2 | 0 | 0 | 0 | + | lp1 | 0 | 0 | 0 | diff --git a/core/integration/features/amm/0090-VAMM-001.feature b/core/integration/features/amm/0090-VAMM-001.feature index 0babe4bcc4b..64db19a180d 100644 --- a/core/integration/features/amm/0090-VAMM-001.feature +++ b/core/integration/features/amm/0090-VAMM-001.feature @@ -90,14 +90,14 @@ Feature: Test vAMM submission works as expected | 100 | TRADING_MODE_CONTINUOUS | 3600 | 94 | 106 | 39 | 1000 | 1 | 100 | 100 | 100 | When the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | - | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 150 | 0.25 | 0.25 | 0.01 | + | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 150 | 0.25 | 0.25 | 0.01 | Then the AMM pool status should be: | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | - | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 150 | 0.25 | 0.25 | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 150 | 0.25 | 0.25 | And set the following AMM sub account aliases: | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-acc | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-acc | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | diff --git a/core/integration/features/amm/0090-VAMM-002.feature b/core/integration/features/amm/0090-VAMM-002.feature index 5cc83143e4d..54de4fdd289 100644 --- a/core/integration/features/amm/0090-VAMM-002.feature +++ b/core/integration/features/amm/0090-VAMM-002.feature @@ -90,15 +90,15 @@ Feature: Test vAMM submission works as expected | 100 | TRADING_MODE_CONTINUOUS | 3600 | 94 | 106 | 39 | 1000 | 1 | 100 | 100 | 100 | When the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | lower leverage | proposed fee | - | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 0.25 | 0.01 | + | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 0.25 | 0.01 | Then the AMM pool status should be: | party | market id | amount | status | base | lower bound | lower leverage | - | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 0.25 | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 0.25 | And set the following AMM sub account aliases: | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-acc | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-acc | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | diff --git a/core/integration/features/amm/0090-VAMM-003.feature b/core/integration/features/amm/0090-VAMM-003.feature index ab1da29821f..4ff70df24fa 100644 --- a/core/integration/features/amm/0090-VAMM-003.feature +++ b/core/integration/features/amm/0090-VAMM-003.feature @@ -93,12 +93,12 @@ Feature: Test vAMM submission works as expected | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 150 | 0.25 | 0.01 | Then the AMM pool status should be: | party | market id | amount | status | base | upper bound | upper leverage | - | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 150 | 0.25 | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 150 | 0.25 | And set the following AMM sub account aliases: | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-acc | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-acc | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | diff --git a/core/integration/features/amm/0090-VAMM-004.feature b/core/integration/features/amm/0090-VAMM-004.feature index 3aeab6f0b67..38013679bbd 100644 --- a/core/integration/features/amm/0090-VAMM-004.feature +++ b/core/integration/features/amm/0090-VAMM-004.feature @@ -98,35 +98,35 @@ Feature: Test vAMM submission works as expected (invalid submission) When the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | lower leverage | error | proposed fee | - | vamm1 | ETH/MAR22 | 200000 | 0.1 | 90 | 85 | 0.25 | not enough collateral in general account | 0.01 | + | vamm1 | ETH/MAR22 | 200000 | 0.1 | 90 | 85 | 0.25 | not enough collateral in general account | 0.01 | Then the AMM pool status should be: | party | market id | amount | status | base | lower bound | lower leverage | reason | - | vamm1 | ETH/MAR22 | 200000 | STATUS_REJECTED | 90 | 85 | 0.25 | STATUS_REASON_CANNOT_FILL_COMMITMENT | + | vamm1 | ETH/MAR22 | 200000 | STATUS_REJECTED | 90 | 85 | 0.25 | STATUS_REASON_CANNOT_FILL_COMMITMENT | When the parties submit the following AMM: | party | market id | amount | slippage | base | upper bound | upper leverage | error | proposed fee | - | vamm1 | ETH/MAR22 | 200000 | 0.1 | 110 | 150 | 0.25 | not enough collateral in general account | 0.01 | + | vamm1 | ETH/MAR22 | 200000 | 0.1 | 110 | 150 | 0.25 | not enough collateral in general account | 0.01 | Then the AMM pool status should be: | party | market id | amount | status | base | upper bound | upper leverage | reason | - | vamm1 | ETH/MAR22 | 200000 | STATUS_REJECTED | 110 | 150 | 0.25 | STATUS_REASON_CANNOT_FILL_COMMITMENT | + | vamm1 | ETH/MAR22 | 200000 | STATUS_REJECTED | 110 | 150 | 0.25 | STATUS_REASON_CANNOT_FILL_COMMITMENT | When the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | lower leverage | error | proposed fee | - | vamm1 | ETH/MAR22 | 200000 | 0.01 | 110 | 99 | 0.1 | not enough collateral in general account | 0.01 | + | vamm1 | ETH/MAR22 | 200000 | 0.01 | 110 | 99 | 0.1 | not enough collateral in general account | 0.01 | Then the AMM pool status should be: | party | market id | amount | status | base | lower bound | lower leverage | reason | - | vamm1 | ETH/MAR22 | 200000 | STATUS_REJECTED | 110 | 99 | 0.1 | STATUS_REASON_CANNOT_FILL_COMMITMENT | + | vamm1 | ETH/MAR22 | 200000 | STATUS_REJECTED | 110 | 99 | 0.1 | STATUS_REASON_CANNOT_FILL_COMMITMENT | When the parties submit the following AMM: | party | market id | amount | slippage | base | upper bound | upper leverage | error | proposed fee | - | vamm1 | ETH/MAR22 | 200000 | 0.01 | 90 | 101 | 0.02 | not enough collateral in general account | 0.01 | + | vamm1 | ETH/MAR22 | 200000 | 0.01 | 90 | 101 | 0.02 | not enough collateral in general account | 0.01 | Then the AMM pool status should be: | party | market id | amount | status | base | upper bound | upper leverage | reason | - | vamm1 | ETH/MAR22 | 200000 | STATUS_REJECTED | 90 | 101 | 0.02 | STATUS_REASON_CANNOT_FILL_COMMITMENT | + | vamm1 | ETH/MAR22 | 200000 | STATUS_REJECTED | 90 | 101 | 0.02 | STATUS_REASON_CANNOT_FILL_COMMITMENT | When the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | error | proposed fee | - | vamm1 | ETH/MAR22 | 200000 | 0.001 | 101 | 95 | 105 | 0.01 | 0.01 | not enough collateral in general account | 0.01 | + | vamm1 | ETH/MAR22 | 200000 | 0.001 | 101 | 95 | 105 | 0.01 | 0.01 | not enough collateral in general account | 0.01 | Then the AMM pool status should be: | party | market id | amount | status | base | lower bound | lower leverage | upper bound | upper leverage | reason | - | vamm1 | ETH/MAR22 | 200000 | STATUS_REJECTED | 101 | 95 | 0.01 | 105 | 0.01 | STATUS_REASON_CANNOT_FILL_COMMITMENT | + | vamm1 | ETH/MAR22 | 200000 | STATUS_REJECTED | 101 | 95 | 0.01 | 105 | 0.01 | STATUS_REASON_CANNOT_FILL_COMMITMENT | diff --git a/core/integration/features/amm/0090-VAMM-005.feature b/core/integration/features/amm/0090-VAMM-005.feature index 699ee17c94d..04c04c96490 100644 --- a/core/integration/features/amm/0090-VAMM-005.feature +++ b/core/integration/features/amm/0090-VAMM-005.feature @@ -97,25 +97,25 @@ Feature: Test vAMM submission works as expected (invalid submission) When the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | - | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 150 | 0.25 | 0.25 | 0.01 | + | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 150 | 0.25 | 0.25 | 0.01 | Then the AMM pool status should be: | party | market id | amount | status | base | lower bound | lower leverage | upper bound | upper leverage | - | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 0.25 | 150 | 0.25 | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 0.25 | 150 | 0.25 | When the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | lower leverage | error | proposed fee | - | vamm2 | ETH/MAR22 | 100000 | 0.1 | 90 | 85 | 0.25 | rebase target outside bounds | 0.01 | + | vamm2 | ETH/MAR22 | 100000 | 0.1 | 90 | 85 | 0.25 | rebase target outside bounds | 0.01 | # can't rebase because the target is 100 and thats outside of its bounds given there is no upper Then the AMM pool status should be: - | party | market id | amount | status | base | lower bound | lower leverage | - | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 90 | 85 | 0.25 | + | party | market id | amount | status | base | lower bound | lower leverage | + | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 90 | 85 | 0.25 | When the parties submit the following AMM: | party | market id | amount | slippage | base | upper bound | upper leverage | error | proposed fee | - | vamm3 | ETH/MAR22 | 100000 | 0.1 | 110 | 150 | 0.25 | rebase target outside bounds | 0.01 | + | vamm3 | ETH/MAR22 | 100000 | 0.1 | 110 | 150 | 0.25 | rebase target outside bounds | 0.01 | Then the AMM pool status should be: - | party | market id | amount | status | base | upper bound | upper leverage | - | vamm3 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 110 | 150 | 0.25 | + | party | market id | amount | status | base | upper bound | upper leverage | + | vamm3 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 110 | 150 | 0.25 | When the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | lower leverage | proposed fee | @@ -133,10 +133,10 @@ Feature: Test vAMM submission works as expected (invalid submission) When the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | error | - | vamm6 | ETH/MAR22 | 100000 | 0.001 | 101 | 95 | 105 | 0.01 | 0.01 | 0.01 | blah | + | vamm6 | ETH/MAR22 | 100000 | 0.001 | 101 | 95 | 105 | 0.01 | 0.01 | 0.01 | blah | Then the AMM pool status should be: - | party | market id | amount | status | base | lower bound | lower leverage | upper bound | upper leverage | - | vamm6 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 101 | 95 | 0.01 | 105 | 0.01 | + | party | market id | amount | status | base | lower bound | lower leverage | upper bound | upper leverage | + | vamm6 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 101 | 95 | 0.01 | 105 | 0.01 | When the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | lower leverage | proposed fee | diff --git a/core/integration/features/amm/0090-VAMM-006-014.feature b/core/integration/features/amm/0090-VAMM-006-014.feature index e7b5b7987f3..c96d7d99ee3 100644 --- a/core/integration/features/amm/0090-VAMM-006-014.feature +++ b/core/integration/features/amm/0090-VAMM-006-014.feature @@ -478,10 +478,10 @@ Feature: Ensure the vAMM positions follow the market correctly | party5 | ETH/MAR22 | buy | 65 | 120 | 1 | TYPE_LIMIT | TIF_GTC | Then the market data for the market "ETH/MAR22" should be: | mark price | trading mode | ref price | mid price | static mid price | best offer price | best bid price | - | 95 | TRADING_MODE_CONTINUOUS | 100 | 120 | 120 | 121 | 120 | + | 95 | TRADING_MODE_CONTINUOUS | 100 | 120 | 120 | 121 | 119 | And the following trades should be executed: | buyer | price | size | seller | is amm | - | party5 | 114 | 64 | vamm1-id | true | + | party5 | 114 | 65 | vamm1-id | true | # Check the resulting position, vAMM further increased their position When the network moves ahead "1" blocks Then the parties should have the following profit and loss: @@ -490,5 +490,5 @@ Feature: Ensure the vAMM positions follow the market correctly | party2 | -1 | -14 | 0 | | | party3 | -350 | -6650 | 0 | | | party4 | 420 | 7980 | 0 | | - | party5 | 64 | 0 | 0 | | - | vamm1-id | -134 | -1330 | 0 | true | + | party5 | 65 | 0 | 0 | | + | vamm1-id | -135 | -1330 | 0 | true | diff --git a/core/integration/features/amm/0090-VAMM-015.feature b/core/integration/features/amm/0090-VAMM-015.feature index eaf9d28f444..a7a6c194846 100644 --- a/core/integration/features/amm/0090-VAMM-015.feature +++ b/core/integration/features/amm/0090-VAMM-015.feature @@ -101,16 +101,16 @@ Feature: Ensure the vAMM contributes to fee factor setting When the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | - | vamm1 | ETH/MAR22 | 100000 | 0.1 | 80 | 65 | 130 | 0.25 | 0.25 | 0.03 | + | vamm1 | ETH/MAR22 | 100000 | 0.1 | 80 | 65 | 130 | 0.25 | 0.25 | 0.03 | Then the AMM pool status should be: | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | - | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 80 | 65 | 130 | 0.25 | 0.25 | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 80 | 65 | 130 | 0.25 | 0.25 | And set the following AMM sub account aliases: | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | When the network moves ahead "4" blocks diff --git a/core/integration/features/amm/0090-VAMM-016.feature b/core/integration/features/amm/0090-VAMM-016.feature index 858f15ddeb1..c0263bb6e6c 100644 --- a/core/integration/features/amm/0090-VAMM-016.feature +++ b/core/integration/features/amm/0090-VAMM-016.feature @@ -22,11 +22,11 @@ Feature: vAMM has the same ELS as liquidity provision with the same commitment a | market.fee.factors.makerFee | 0.004 | | spam.protection.max.stopOrdersPerMarket | 5 | | market.liquidity.equityLikeShareFeeFraction | 1 | - | market.amm.minCommitmentQuantum | 1 | + | market.amm.minCommitmentQuantum | 1 | | market.liquidity.bondPenaltyParameter | 0.2 | | market.liquidity.stakeToCcyVolume | 1 | | market.liquidity.successorLaunchWindowLength | 1h | - | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0 | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0 | | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | | validators.epoch.length | 10s | | market.liquidity.earlyExitPenalty | 0.25 | @@ -85,7 +85,7 @@ Feature: vAMM has the same ELS as liquidity provision with the same commitment a And the market data for the market "ETH/MAR22" should be: | mark price | trading mode | target stake | supplied stake | open interest | ref price | mid price | static mid price | - | 100 | TRADING_MODE_CONTINUOUS | 39 | 10000 | 0 | 100 | 100 | 100 | + | 100 | TRADING_MODE_CONTINUOUS | 39 | 10000 | 0 | 100 | 100 | 100 | When the parties submit the following liquidity provision: # Using 9788 instead of exactly 10,000 makes things easier because getting exactly 10,000 from an AMM pool as virtual stake can be tricky due to complex math. @@ -103,7 +103,7 @@ Feature: vAMM has the same ELS as liquidity provision with the same commitment a | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 10000 | USD | true | TRANSFER_TYPE_AMM_LOW | Then the network moves ahead "1" epochs @@ -141,7 +141,7 @@ Feature: vAMM has the same ELS as liquidity provision with the same commitment a | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 10000 | USD | true | TRANSFER_TYPE_AMM_LOW | Then the network moves ahead "1" epochs @@ -157,4 +157,4 @@ Feature: vAMM has the same ELS as liquidity provision with the same commitment a And the liquidity provider fee shares for the market "ETH/MAR22" should be: | party | equity like share | virtual stake | average entry valuation | | lp2 | 0.3320682474642305 | 9887.0000000000000000 | 29774 | - | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | 0.3320682474642305 | 9887.0000000000000000 | 19887 | \ No newline at end of file + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | 0.3320682474642305 | 9887.0000000000000000 | 19887 | diff --git a/core/integration/features/amm/0090-VAMM-019.feature b/core/integration/features/amm/0090-VAMM-019.feature index 6d4d5aa4afd..a1e2aac4c18 100644 --- a/core/integration/features/amm/0090-VAMM-019.feature +++ b/core/integration/features/amm/0090-VAMM-019.feature @@ -93,7 +93,7 @@ Feature: Test vAMM cancellation by abandoning. | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | @@ -113,8 +113,8 @@ Feature: Test vAMM cancellation by abandoning. # trying to trade again causes no trades because the AMM has no more volume When the parties place the following orders: - | party | market id | side | volume | price | resulting trades | type | tif | - | party4 | ETH/MAR22 | buy | 500 | 150 | 0 | TYPE_LIMIT | TIF_GTC | + | party | market id | side | volume | price | resulting trades | type | tif | + | party4 | ETH/MAR22 | buy | 500 | 150 | 0 | TYPE_LIMIT | TIF_GTC | # the AMM's mid price has moved to 150, but it has no volume +150 so that best offer comes from the orderbook of 160 Then the market data for the market "ETH/MAR22" should be: @@ -152,7 +152,7 @@ Feature: Test vAMM cancellation by abandoning. | party | asset | market id | general | margin | is amm | | vamm1 | USD | | 925595 | | | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 25595 | USD | true | TRANSFER_TYPE_AMM_RELEASE | | vamm1-id | ACCOUNT_TYPE_MARGIN | | ACCOUNT_TYPE_INSURANCE | ETH/MAR22 | 74548 | USD | true | TRANSFER_TYPE_AMM_RELEASE | diff --git a/core/integration/features/amm/0090-VAMM-020.feature b/core/integration/features/amm/0090-VAMM-020.feature index 38b856d2cae..78dcf289cfa 100644 --- a/core/integration/features/amm/0090-VAMM-020.feature +++ b/core/integration/features/amm/0090-VAMM-020.feature @@ -93,7 +93,7 @@ Feature: Test vAMM cancellation by reduce-only from long. | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | @@ -151,18 +151,18 @@ Feature: Test vAMM cancellation by reduce-only from long. | buyer | price | size | seller | is amm | | party5 | 89 | 10 | party4 | | | party5 | 90 | 10 | party4 | | - | party5 | 90 | 39 | vamm1-id | true | + | party5 | 90 | 19 | vamm1-id | true | | party5 | 91 | 10 | party4 | | - | party5 | 90 | 39 | vamm1-id | true | - | party5 | 94 | 211 | vamm1-id | true | + | party5 | 90 | 19 | vamm1-id | true | + | party5 | 94 | 231 | vamm1-id | true | # check the state of the market, trigger MTM settlement and check balances before closing out the last 100 for the vAMM When the network moves ahead "1" blocks Then the parties should have the following profit and loss: | party | volume | unrealised pnl | realised pnl | is amm | | party4 | -380 | 230 | 0 | | - | party5 | 280 | 276 | 0 | | - | vamm1-id | 100 | -100 | -406 | true | + | party5 | 280 | 196 | 0 | | + | vamm1-id | 100 | -100 | -326 | true | # vAMM is still quoting bid price, though it is in reduce-only mode, and therefore doesn't place those orders. # The best bid should be 40 here? And the market data for the market "ETH/MAR22" should be: @@ -171,14 +171,14 @@ Feature: Test vAMM cancellation by reduce-only from long. # vAMM receives some fees, but pays MTM loss, excess margin is released And the following transfers should happen: | from | from account | to | to account | market id | amount | asset | is amm | type | - | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 80 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | - | vamm1-id | ACCOUNT_TYPE_MARGIN | | ACCOUNT_TYPE_SETTLEMENT | ETH/MAR22 | 506 | USD | true | TRANSFER_TYPE_MTM_LOSS | - | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 45731 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | + | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 87 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | + | vamm1-id | ACCOUNT_TYPE_MARGIN | | ACCOUNT_TYPE_SETTLEMENT | ETH/MAR22 | 426 | USD | true | TRANSFER_TYPE_MTM_LOSS | + | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 45811 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | # After receiving fees, and excess margin is correctly released, the balances of the vAMM sub-accounts match the position: And the parties should have the following account balances: | party | asset | market id | general | margin | is amm | | vamm1 | USD | | 900000 | | | - | vamm1-id | USD | ETH/MAR22 | 81497 | 18225 | true | + | vamm1-id | USD | ETH/MAR22 | 81576 | 18225 | true | # Now make sure the vAMM, though clearly having sufficient balance to increase its position, still doesn't place any buy orders (reduce only check 2) # Like before, place orders at mid, offer, and bid prices @@ -211,8 +211,8 @@ Feature: Test vAMM cancellation by reduce-only from long. Then the parties should have the following profit and loss: | party | volume | unrealised pnl | realised pnl | is amm | | party4 | -380 | -1290 | 0 | | - | party5 | 380 | 1396 | 0 | | - | vamm1-id | 0 | 0 | -106 | true | + | party5 | 380 | 1316 | 0 | | + | vamm1-id | 0 | 0 | -26 | true | And the AMM pool status should be: | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | | vamm1 | ETH/MAR22 | 100000 | STATUS_CANCELLED | 100 | 85 | 150 | 4 | 4 | @@ -224,10 +224,10 @@ Feature: Test vAMM cancellation by reduce-only from long. | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 40 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | | | ACCOUNT_TYPE_SETTLEMENT | vamm1-id | ACCOUNT_TYPE_MARGIN | ETH/MAR22 | 400 | USD | true | TRANSFER_TYPE_MTM_WIN | | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 18625 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | - | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 100162 | USD | true | TRANSFER_TYPE_AMM_RELEASE | + | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 100241 | USD | true | TRANSFER_TYPE_AMM_RELEASE | And the parties should have the following account balances: | party | asset | market id | general | margin | is amm | - | vamm1 | USD | | 1000162 | | | + | vamm1 | USD | | 1000241 | | | | vamm1-id | USD | ETH/MAR22 | 0 | 0 | true | @VAMM @@ -284,18 +284,18 @@ Feature: Test vAMM cancellation by reduce-only from long. | buyer | price | size | seller | is amm | | party5 | 89 | 10 | party4 | | | party5 | 90 | 10 | party4 | | - | party5 | 90 | 39 | vamm1-id | true | + | party5 | 90 | 19 | vamm1-id | true | | party5 | 91 | 10 | party4 | | - | party5 | 90 | 39 | vamm1-id | true | - | party5 | 94 | 211 | vamm1-id | true | + | party5 | 90 | 19 | vamm1-id | true | + | party5 | 94 | 231 | vamm1-id | true | # check the state of the market, trigger MTM settlement and check balances before closing out the last 100 for the vAMM When the network moves ahead "1" blocks Then the parties should have the following profit and loss: | party | volume | unrealised pnl | realised pnl | is amm | | party4 | -380 | 230 | 0 | | - | party5 | 280 | 276 | 0 | | - | vamm1-id | 100 | -100 | -406 | true | + | party5 | 280 | 196 | 0 | | + | vamm1-id | 100 | -100 | -326 | true | # vAMM is still quoting bid price, though it is in reduce-only mode, and therefore doesn't place those orders. # The best bid should be 40 here? And the market data for the market "ETH/MAR22" should be: @@ -304,14 +304,14 @@ Feature: Test vAMM cancellation by reduce-only from long. # vAMM receives some fees, but pays MTM loss, excess margin is released And the following transfers should happen: | from | from account | to | to account | market id | amount | asset | is amm | type | - | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 80 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | - | vamm1-id | ACCOUNT_TYPE_MARGIN | | ACCOUNT_TYPE_SETTLEMENT | ETH/MAR22 | 506 | USD | true | TRANSFER_TYPE_MTM_LOSS | - | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 45731 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | + | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 87 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | + | vamm1-id | ACCOUNT_TYPE_MARGIN | | ACCOUNT_TYPE_SETTLEMENT | ETH/MAR22 | 426 | USD | true | TRANSFER_TYPE_MTM_LOSS | + | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 45811 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | # After receiving fees, and excess margin is correctly released, the balances of the vAMM sub-accounts match the position: And the parties should have the following account balances: | party | asset | market id | general | margin | is amm | | vamm1 | USD | | 900000 | | | - | vamm1-id | USD | ETH/MAR22 | 81497 | 18225 | true | + | vamm1-id | USD | ETH/MAR22 | 81576 | 18225 | true | # Now make sure the vAMM, though clearly having sufficient balance to increase its position, still doesn't place any buy orders (reduce only check 2) # Like before, place orders at mid, offer, and bid prices @@ -343,8 +343,8 @@ Feature: Test vAMM cancellation by reduce-only from long. Then the parties should have the following profit and loss: | party | volume | unrealised pnl | realised pnl | is amm | | party4 | -380 | -1290 | 0 | | - | party5 | 380 | 1396 | 0 | | - | vamm1-id | 0 | 0 | -106 | true | + | party5 | 380 | 1316 | 0 | | + | vamm1-id | 0 | 0 | -26 | true | And the AMM pool status should be: | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | | vamm1 | ETH/MAR22 | 100000 | STATUS_CANCELLED | 100 | 85 | 150 | 4 | 4 | @@ -352,12 +352,12 @@ Feature: Test vAMM cancellation by reduce-only from long. | mark price | trading mode | mid price | static mid price | best offer price | best bid price | | 98 | TRADING_MODE_CONTINUOUS | 135 | 135 | 160 | 110 | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | - | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 40 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | - | | ACCOUNT_TYPE_SETTLEMENT | vamm1-id | ACCOUNT_TYPE_MARGIN | ETH/MAR22 | 400 | USD | true | TRANSFER_TYPE_MTM_WIN | - | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 18625 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | - | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 100162 | USD | true | TRANSFER_TYPE_AMM_RELEASE | + | from | from account | to | to account | market id | amount | asset | is amm | type | + | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 40 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | + | | ACCOUNT_TYPE_SETTLEMENT | vamm1-id | ACCOUNT_TYPE_MARGIN | ETH/MAR22 | 400 | USD | true | TRANSFER_TYPE_MTM_WIN | + | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 18625 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | + | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 100241 | USD | true | TRANSFER_TYPE_AMM_RELEASE | And the parties should have the following account balances: | party | asset | market id | general | margin | is amm | - | vamm1 | USD | | 1000162 | | | + | vamm1 | USD | | 1000241 | | | | vamm1-id | USD | ETH/MAR22 | 0 | 0 | true | diff --git a/core/integration/features/amm/0090-VAMM-021.feature b/core/integration/features/amm/0090-VAMM-021.feature index 03e09261383..6f681508cac 100644 --- a/core/integration/features/amm/0090-VAMM-021.feature +++ b/core/integration/features/amm/0090-VAMM-021.feature @@ -93,7 +93,7 @@ Feature: Test vAMM cancellation by reduce-only from short. | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | @@ -240,10 +240,10 @@ Feature: Test vAMM cancellation by reduce-only from short. | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | | vamm1 | ETH/MAR22 | 100000 | STATUS_CANCELLED | 100 | 85 | 150 | 4 | 4 | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | - | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 47 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | - | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 28208 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | - | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 100331 | USD | true | TRANSFER_TYPE_AMM_RELEASE | + | from | from account | to | to account | market id | amount | asset | is amm | type | + | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 47 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | + | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 28208 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | + | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 100331 | USD | true | TRANSFER_TYPE_AMM_RELEASE | And the parties should have the following account balances: | party | asset | market id | general | margin | is amm | | vamm1 | USD | | 1000331 | | | @@ -396,14 +396,14 @@ Feature: Test vAMM cancellation by reduce-only from short. | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | | vamm1 | ETH/MAR22 | 100000 | STATUS_CANCELLED | 100 | 85 | 150 | 4 | 4 | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | - | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 47 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | - | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 28208 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | - | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 100331 | USD | true | TRANSFER_TYPE_AMM_RELEASE | + | from | from account | to | to account | market id | amount | asset | is amm | type | + | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 47 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | + | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 28208 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | + | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 100331 | USD | true | TRANSFER_TYPE_AMM_RELEASE | And the parties should have the following account balances: | party | asset | market id | general | margin | is amm | | vamm1 | USD | | 1000331 | | | | vamm1-id | USD | ETH/MAR22 | 0 | 0 | true | And the market data for the market "ETH/MAR22" should be: | mark price | trading mode | mid price | static mid price | best offer price | best bid price | - | 107 | TRADING_MODE_CONTINUOUS | 70 | 70 | 100 | 40 | \ No newline at end of file + | 107 | TRADING_MODE_CONTINUOUS | 70 | 70 | 100 | 40 | diff --git a/core/integration/features/amm/0090-VAMM-022.feature b/core/integration/features/amm/0090-VAMM-022.feature index 546884ae69e..8dc8c17395c 100644 --- a/core/integration/features/amm/0090-VAMM-022.feature +++ b/core/integration/features/amm/0090-VAMM-022.feature @@ -98,10 +98,10 @@ Feature: Test vAMM cancellation without position works as expected. # Now submit our vAMM, no trades should happen Then the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | - | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 150 | 0.25 | 0.25 | 0.01 | + | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 150 | 0.25 | 0.25 | 0.01 | Then the AMM pool status should be: | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | - | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 150 | 0.25 | 0.25 | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 150 | 0.25 | 0.25 | And set the following AMM sub account aliases: | party | market id | alias | diff --git a/core/integration/features/amm/0090-VAMM-023.feature b/core/integration/features/amm/0090-VAMM-023.feature index 8d6bf4773fc..67d73df4f7f 100644 --- a/core/integration/features/amm/0090-VAMM-023.feature +++ b/core/integration/features/amm/0090-VAMM-023.feature @@ -98,16 +98,16 @@ Feature: Test cancelled vAMM becomes active on amend. # Now submit our vAMM, no trades should happen Then the parties submit the following AMM: | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | - | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 150 | 0.25 | 0.25 | 0.01 | + | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 150 | 0.25 | 0.25 | 0.01 | Then the AMM pool status should be: | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | - | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 150 | 0.25 | 0.25 | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 150 | 0.25 | 0.25 | And set the following AMM sub account aliases: | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-acc | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-acc | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | And the parties should have the following account balances: | party | asset | market id | general | margin | is amm | @@ -120,7 +120,7 @@ Feature: Test cancelled vAMM becomes active on amend. | vamm1 | ETH/MAR22 | METHOD_REDUCE_ONLY | Then the AMM pool status should be: | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | - | vamm1 | ETH/MAR22 | 100000 | STATUS_REDUCE_ONLY | 100 | 85 | 150 | 0.25 | 0.25 | + | vamm1 | ETH/MAR22 | 100000 | STATUS_REDUCE_ONLY | 100 | 85 | 150 | 0.25 | 0.25 | # Balance is not yet released And the parties should have the following account balances: | party | asset | market id | general | margin | is amm | @@ -133,7 +133,7 @@ Feature: Test cancelled vAMM becomes active on amend. | vamm1 | ETH/MAR22 | 0.1 | 105 | 90 | 155 | 0.25 | 0.25 | Then the AMM pool status should be: | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | - | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 105 | 90 | 155 | 0.25 | 0.25 | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 105 | 90 | 155 | 0.25 | 0.25 | # Now trigger a MTM settlement, this should not change anything, whereas without the amend it'd move the vAMM to the cancelled status. When the network moves ahead "1" blocks diff --git a/core/integration/features/amm/0090-VAMM-024-026.feature b/core/integration/features/amm/0090-VAMM-024-026.feature index db92004c1f7..1cf7d8cb50a 100644 --- a/core/integration/features/amm/0090-VAMM-024-026.feature +++ b/core/integration/features/amm/0090-VAMM-024-026.feature @@ -93,7 +93,7 @@ Feature: When market.amm.minCommitmentQuantum is 1000, mid price of the market 1 | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 30000 | USD | true | TRANSFER_TYPE_AMM_LOW | @VAMM diff --git a/core/integration/features/amm/0090-VAMM-028.feature b/core/integration/features/amm/0090-VAMM-028.feature index c92ce6aa976..d4b0a2c2a3a 100644 --- a/core/integration/features/amm/0090-VAMM-028.feature +++ b/core/integration/features/amm/0090-VAMM-028.feature @@ -22,7 +22,7 @@ Feature: Ensure the vAMM positions follow the market correctly | market.fee.factors.makerFee | 0.004 | | spam.protection.max.stopOrdersPerMarket | 5 | | market.liquidity.equityLikeShareFeeFraction | 1 | - | market.amm.minCommitmentQuantum | 1 | + | market.amm.minCommitmentQuantum | 1 | | market.liquidity.bondPenaltyParameter | 0.2 | | market.liquidity.stakeToCcyVolume | 1 | | market.liquidity.successorLaunchWindowLength | 1h | @@ -111,7 +111,7 @@ Feature: Ensure the vAMM positions follow the market correctly | vamm1 | ETH/MAR22 | vamm1-id | | vamm2 | ETH/MAR23 | vamm2-id | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | | vamm2 | ACCOUNT_TYPE_GENERAL | vamm2-id | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | diff --git a/core/integration/features/amm/0090-VAMM-031.feature b/core/integration/features/amm/0090-VAMM-031.feature index b9ffd7db0c6..c3ec51a1a38 100644 --- a/core/integration/features/amm/0090-VAMM-031.feature +++ b/core/integration/features/amm/0090-VAMM-031.feature @@ -103,8 +103,8 @@ Feature: vAMM behaviour when a market settles | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | - | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 30000 | USD | true | TRANSFER_TYPE_AMM_LOW | + | from | from account | to | to account | market id | amount | asset | is amm | type | + | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 30000 | USD | true | TRANSFER_TYPE_AMM_LOW | @VAMM Scenario Outline: 0090-VAMM-031: When an AMM is active on a market at time of settlement with a position in a well collateralised state, the market can settle successfully and then all funds on the AMM key are transferred back to the main party's account. @@ -146,10 +146,10 @@ Feature: vAMM behaviour when a market settles # verify the that the margin balance is released, and then the correct balance if transferred from the pool account back to the party. And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | - | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 1 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | - | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | | USD | true | TRANSFER_TYPE_MARGIN_HIGH | - | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | | | USD | true | TRANSFER_TYPE_AMM_RELEASE | + | from | from account | to | to account | market id | amount | asset | is amm | type | + | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 1 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | + | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | | USD | true | TRANSFER_TYPE_MARGIN_HIGH | + | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | | | USD | true | TRANSFER_TYPE_AMM_RELEASE | And the parties should have the following account balances: | party | asset | market id | general | margin | is amm | | vamm1 | USD | | | | | diff --git a/core/integration/features/amm/0090-VAMM-032.feature b/core/integration/features/amm/0090-VAMM-032.feature index dd3f9ea03d2..0819a84faeb 100644 --- a/core/integration/features/amm/0090-VAMM-032.feature +++ b/core/integration/features/amm/0090-VAMM-032.feature @@ -101,7 +101,7 @@ Feature: vAMM behaviour when a market settles with distressed AMM. | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | + | from | from account | to | to account | market id | amount | asset | is amm | type | | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 30000 | USD | true | TRANSFER_TYPE_AMM_LOW | @VAMM @@ -123,8 +123,8 @@ Feature: vAMM behaviour when a market settles with distressed AMM. | 100 | TRADING_MODE_CONTINUOUS | 101 | 101 | 1000 | 119 | And the parties should have the following profit and loss: | party | volume | unrealised pnl | realised pnl | is amm | - | party4 | 2 | 0 | 0 | | - | vamm1-id | -2 | 0 | 0 | true | + | party4 | 2 | 0 | 0 | | + | vamm1-id | -2 | 0 | 0 | true | And the AMM pool status should be: | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | | vamm1 | ETH/MAR22 | 30000 | STATUS_ACTIVE | 100 | 85 | 150 | 4 | 4 | @@ -151,13 +151,13 @@ Feature: vAMM behaviour when a market settles with distressed AMM. # and lastly a transfer of the general account back to the owner. Then debug transfers And the following transfers should happen: - | from | from account | to | to account | market id | amount | asset | is amm | type | - | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 1 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | - | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 1 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | - | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_MARGIN | ETH/MAR22 | 420 | USD | true | TRANSFER_TYPE_MARGIN_LOW | - | vamm1-id | ACCOUNT_TYPE_MARGIN | | ACCOUNT_TYPE_SETTLEMENT | ETH/MAR22 | 420 | USD | true | TRANSFER_TYPE_LOSS | - | vamm1-id | ACCOUNT_TYPE_GENERAL | | ACCOUNT_TYPE_SETTLEMENT | ETH/MAR22 | 1380 | USD | true | TRANSFER_TYPE_LOSS | - | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | | 28204 | USD | true | TRANSFER_TYPE_AMM_RELEASE | + | from | from account | to | to account | market id | amount | asset | is amm | type | + | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 1 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | + | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 1 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | + | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_MARGIN | ETH/MAR22 | 420 | USD | true | TRANSFER_TYPE_MARGIN_LOW | + | vamm1-id | ACCOUNT_TYPE_MARGIN | | ACCOUNT_TYPE_SETTLEMENT | ETH/MAR22 | 420 | USD | true | TRANSFER_TYPE_LOSS | + | vamm1-id | ACCOUNT_TYPE_GENERAL | | ACCOUNT_TYPE_SETTLEMENT | ETH/MAR22 | 1380 | USD | true | TRANSFER_TYPE_LOSS | + | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | | 28204 | USD | true | TRANSFER_TYPE_AMM_RELEASE | And the parties should have the following account balances: | party | asset | market id | general | margin | is amm | | vamm1 | USD | | 28204 | | | diff --git a/core/integration/features/amm/0090-VAMM-035.feature b/core/integration/features/amm/0090-VAMM-035.feature new file mode 100644 index 00000000000..8846bb481d3 --- /dev/null +++ b/core/integration/features/amm/0090-VAMM-035.feature @@ -0,0 +1,208 @@ +Feature: With two vAMMs existing on the market, and no other orders, both of which have the same fair price, another counterparty placing a large buy order for a given volume, followed by a large sell order for the same volume, results in the vAMMs both taking a position and then returning to 0 position, with a balance increase equal to the maker fees received plus those for the incoming trader crossing the spread. + + Background: + Given the average block duration is "1" + And the following network parameters are set: + | name | value | + | market.value.windowLength | 60s | + | validators.epoch.length | 10s | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 2s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | + | spam.protection.max.stopOrdersPerMarket | 5 | + | market.liquidity.equityLikeShareFeeFraction | 1 | + | market.amm.minCommitmentQuantum | 1 | + | market.liquidity.bondPenaltyParameter | 0.2 | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.1 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | + | market.liquidity.maximumLiquidityFeeFactorLevel | 0.25 | + + And the following assets are registered: + | id | decimal places | + | USD | 0 | + And the fees configuration named "fees-config-1": + | maker fee | infrastructure fee | + | 0.0004 | 0.001 | + And the markets: + | id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | + | ETH/MAR22 | USD | USD | default-log-normal-risk-model | default-margin-calculator | 2 | fees-config-1 | default-none | default-eth-for-future | 1e0 | 0 | default-basic | + + @VAMM + Scenario: Double-sided vAMMs + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | party1 | USD | 100000 | + | party2 | USD | 100000 | + | party3 | USD | 100000 | + | vamm1 | USD | 100000 | + | vamm2 | USD | 100000 | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party1 | ETH/MAR22 | buy | 1 | 1 | 0 | TYPE_LIMIT | TIF_GTC | + | party1 | ETH/MAR22 | buy | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | + | party2 | ETH/MAR22 | sell | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | + | party2 | ETH/MAR22 | sell | 1 | 200 | 0 | TYPE_LIMIT | TIF_GTC | + And the opening auction period ends for market "ETH/MAR22" + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 100 | 1 | party2 | + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | + | 100 | TRADING_MODE_CONTINUOUS | + + When the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | + | vamm1 | ETH/MAR22 | 50000 | 0.1 | 100 | 85 | 115 | 0.25 | 0.3 | 0.01 | + | vamm2 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 115 | 0.25 | 0.3 | 0.012 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 50000 | STATUS_ACTIVE | 100 | 85 | 115 | 0.25 | 0.3 | + | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 115 | 0.25 | 0.3 | + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-acc | + | vamm2 | ETH/MAR22 | vamm2-acc | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-acc | ACCOUNT_TYPE_GENERAL | | 50000 | USD | true | TRANSFER_TYPE_AMM_LOW | + | vamm2 | ACCOUNT_TYPE_GENERAL | vamm2-acc | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | + + Then the parties should have the following account balances: + | party | asset | market id | margin | general | vesting | vested | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | USD | ETH/MAR22 | 0 | 50000 | | | + | 4582953f1f1dd07603befe97994d6414c0ebb53c7d52c29e866bb3e85d7b30b4 | USD | ETH/MAR22 | 0 | 100000 | | | + + When the network moves ahead "11" blocks + Then the current epoch is "0" + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party1 | ETH/MAR22 | buy | 100 | 0 | 2 | TYPE_MARKET | TIF_FOK | + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 101 | 34 | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | + | party1 | 101 | 66 | 4582953f1f1dd07603befe97994d6414c0ebb53c7d52c29e866bb3e85d7b30b4 | + + When the network moves ahead "2" blocks + And the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | + | party1 | 101 | 1 | 0 | + | party2 | -1 | -1 | 0 | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | -34 | 0 | 0 | + | 4582953f1f1dd07603befe97994d6414c0ebb53c7d52c29e866bb3e85d7b30b4 | -66 | 0 | 0 | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party2 | ETH/MAR22 | sell | 100 | 0 | 2 | TYPE_MARKET | TIF_FOK | + Then debug trades + Then the following trades should be executed: + | buyer | price | size | seller | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | 101 | 34 | party2 | + | 4582953f1f1dd07603befe97994d6414c0ebb53c7d52c29e866bb3e85d7b30b4 | 101 | 66 | party2 | + + When the network moves ahead "2" blocks + And the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | + | party1 | 101 | 1 | 0 | + | party2 | -101 | -1 | 0 | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | 0 | 0 | 0 | + | 4582953f1f1dd07603befe97994d6414c0ebb53c7d52c29e866bb3e85d7b30b4 | 0 | 0 | 0 | + + Then the parties should have the following account balances: + | party | asset | market id | margin | general | vesting | vested | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | USD | ETH/MAR22 | 0 | 50028 | | | + | 4582953f1f1dd07603befe97994d6414c0ebb53c7d52c29e866bb3e85d7b30b4 | USD | ETH/MAR22 | 0 | 100054 | | | + +Scenario: Single-sided vAMMs + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | party1 | USD | 100000 | + | party2 | USD | 100000 | + | party3 | USD | 100000 | + | vamm1 | USD | 100000 | + | vamm2 | USD | 100000 | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party1 | ETH/MAR22 | buy | 1 | 1 | 0 | TYPE_LIMIT | TIF_GTC | + | party1 | ETH/MAR22 | buy | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | + | party2 | ETH/MAR22 | sell | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | + | party2 | ETH/MAR22 | sell | 1 | 200 | 0 | TYPE_LIMIT | TIF_GTC | + And the opening auction period ends for market "ETH/MAR22" + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 100 | 1 | party2 | + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | + | 100 | TRADING_MODE_CONTINUOUS | + + When the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | + | vamm1 | ETH/MAR22 | 50000 | 0.1 | 100 | | 115 | | 0.3 | 0.01 | + | vamm2 | ETH/MAR22 | 100000 | 0.1 | 100 | | 115 | | 0.3 | 0.012 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 50000 | STATUS_ACTIVE | 100 | | 115 | | 0.3 | + | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | | 115 | | 0.3 | + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-acc | + | vamm2 | ETH/MAR22 | vamm2-acc | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-acc | ACCOUNT_TYPE_GENERAL | | 50000 | USD | true | TRANSFER_TYPE_AMM_LOW | + | vamm2 | ACCOUNT_TYPE_GENERAL | vamm2-acc | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | + + Then the parties should have the following account balances: + | party | asset | market id | margin | general | vesting | vested | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | USD | ETH/MAR22 | 0 | 50000 | | | + | 4582953f1f1dd07603befe97994d6414c0ebb53c7d52c29e866bb3e85d7b30b4 | USD | ETH/MAR22 | 0 | 100000 | | | + + When the network moves ahead "11" blocks + Then the current epoch is "0" + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party1 | ETH/MAR22 | buy | 100 | 0 | 2 | TYPE_MARKET | TIF_FOK | + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 101 | 34 | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | + | party1 | 101 | 66 | 4582953f1f1dd07603befe97994d6414c0ebb53c7d52c29e866bb3e85d7b30b4 | + + When the network moves ahead "2" blocks + And the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | + | party1 | 101 | 1 | 0 | + | party2 | -1 | -1 | 0 | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | -34 | 0 | 0 | + | 4582953f1f1dd07603befe97994d6414c0ebb53c7d52c29e866bb3e85d7b30b4 | -66 | 0 | 0 | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party2 | ETH/MAR22 | sell | 100 | 0 | 2 | TYPE_MARKET | TIF_FOK | + Then debug trades + Then the following trades should be executed: + | buyer | price | size | seller | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | 101 | 34 | party2 | + | 4582953f1f1dd07603befe97994d6414c0ebb53c7d52c29e866bb3e85d7b30b4 | 101 | 66 | party2 | + + When the network moves ahead "2" blocks + And the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | + | party1 | 101 | 1 | 0 | + | party2 | -101 | -1 | 0 | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | 0 | 0 | 0 | + | 4582953f1f1dd07603befe97994d6414c0ebb53c7d52c29e866bb3e85d7b30b4 | 0 | 0 | 0 | + + Then the parties should have the following account balances: + | party | asset | market id | margin | general | vesting | vested | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | USD | ETH/MAR22 | 0 | 50028 | | | + | 4582953f1f1dd07603befe97994d6414c0ebb53c7d52c29e866bb3e85d7b30b4 | USD | ETH/MAR22 | 0 | 100054 | | | \ No newline at end of file diff --git a/core/integration/features/amm/0090-VAMM-037.feature b/core/integration/features/amm/0090-VAMM-037.feature new file mode 100644 index 00000000000..f1ac9270622 --- /dev/null +++ b/core/integration/features/amm/0090-VAMM-037.feature @@ -0,0 +1,212 @@ +Feature: 0090-VAMM-037: Pegged orders are deployed using vAMM orders as pegs if possible + Background: + Given the average block duration is "1" + And the margin calculator named "margin-calculator-1": + | search factor | initial factor | release factor | + | 1.2 | 1.5 | 1.7 | + And the log normal risk model named "log-normal-risk-model": + | risk aversion | tau | mu | r | sigma | + | 0.001 | 0.0011407711613050422 | 0 | 0.9 | 3.0 | + And the liquidity monitoring parameters: + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.00 | 20s | 1 | + + And the following network parameters are set: + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 2s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | + | spam.protection.max.stopOrdersPerMarket | 5 | + | market.liquidity.equityLikeShareFeeFraction | 1 | + | market.amm.minCommitmentQuantum | 1 | + | market.liquidity.bondPenaltyParameter | 0.2 | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.1 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | + | market.liquidity.maximumLiquidityFeeFactorLevel | 0.25 | + #risk factor short:3.5569037 + #risk factor long:0.801225765 + And the following assets are registered: + | id | decimal places | + | USD | 0 | + And the fees configuration named "fees-config-1": + | maker fee | infrastructure fee | + | 0.0004 | 0.001 | + + And the liquidity sla params named "SLA-22": + | price range | commitment min time fraction | performance hysteresis epochs | sla competition factor | + | 0.5 | 0.6 | 1 | 1.0 | + + And the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | + | ETH/MAR22 | USD | USD | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | default-none | default-eth-for-future | 1e0 | 0 | SLA-22 | + + # Setting up the accounts and vAMM submission now is part of the background, because we'll be running scenarios 0090-VAMM-006 through 0090-VAMM-014 on this setup + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lp1 | USD | 1000000 | + | lp2 | USD | 1000000 | + | lp3 | USD | 1000000 | + | party1 | USD | 1000000 | + | party2 | USD | 1000000 | + | party3 | USD | 1000000 | + | party4 | USD | 1000000 | + | party5 | USD | 1000000 | + | vamm1 | USD | 1000000 | + + When the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR22 | 600 | 0.02 | submission | + | lp_2 | lp2 | ETH/MAR22 | 400 | 0.015 | submission | + Then the network moves ahead "4" blocks + And the current epoch is "0" + + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | lp1 | ETH/MAR22 | buy | 20 | 40 | 0 | TYPE_LIMIT | TIF_GTC | LP1BO | + | party1 | ETH/MAR22 | buy | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | ETH/MAR22 | sell | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | lp1 | ETH/MAR22 | sell | 10 | 160 | 0 | TYPE_LIMIT | TIF_GTC | LP1SO | + When the opening auction period ends for market "ETH/MAR22" + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 100 | 1 | party2 | + + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | target stake | supplied stake | open interest | ref price | mid price | static mid price | + | 100 | TRADING_MODE_CONTINUOUS | 39 | 1000 | 1 | 100 | 100 | 100 | + When the parties submit the following AMM: + | party | market id | amount | slippage | base | upper bound | upper leverage | proposed fee | + | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 150 | 4 | 0.01 | + Then the AMM pool status should be: + | party | market id | amount | status | base | upper bound | upper leverage | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 150 | 4 | + + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-id | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | + + @VAMM + Scenario: 0090-VAMM-037: With an existing book consisting solely of vAMM orders on one side, pegged orders referencing best bid/best ask remain deployed on the side with the vAMM orders. Pegged orders referencing the empty side of the book are parked. + # LPs submit pegged iceberg orders + When the parties place the following pegged iceberg orders: + | party | market id | side | volume | peak size | minimum visible size | pegged reference | offset | + | lp1 | ETH/MAR22 | buy | 100 | 10 | 2 | BID | 5 | + | lp1 | ETH/MAR22 | sell | 100 | 10 | 2 | ASK | 5 | + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 35 | 10 | + | sell | 106 | 10 | + | sell | 160 | 10 | + + # Make sure the book stays the same when moving ahead blocks + When the network moves ahead "1" blocks + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 35 | 10 | + | sell | 106 | 10 | + | sell | 160 | 10 | + + When the parties cancel the following orders: + | party | reference | + | lp1 | LP1BO | + | lp1 | LP1SO | + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 0 | + | buy | 35 | 0 | + | sell | 106 | 10 | + | sell | 160 | 0 | + + # Move ahead 1 block to ensure the end block CheckBook call doesn't panic. + When the network moves ahead "1" blocks + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 0 | + | buy | 35 | 0 | + | sell | 106 | 10 | + | sell | 160 | 0 | + + # Switch sides, still on an empty book, the pegged orders on sell side are now parked + # but the buy order is deployed again. + When the parties amend the following AMM: + | party | market id | slippage | base | lower bound | + | vamm1 | ETH/MAR22 | 0.05 | 100 | 85 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | + + When the network moves ahead "1" blocks + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 0 | + | buy | 94 | 10 | + | sell | 106 | 0 | + | sell | 160 | 0 | + + @VAMM + Scenario: 0090-VAMM-037: Cancelling an AMM with pegged orders hanging off its quotes causes them to be re-pegged + + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | ref price | mid price | static mid price | best bid price | best offer price | + | 100 | TRADING_MODE_CONTINUOUS | 100 | 70 | 70 | 40 | 101 | + + + When the parties place the following pegged orders: + | party | market id | side | volume | pegged reference | offset | + | lp1 | ETH/MAR22 | buy | 100 | BID | 5 | + | lp1 | ETH/MAR22 | sell | 100 | ASK | 5 | + + + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 35 | 100 | + | sell | 106 | 100 | + | sell | 160 | 10 | + + # cancel the AMM and the pegged order should get repriced + When the network moves ahead "1" blocks + When the parties cancel the following AMM: + | party | market id | method | + | vamm1 | ETH/MAR22 | METHOD_IMMEDIATE | + + Then the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | ref price | mid price | static mid price | best bid price | best offer price | + | 100 | TRADING_MODE_CONTINUOUS | 100 | 100 | 100 | 40 | 160 | + + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 35 | 100 | + | sell | 160 | 10 | + | sell | 165 | 100 | + + # submit a new AMM that will cause a reprice again + When the network moves ahead "1" blocks + When the parties submit the following AMM: + | party | market id | amount | slippage | lower bound | base | upper bound | upper leverage | proposed fee | + | vamm1 | ETH/MAR22 | 100000 | 0.1 | 90 | 100 | 150 | 4 | 0.01 | + + Then the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | ref price | mid price | static mid price | best bid price | best offer price | + | 100 | TRADING_MODE_CONTINUOUS | 100 | 100 | 100 | 99 | 101 | + + + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 94 | 100 | + | sell | 106 | 100 | + | sell | 160 | 10 | \ No newline at end of file diff --git a/core/integration/features/amm/0090-VAMM-092.feature b/core/integration/features/amm/0090-VAMM-092.feature new file mode 100644 index 00000000000..959f28e9620 --- /dev/null +++ b/core/integration/features/amm/0090-VAMM-092.feature @@ -0,0 +1,157 @@ +Feature: check VAMM when SLA bond penalty is due + + Background: + Given the average block duration is "1" + And the margin calculator named "margin-calculator-1": + | search factor | initial factor | release factor | + | 1.2 | 1.5 | 1.7 | + And the log normal risk model named "log-normal-risk-model": + | risk aversion | tau | mu | r | sigma | + | 0.001 | 0.0011407711613050422 | 0 | 0.9 | 3.0 | + And the liquidity monitoring parameters: + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.00 | 20s | 1 | + + And the following network parameters are set: + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 0s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | + | spam.protection.max.stopOrdersPerMarket | 5 | + | market.liquidity.equityLikeShareFeeFraction | 1 | + | market.amm.minCommitmentQuantum | 1000 | + | market.liquidity.bondPenaltyParameter | 0.2 | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.1 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | + | market.liquidity.maximumLiquidityFeeFactorLevel | 0.25 | + #risk factor short:3.5569036 + #risk factor long:0.801225765 + And the following assets are registered: + | id | decimal places | + | USD | 0 | + And the fees configuration named "fees-config-1": + | maker fee | infrastructure fee | + | 0.0004 | 0.001 | + + And the liquidity sla params named "SLA-22": + | price range | commitment min time fraction | performance hysteresis epochs | sla competition factor | + | 0.5 | 0.6 | 1 | 1.0 | + + And the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | + | ETH/MAR22 | USD | USD | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | default-none | default-eth-for-future | 1e0 | 0 | SLA-22 | + + Scenario: set up AMM + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lp1 | USD | 1000000 | + | lp2 | USD | 1000000 | + | lp3 | USD | 1000000 | + | party1 | USD | 1000000 | + | party2 | USD | 1000000 | + | party3 | USD | 1000000 | + | party4 | USD | 1000000 | + | party5 | USD | 1000000 | + | vamm1 | USD | 30000 | + + When the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR22 | 600 | 0.02 | submission | + | lp_2 | lp2 | ETH/MAR22 | 400 | 0.015 | submission | + Then the network moves ahead "4" blocks + And the current epoch is "0" + + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | lp1 | ETH/MAR22 | buy | 20 | 40 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + | party1 | ETH/MAR22 | buy | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | ETH/MAR22 | sell | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | lp1 | ETH/MAR22 | sell | 10 | 160 | 0 | TYPE_LIMIT | TIF_GTC | lp1-s | + When the opening auction period ends for market "ETH/MAR22" + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 100 | 1 | party2 | + + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | target stake | supplied stake | open interest | ref price | mid price | static mid price | + | 100 | TRADING_MODE_CONTINUOUS | 39 | 1000 | 1 | 100 | 100 | 100 | + When the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | + | vamm1 | ETH/MAR22 | 30000 | 0.1 | 100 | 85 | 150 | 4 | 4 | 0.01 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 30000 | STATUS_ACTIVE | 100 | 85 | 150 | 4 | 4 | + + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-id | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 30000 | USD | true | TRANSFER_TYPE_AMM_LOW | + + Then the parties should have the following account balances: + | party | asset | market id | general | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | USD | ETH/MAR22 | 30000 | + + Then the parties should have the following account balances: + | party | asset | market id | margin | general | bond | + | lp1 | USD | ETH/MAR22 | 960 | 998440 | 600 | + | lp2 | USD | ETH/MAR22 | 0 | 999600 | 400 | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | USD | ETH/MAR22 | 0 | 30000 | | + + Then the network moves ahead "8" blocks + + Then the parties should have the following account balances: + | party | asset | market id | margin | general | bond | + | lp1 | USD | ETH/MAR22 | 960 | 998440 | 540 | + | lp2 | USD | ETH/MAR22 | 0 | 999600 | 360 | + | 137112507e25d3845a56c47db15d8ced0f28daa8498a0fd52648969c4b296aba | USD | ETH/MAR22 | 0 | 30000 | | + + #0042-LIQF-092:All vAMMs active on a market at the end of an epoch receive SLA bonus rebalancing payments with `0` penalty fraction. + #both lp1 and lp2 got SLA penalty while vamm1 did not + Then the following transfers should happen: + | from | to | from account | to account | market id | amount | asset | + | lp1 | market | ACCOUNT_TYPE_BOND | ACCOUNT_TYPE_INSURANCE | ETH/MAR22 | 60 | USD | + | lp2 | market | ACCOUNT_TYPE_BOND | ACCOUNT_TYPE_INSURANCE | ETH/MAR22 | 40 | USD | + + Then the network moves ahead "8" blocks + + When the parties cancel the following AMM: + | party | market id | method | + | vamm1 | ETH/MAR22 | METHOD_IMMEDIATE | + + Then the parties should have the following account balances: + | party | asset | market id | general | + | vamm1 | USD | ETH/MAR22 | 30000 | + + Then the network moves ahead "4" blocks + #0042-LIQF-093:A vAMM active on a market during an epoch, which was cancelled prior to the end of an epoch, receives SLA bonus rebalancing payments with `0` penalty fraction. + #both lp1 and lp2 got SLA penalty while vamm1 did not, and asset has been released from alias back to vamm1 + Then the following transfers should happen: + | from | to | from account | to account | market id | amount | asset | + | lp1 | market | ACCOUNT_TYPE_BOND | ACCOUNT_TYPE_INSURANCE | ETH/MAR22 | 54 | USD | + | lp2 | market | ACCOUNT_TYPE_BOND | ACCOUNT_TYPE_INSURANCE | ETH/MAR22 | 36 | USD | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party4 | ETH/MAR22 | buy | 4 | 120 | 0 | TYPE_LIMIT | TIF_GTC | + + #0042-LIQF-094:A vAMMs cancelled in a previous epoch does not receive anything and is not considered during SLA rebalancing at the end of an epoch + Then the network moves ahead "12" blocks + + Then the following transfers should happen: + | from | to | from account | to account | market id | amount | asset | + | lp1 | market | ACCOUNT_TYPE_BOND | ACCOUNT_TYPE_INSURANCE | ETH/MAR22 | 48 | USD | + | lp2 | market | ACCOUNT_TYPE_BOND | ACCOUNT_TYPE_INSURANCE | ETH/MAR22 | 32 | USD | + + Then the parties should have the following account balances: + | party | asset | market id | general | + | vamm1 | USD | ETH/MAR22 | 30000 | + diff --git a/core/integration/features/amm/0090-VAMM-auction.feature b/core/integration/features/amm/0090-VAMM-auction.feature index 05bf409062e..48c29547549 100644 --- a/core/integration/features/amm/0090-VAMM-auction.feature +++ b/core/integration/features/amm/0090-VAMM-auction.feature @@ -61,6 +61,8 @@ Feature: vAMM rebasing when created or amended | party5 | USD | 1000000 | | vamm1 | USD | 1000000 | | vamm2 | USD | 1000000 | + | vamm3 | USD | 1000000 | + | vamm4 | USD | 1000000 | @VAMM Scenario: two crossed AMMs at opening auction end @@ -436,4 +438,70 @@ Feature: vAMM rebasing when created or amended Then the network moves ahead "1" blocks And the market data for the market "ETH/MAR22" should be: | mark price | trading mode | best bid price | best offer price | - | 96 | TRADING_MODE_CONTINUOUS | 96 | 98 | \ No newline at end of file + | 96 | TRADING_MODE_CONTINUOUS | 96 | 98 | + +@VAMM + Scenario: complicated AMM's crossed with orders and pegged orders + + # these are buys + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | lp1 | ETH/MAR22 | buy | 50 | 105 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + | lp1 | ETH/MAR22 | buy | 50 | 103 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + | lp1 | ETH/MAR22 | buy | 50 | 101 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + | lp1 | ETH/MAR22 | buy | 50 | 99 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + | lp1 | ETH/MAR22 | buy | 50 | 98 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + + # these are sells + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | lp2 | ETH/MAR22 | sell | 50 | 102 | 0 | TYPE_LIMIT | TIF_GTC | lp2-b | + | lp2 | ETH/MAR22 | sell | 50 | 98 | 0 | TYPE_LIMIT | TIF_GTC | lp2-b | + | lp2 | ETH/MAR22 | sell | 50 | 95 | 0 | TYPE_LIMIT | TIF_GTC | lp2-b | + + + Then the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | + | vamm1 | ETH/MAR22 | 100000 | 0.05 | 100 | 90 | 110 | 0.03 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 90 | 110 | + + Then the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | + | vamm2 | ETH/MAR22 | 100000 | 0.05 | 90 | 85 | 95 | 0.03 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | + | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 90 | 85 | 95 | + + + Then the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | + | vamm3 | ETH/MAR22 | 100000 | 0.05 | 90 | 85 | 95 | 0.03 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | + | vamm3 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 90 | 85 | 95 | + + + + # now place some pegged orders which will cause a panic if the uncrossing is crossed + When the parties place the following pegged orders: + | party | market id | side | volume | pegged reference | offset | + | lp3 | ETH/MAR22 | buy | 100 | BID | 1 | + | lp3 | ETH/MAR22 | sell | 100 | ASK | 1 | + + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-id | + | vamm2 | ETH/MAR22 | vamm2-id | + + + And the market data for the market "ETH/MAR22" should be: + | trading mode | indicative price | indicative volume | + | TRADING_MODE_OPENING_AUCTION | 93 | 602 | + + + When the opening auction period ends for market "ETH/MAR22" + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | best bid price | best offer price | + | 93 | TRADING_MODE_CONTINUOUS | 92 | 94 | \ No newline at end of file diff --git a/core/integration/features/amm/0090-VAMM-market-dp.feature b/core/integration/features/amm/0090-VAMM-market-dp.feature new file mode 100644 index 00000000000..a9c0c1bf8a7 --- /dev/null +++ b/core/integration/features/amm/0090-VAMM-market-dp.feature @@ -0,0 +1,175 @@ +Feature: vAMM rebasing when created or amended + + Background: + Given the average block duration is "1" + And the margin calculator named "margin-calculator-1": + | search factor | initial factor | release factor | + | 1.2 | 1.5 | 1.7 | + And the log normal risk model named "log-normal-risk-model": + | risk aversion | tau | mu | r | sigma | + | 0.001 | 0.0011407711613050422 | 0 | 0.9 | 3.0 | + And the liquidity monitoring parameters: + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.00 | 20s | 1 | + + And the following network parameters are set: + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 0s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | + | spam.protection.max.stopOrdersPerMarket | 5 | + | market.liquidity.equityLikeShareFeeFraction | 1 | + | market.amm.minCommitmentQuantum | 1 | + | market.liquidity.bondPenaltyParameter | 0.2 | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | + | market.liquidity.maximumLiquidityFeeFactorLevel | 0.25 | + #risk factor short:3.5569036 + #risk factor long:0.801225765 + And the following assets are registered: + | id | decimal places | + | USD | 2 | + And the fees configuration named "fees-config-1": + | maker fee | infrastructure fee | + | 0.0004 | 0.001 | + + And the liquidity sla params named "SLA-22": + | price range | commitment min time fraction | performance hysteresis epochs | sla competition factor | + | 0.5 | 0.6 | 1 | 1.0 | + + And the oracle spec for settlement data filtering data from "0xCAFECAFE19" named "termination-oracle": + | property | type | binding | decimals | + | prices.ETH.value | TYPE_INTEGER | settlement data | 0 | + + And the oracle spec for trading termination filtering data from "0xCAFECAFE19" named "termination-oracle": + | property | type | binding | + | trading.terminated | TYPE_BOOLEAN | trading termination | + + And the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | decimal places | position decimal places | + | ETH/MAR22 | USD | USD | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | default-none | termination-oracle | 1e0 | 0 | SLA-22 | 3 | -1 | + + # Setting up the accounts and vAMM submission now is part of the background, because we'll be running scenarios 0090-VAMM-006 through 0090-VAMM-014 on this setup + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lp1 | USD | 10000000 | + | lp2 | USD | 10000000 | + | lp3 | USD | 10000000 | + | party1 | USD | 10000000 | + | party2 | USD | 10000000 | + | party3 | USD | 10000000 | + | party4 | USD | 10000000 | + | party5 | USD | 10000000 | + | vamm1 | USD | 1000000000 | + | vamm2 | USD | 1000000000 | + + + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | lp1 | ETH/MAR22 | buy | 20 | 400 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + | party5 | ETH/MAR22 | buy | 20 | 900 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + | party1 | ETH/MAR22 | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | ETH/MAR22 | sell | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | | + | party3 | ETH/MAR22 | sell | 10 | 1100 | 0 | TYPE_LIMIT | TIF_GTC | | + | lp1 | ETH/MAR22 | sell | 10 | 1600 | 0 | TYPE_LIMIT | TIF_GTC | lp1-s | + When the opening auction period ends for market "ETH/MAR22" + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 1000 | 1 | party2 | + + + Then the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | + | vamm1 | ETH/MAR22 | 100000 | 0.05 | 1000 | 900 | 1100 | 0.03 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 1000 | 900 | 1100 | + + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-id | + + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | best bid price | best offer price | best bid volume | best offer volume | + | 1000 | TRADING_MODE_CONTINUOUS | 990 | 1010 | 5 | 4 | + + @VAMM + Scenario: Incoming order at AMM best price + + # AMM's has a BUY at 99 so a SELL at that price should match + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | party1 | ETH/MAR22 | sell | 1 | 990 | 1 | TYPE_LIMIT | TIF_GTC | | + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | vamm1-id | 990 | 1 | party1 | true | + + @VAMM + Scenario: Incoming order at AMM best price and orderbook volume exists at that price + + # AMM's has a BUY at 99 so a SELL at that price should match + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | party2 | ETH/MAR22 | buy | 150 | 990 | 0 | TYPE_LIMIT | TIF_GTC | | + | party1 | ETH/MAR22 | sell | 150 | 990 | 2 | TYPE_LIMIT | TIF_GTC | | + + # incoming move AMM to a fair-price of 990, then we take the orderbook volume at 990 + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | vamm1-id | 990 | 5 | party1 | true | + | party2 | 990 | 145 | party1 | true | + + + @VAMM + Scenario: Incoming order at AMM best price and orderbook volume exists at FAIR PRICE + + # AMM's has a BUY at 99 so a SELL at that price should match + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | party2 | ETH/MAR22 | buy | 146 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | | + | party1 | ETH/MAR22 | sell | 150 | 990 | 2 | TYPE_LIMIT | TIF_GTC | | + + # incoming absorbs order at fair price, then we take volume from AMM + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | party2 | 1000 | 146 | party1 | true | + | vamm1-id | 990 | 4 | party1 | true | + + + @VAMM + Scenario: Incoming order at AMM fair price and orderbook volume exists at FAIR PRICE + + # AMM's has a BUY at 99 so a SELL at 100 should not match + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | party2 | ETH/MAR22 | buy | 100 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | | + | party1 | ETH/MAR22 | sell | 150 | 1000 | 1 | TYPE_LIMIT | TIF_GTC | | + + # incoming absorbs order at fair price, then we take volume from AMM + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | party2 | 1000 | 100 | party1 | true | + + @VAMM + Scenario: Incoming order at AMM fair price and orderbook volume exists at FAIR PRICE and at AMM best price + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | party2 | ETH/MAR22 | buy | 50 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | ETH/MAR22 | buy | 50 | 990 | 0 | TYPE_LIMIT | TIF_GTC | | + | party1 | ETH/MAR22 | sell | 60 | 990 | 3 | TYPE_LIMIT | TIF_GTC | | + + # incoming absorbs order at fair price, then we take volume from AMM + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | party2 | 1000 | 50 | party1 | true | + | vamm1-id | 990 | 5 | party1 | true | + | party2 | 990 | 5 | party1 | true | + + When the network moves ahead "1" blocks diff --git a/core/integration/features/amm/0090-VAMM-rebase.feature b/core/integration/features/amm/0090-VAMM-rebase.feature index ea144a17127..5121cf5ad2a 100644 --- a/core/integration/features/amm/0090-VAMM-rebase.feature +++ b/core/integration/features/amm/0090-VAMM-rebase.feature @@ -22,11 +22,11 @@ Feature: vAMM rebasing when created or amended | market.fee.factors.makerFee | 0.004 | | spam.protection.max.stopOrdersPerMarket | 5 | | market.liquidity.equityLikeShareFeeFraction | 1 | - | market.amm.minCommitmentQuantum | 1 | + | market.amm.minCommitmentQuantum | 1 | | market.liquidity.bondPenaltyParameter | 0.2 | | market.liquidity.stakeToCcyVolume | 1 | | market.liquidity.successorLaunchWindowLength | 1h | - | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0 | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0 | | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | | validators.epoch.length | 10s | | market.liquidity.earlyExitPenalty | 0.25 | @@ -49,7 +49,7 @@ Feature: vAMM rebasing when created or amended | ETH/MAR22 | USD | USD | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | default-none | default-eth-for-future | 1e0 | 0 | SLA-22 | # Setting up the accounts and vAMM submission now is part of the background, because we'll be running scenarios 0090-VAMM-006 through 0090-VAMM-014 on this setup - Given the parties deposit on asset's general account the following amount: + And the parties deposit on asset's general account the following amount: | party | asset | amount | | lp1 | USD | 1000000 | | lp2 | USD | 1000000 | @@ -63,8 +63,8 @@ Feature: vAMM rebasing when created or amended | vamm2 | USD | 1000000 | When the parties submit the following liquidity provision: - | id | party | market id | commitment amount | fee | lp type | - | lp_1 | lp1 | ETH/MAR22 | 10000 | 0.02 | submission | + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR22 | 10000 | 0.02 | submission | And the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | reference | @@ -82,21 +82,21 @@ Feature: vAMM rebasing when created or amended And the current epoch is "1" Then the parties submit the following AMM: - | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | - | vamm1 | ETH/MAR22 | 100000 | 0.05 | 100 | 90 | 110 | 0.03 | + | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | + | vamm1 | ETH/MAR22 | 100000 | 0.05 | 100 | 90 | 110 | 0.03 | Then the AMM pool status should be: - | party | market id | amount | status | base | lower bound | upper bound | - | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 90 | 110 | + | party | market id | amount | status | base | lower bound | upper bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 90 | 110 | @VAMM Scenario: a vAMM submits a rebasing order to SELL when its base is lower than an existing AMM's (0090-VAMM-033) When the parties submit the following AMM: - | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | - | vamm2 | ETH/MAR22 | 100000 | 0.05 | 95 | 90 | 105 | 0.03 | + | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | + | vamm2 | ETH/MAR22 | 100000 | 0.05 | 95 | 90 | 105 | 0.03 | Then the AMM pool status should be: - | party | market id | amount | status | base | lower bound | upper bound | - | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 95 | 90 | 105 | + | party | market id | amount | status | base | lower bound | upper bound | + | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 95 | 90 | 105 | And set the following AMM sub account aliases: | party | market id | alias | @@ -111,7 +111,7 @@ Feature: vAMM rebasing when created or amended # and now the mid-price has shifted lower to a value between the two AMM's bases 95 < 97 < 100 And the market data for the market "ETH/MAR22" should be: - | mark price | trading mode | mid price | + | mark price | trading mode | mid price | | 98 | TRADING_MODE_CONTINUOUS | 97 | @@ -120,32 +120,32 @@ Feature: vAMM rebasing when created or amended # sell rebase order When the parties submit the following AMM: - | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | error | - | vamm2 | ETH/MAR22 | 100000 | 0.0005 | 95 | 90 | 105 | 0.03 | not enough liquidity for AMM to rebase | + | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | error | + | vamm2 | ETH/MAR22 | 100000 | 0.0005 | 95 | 90 | 105 | 0.03 | not enough liquidity for AMM to rebase | Then the AMM pool status should be: - | party | market id | amount | status | base | lower bound | upper bound | reason | - | vamm2 | ETH/MAR22 | 100000 | STATUS_REJECTED | 95 | 90 | 105 | STATUS_REASON_CANNOT_REBASE | + | party | market id | amount | status | base | lower bound | upper bound | reason | + | vamm2 | ETH/MAR22 | 100000 | STATUS_REJECTED | 95 | 90 | 105 | STATUS_REASON_CANNOT_REBASE | # buy rebase order When the parties submit the following AMM: - | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | error | - | vamm2 | ETH/MAR22 | 100000 | 0.0005 | 105 | 100 | 110 | 0.03 | not enough liquidity for AMM to rebase | + | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | error | + | vamm2 | ETH/MAR22 | 100000 | 0.0005 | 105 | 100 | 110 | 0.03 | not enough liquidity for AMM to rebase | Then the AMM pool status should be: - | party | market id | amount | status | base | lower bound | upper bound | reason | - | vamm2 | ETH/MAR22 | 100000 | STATUS_REJECTED | 105 | 100 | 110 | STATUS_REASON_CANNOT_REBASE | + | party | market id | amount | status | base | lower bound | upper bound | reason | + | vamm2 | ETH/MAR22 | 100000 | STATUS_REJECTED | 105 | 100 | 110 | STATUS_REASON_CANNOT_REBASE | @VAMM Scenario: a vAMM submits a rebasing order to BUY when its base is lower than an existing AMM's (0090-VAMM-033) When the parties submit the following AMM: - | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | - | vamm2 | ETH/MAR22 | 100000 | 0.05 | 105 | 100 | 110 | 0.03 | + | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | + | vamm2 | ETH/MAR22 | 100000 | 0.05 | 105 | 100 | 110 | 0.03 | Then the AMM pool status should be: - | party | market id | amount | status | base | lower bound | upper bound | - | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 105 | 100 | 110 | + | party | market id | amount | status | base | lower bound | upper bound | + | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 105 | 100 | 110 | And set the following AMM sub account aliases: - | party | market id | alias | + | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | | vamm2 | ETH/MAR22 | vamm2-id | @@ -169,11 +169,11 @@ Feature: vAMM rebasing when created or amended | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | | vamm2 | ETH/MAR22 | 100000 | 0.05 | 100 | 95 | 105 | 0.03 | Then the AMM pool status should be: - | party | market id | amount | status | base | lower bound | upper bound | - | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 95 | 105 | + | party | market id | amount | status | base | lower bound | upper bound | + | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 95 | 105 | And set the following AMM sub account aliases: - | party | market id | alias | + | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | | vamm2 | ETH/MAR22 | vamm2-id | @@ -186,8 +186,8 @@ Feature: vAMM rebasing when created or amended | party | market id | slippage | base | lower bound | upper bound | | vamm2 | ETH/MAR22 | 0.1 | 95 | 90 | 105 | Then the AMM pool status should be: - | party | market id | amount | status | base | lower bound | upper bound | - | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 95 | 90 | 105 | + | party | market id | amount | status | base | lower bound | upper bound | + | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 95 | 90 | 105 | # second AMM has its base 5 away from the first AMM so it must submit a rebasing-order @@ -198,7 +198,7 @@ Feature: vAMM rebasing when created or amended # and now the mid-price has shifted lower to a value between the two AMM's bases 95 < 98 < 100 And the market data for the market "ETH/MAR22" should be: - | mark price | trading mode | mid price | + | mark price | trading mode | mid price | | 98 | TRADING_MODE_CONTINUOUS | 97 | @@ -206,14 +206,14 @@ Feature: vAMM rebasing when created or amended Scenario: two aligned AMM's and one amends shifting its base lower and needs to submit a BUY rebasing order When the parties submit the following AMM: - | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | - | vamm2 | ETH/MAR22 | 100000 | 0.05 | 100 | 95 | 105 | 0.03 | + | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | + | vamm2 | ETH/MAR22 | 100000 | 0.05 | 100 | 95 | 105 | 0.03 | Then the AMM pool status should be: - | party | market id | amount | status | base | lower bound | upper bound | - | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 95 | 105 | + | party | market id | amount | status | base | lower bound | upper bound | + | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 95 | 105 | And set the following AMM sub account aliases: - | party | market id | alias | + | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | | vamm2 | ETH/MAR22 | vamm2-id | @@ -223,11 +223,11 @@ Feature: vAMM rebasing when created or amended | 100 | TRADING_MODE_CONTINUOUS | 100 | When the parties amend the following AMM: - | party | market id | slippage | base | lower bound | upper bound | - | vamm2 | ETH/MAR22 | 0.1 | 105 | 100 | 110 | + | party | market id | slippage | base | lower bound | upper bound | + | vamm2 | ETH/MAR22 | 0.1 | 105 | 100 | 110 | Then the AMM pool status should be: - | party | market id | amount | status | base | lower bound | upper bound | - | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 105 | 100 | 110 | + | party | market id | amount | status | base | lower bound | upper bound | + | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 105 | 100 | 110 | # second AMM has its base 5 away from the first AMM so it must submit a rebasing-order @@ -242,18 +242,18 @@ Feature: vAMM rebasing when created or amended | 101 | TRADING_MODE_CONTINUOUS | 103 | -@VAMM + @VAMM Scenario: One AMM exists and another on is submitted such that their ranges are disjoint and cross entirely (0090-VAMM-034) When the parties submit the following AMM: - | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | - | vamm2 | ETH/MAR22 | 100000 | 0.50 | 200 | 195 | 205 | 0.03 | + | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | + | vamm2 | ETH/MAR22 | 100000 | 0.50 | 200 | 195 | 205 | 0.03 | Then the AMM pool status should be: - | party | market id | amount | status | base | lower bound | upper bound | - | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 200 | 195 | 205 | + | party | market id | amount | status | base | lower bound | upper bound | + | vamm2 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 200 | 195 | 205 | And set the following AMM sub account aliases: - | party | market id | alias | + | party | market id | alias | | vamm1 | ETH/MAR22 | vamm1-id | | vamm2 | ETH/MAR22 | vamm2-id | @@ -268,5 +268,5 @@ Feature: vAMM rebasing when created or amended # and now the mid-price has shifted lower to a value between the two AMM's bases 100 < 104 < 105 And the market data for the market "ETH/MAR22" should be: - | mark price | trading mode | mid price | - | 102 | TRADING_MODE_CONTINUOUS | 107 | \ No newline at end of file + | mark price | trading mode | mid price | + | 102 | TRADING_MODE_CONTINUOUS | 107 | diff --git a/core/integration/features/amm/11504-panic-empty-curve.feature b/core/integration/features/amm/11504-panic-empty-curve.feature new file mode 100644 index 00000000000..1d7d6cb9a02 --- /dev/null +++ b/core/integration/features/amm/11504-panic-empty-curve.feature @@ -0,0 +1,220 @@ + Feature: Attempt to replicate a bug in vAMM + + Background: + Given the average block duration is "1" + And the margin calculator named "margin-calculator-1": + | search factor | initial factor | release factor | + | 1.2 | 1.5 | 1.7 | + And the log normal risk model named "log-normal-risk-model": + | risk aversion | tau | mu | r | sigma | + | 0.001 | 0.0011407711613050422 | 0 | 0.9 | 3.0 | + And the liquidity monitoring parameters: + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.00 | 20s | 1 | + + And the following network parameters are set: + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 0s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | + | spam.protection.max.stopOrdersPerMarket | 5 | + | market.liquidity.equityLikeShareFeeFraction | 1 | + | market.amm.minCommitmentQuantum | 1 | + | market.liquidity.bondPenaltyParameter | 0.2 | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.1 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | + | market.liquidity.maximumLiquidityFeeFactorLevel | 0.25 | + #risk factor short:3.5569036 + #risk factor long:0.801225765 + And the following assets are registered: + | id | decimal places | + | USD | 0 | + And the fees configuration named "fees-config-1": + | maker fee | infrastructure fee | + | 0.0004 | 0.001 | + + And the liquidity sla params named "SLA-22": + | price range | commitment min time fraction | performance hysteresis epochs | sla competition factor | + | 0.5 | 0.6 | 1 | 1.0 | + + And the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | + | ETH/MAR22 | USD | USD | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | default-none | default-eth-for-future | 1e0 | 0 | SLA-22 | + + # Setting up the accounts and vAMM submission now is part of the background, because we'll be running scenarios 0090-VAMM-006 through 0090-VAMM-014 on this setup + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lp1 | USD | 1000000 | + | lp2 | USD | 1000000 | + | lp3 | USD | 1000000 | + | party1 | USD | 1000000 | + | party2 | USD | 1000000 | + | party3 | USD | 1000000 | + | party4 | USD | 1000000 | + | party5 | USD | 1000000 | + | vamm1 | USD | 1000000 | + + When the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR22 | 600 | 0.02 | submission | + | lp_2 | lp2 | ETH/MAR22 | 400 | 0.015 | submission | + Then the network moves ahead "4" blocks + And the current epoch is "0" + + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | lp1 | ETH/MAR22 | buy | 20 | 40 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + | party1 | ETH/MAR22 | buy | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | ETH/MAR22 | sell | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | lp1 | ETH/MAR22 | sell | 10 | 160 | 0 | TYPE_LIMIT | TIF_GTC | lp1-s | + When the opening auction period ends for market "ETH/MAR22" + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 100 | 1 | party2 | + + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | target stake | supplied stake | open interest | ref price | mid price | static mid price | + | 100 | TRADING_MODE_CONTINUOUS | 39 | 1000 | 1 | 100 | 100 | 100 | + When the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | + | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 150 | 4 | 4 | 0.01 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 150 | 4 | 4 | + + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-id | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | + + @VAMM + Scenario: AMM holds a long position, there should be no more buy orders created, when submitting a sell order at a lower price, a panic seemingly can occur. + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | best offer price | best bid price | + | 100 | TRADING_MODE_CONTINUOUS | 100 | 100 | 101 | 99 | + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party4 | ETH/MAR22 | buy | 500 | 155 | 1 | TYPE_LIMIT | TIF_GTC | + # see the trades that make the vAMM go short + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | party4 | 122 | 291 | vamm1-id | true | + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | best offer price | best bid price | + | 100 | TRADING_MODE_CONTINUOUS | 157 | 157 | 160 | 155 | + + # trying to trade again causes no trades because the AMM has no more volume + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party4 | ETH/MAR22 | buy | 500 | 150 | 0 | TYPE_LIMIT | TIF_GTC | + + # the AMM's mid price has moved to 150, but it has no volume +150 so that best offer comes from the orderbook of 160 + Then the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | best offer price | best bid price | + | 100 | TRADING_MODE_CONTINUOUS | 157 | 157 | 160 | 155 | + + When the network moves ahead "1" blocks + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party4 | 291 | 0 | 0 | | + | vamm1-id | -291 | 0 | 0 | true | + # Notional value therefore is 317 * 122 + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | best offer price | best bid price | + | 122 | TRADING_MODE_CONTINUOUS | 157 | 157 | 160 | 155 | + + # vAMM receives fees, but loses out in the MTM settlement + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 143 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | + | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_MARGIN | ETH/MAR22 | 74548 | USD | true | TRANSFER_TYPE_MARGIN_LOW | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party5 | ETH/MAR22 | buy | 1 | 160 | 1 | TYPE_LIMIT | TIF_GTC | + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | party5 | 160 | 1 | lp1 | false | + + When the network moves ahead "1" blocks + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party4 | 291 | 11058 | 0 | | + | party5 | 1 | 0 | 0 | | + | lp1 | -1 | 0 | 0 | | + | vamm1-id | -291 | -11058 | 0 | true | + + # Now the same buy use a market order + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party5 | ETH/MAR22 | buy | 2 | 0 | 1 | TYPE_MARKET | TIF_FOK | + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | party5 | 160 | 2 | lp1 | false | + + When the network moves ahead "1" blocks + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party4 | 291 | 11058 | 0 | | + | party5 | 3 | 0 | 0 | | + | lp1 | -3 | 0 | 0 | | + | vamm1-id | -291 | -11058 | 0 | true | + + When the parties cancel the following AMM: + | party | market id | method | + | vamm1 | ETH/MAR22 | METHOD_IMMEDIATE | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 100000 | STATUS_CANCELLED | 100 | 85 | 150 | 4 | 4 | + And the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | lower leverage | proposed fee | + | vamm1 | ETH/MAR22 | 99999 | 0.1 | 100 | 85 | 4 | 0.01 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | lower leverage | + | vamm1 | ETH/MAR22 | 99999 | STATUS_ACTIVE | 100 | 85 | 4 | + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm2-id | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | vamm1 | ACCOUNT_TYPE_GENERAL | vamm2-id | ACCOUNT_TYPE_GENERAL | | 99999 | USD | true | TRANSFER_TYPE_AMM_LOW | + ## This is what we're after + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party4 | 291 | 11058 | 0 | | + | party1 | 1 | 60 | 0 | | + | party5 | 3 | 0 | 0 | | + | lp1 | -3 | 0 | 0 | | + | vamm1-id | 0 | 0 | -11058 | true | + + # Now the same but use a sell order that is outside of the range + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party1 | ETH/MAR22 | sell | 1 | 150 | 1 | TYPE_LIMIT | TIF_GTC | + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | party4 | 155 | 1 | party1 | false | + + When the network moves ahead "1" blocks + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party4 | 292 | 9603 | 0 | | + | party1 | 0 | 0 | 55 | | + | party5 | 3 | -15 | 0 | | + | lp1 | -3 | 15 | 0 | | + | vamm1-id | 0 | 0 | -11058 | true | + + When the network moves ahead "1" blocks + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party4 | ETH/MAR22 | buy | 100 | 90 | 0 | TYPE_LIMIT | TIF_GTC | + | party4 | ETH/MAR22 | sell | 100 | 100 | 0 | TYPE_LIMIT | TIF_GTC | diff --git a/core/integration/features/amm/11504-rounding.feature b/core/integration/features/amm/11504-rounding.feature new file mode 100644 index 00000000000..1237845c54c --- /dev/null +++ b/core/integration/features/amm/11504-rounding.feature @@ -0,0 +1,147 @@ +Feature: Ensure rounding errors do not cause empty curve panic. + + Background: + Given the average block duration is "1" + And the margin calculator named "margin-calculator-1": + | search factor | initial factor | release factor | + | 1.2 | 1.5 | 1.7 | + And the log normal risk model named "log-normal-risk-model": + | risk aversion | tau | mu | r | sigma | + | 0.001 | 0.0011407711613050422 | 0 | 0.9 | 3.0 | + And the liquidity monitoring parameters: + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.00 | 20s | 1 | + + And the following network parameters are set: + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 0s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | + | spam.protection.max.stopOrdersPerMarket | 5 | + | market.liquidity.equityLikeShareFeeFraction | 1 | + | market.amm.minCommitmentQuantum | 1000 | + | market.liquidity.bondPenaltyParameter | 0.2 | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.1 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | + | market.liquidity.maximumLiquidityFeeFactorLevel | 0.25 | + #risk factor short:3.5569036 + #risk factor long:0.801225765 + And the following assets are registered: + | id | decimal places | + | USD | 0 | + And the fees configuration named "fees-config-1": + | maker fee | infrastructure fee | + | 0.0004 | 0.001 | + + And the liquidity sla params named "SLA-22": + | price range | commitment min time fraction | performance hysteresis epochs | sla competition factor | + | 0.5 | 0.6 | 1 | 1.0 | + + And the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | + | ETH/MAR22 | USD | USD | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | default-none | default-eth-for-future | 1e0 | 0 | SLA-22 | + + # Setting up the accounts and vAMM submission now is part of the background, because we'll be running scenarios 0090-VAMM-006 through 0090-VAMM-014 on this setup + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lp1 | USD | 1000000 | + | lp2 | USD | 1000000 | + | lp3 | USD | 1000000 | + | party1 | USD | 1000000 | + | party2 | USD | 1000000 | + | party3 | USD | 1000000 | + | party4 | USD | 1000000 | + | party5 | USD | 1000000 | + | vamm1 | USD | 30000 | + | vamm2 | USD | 30000 | + + When the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR22 | 600 | 0.02 | submission | + | lp_2 | lp2 | ETH/MAR22 | 400 | 0.015 | submission | + Then the network moves ahead "4" blocks + And the current epoch is "0" + + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | lp1 | ETH/MAR22 | buy | 20 | 40 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + | party1 | ETH/MAR22 | buy | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | ETH/MAR22 | sell | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | lp1 | ETH/MAR22 | sell | 10 | 160 | 0 | TYPE_LIMIT | TIF_GTC | lp1-s | + When the opening auction period ends for market "ETH/MAR22" + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 100 | 1 | party2 | + + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | target stake | supplied stake | open interest | ref price | mid price | static mid price | + | 100 | TRADING_MODE_CONTINUOUS | 39 | 1000 | 1 | 100 | 100 | 100 | + When the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | + | vamm1 | ETH/MAR22 | 30000 | 0.1 | 100 | 85 | 150 | 4 | 4 | 0.01 | + | vamm2 | ETH/MAR22 | 30000 | 0.1 | 100 | 85 | 150 | 4 | 4 | 0.01 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 30000 | STATUS_ACTIVE | 100 | 85 | 150 | 4 | 4 | + | vamm2 | ETH/MAR22 | 30000 | STATUS_ACTIVE | 100 | 85 | 150 | 4 | 4 | + + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-id | + | vamm2 | ETH/MAR22 | vamm2-id | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 30000 | USD | true | TRANSFER_TYPE_AMM_LOW | + | vamm2 | ACCOUNT_TYPE_GENERAL | vamm2-id | ACCOUNT_TYPE_GENERAL | | 30000 | USD | true | TRANSFER_TYPE_AMM_LOW | + + @VAMM + Scenario: both AMMs trade with a given order, the amount traded is distributed pro-rata. + # Volume is 5, divided by 2 -> 1 party will trade 3, one will trade 2. + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party4 | ETH/MAR22 | buy | 5 | 120 | 2 | TYPE_LIMIT | TIF_GTC | + # see the trades that make the vAMM go short + + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | party4 | 100 | 3 | vamm1-id | true | + | party4 | 100 | 2 | vamm2-id | true | + + When the network moves ahead "1" blocks + Then the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | + | 100 | TRADING_MODE_CONTINUOUS | 101 | 101 | + And the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party4 | 5 | 0 | 0 | | + | vamm1-id | -3 | 0 | 0 | true | + | vamm2-id | -2 | 0 | 0 | true | + And the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 30000 | STATUS_ACTIVE | 100 | 85 | 150 | 4 | 4 | + | vamm2 | ETH/MAR22 | 30000 | STATUS_ACTIVE | 100 | 85 | 150 | 4 | 4 | + + # Now submit another order, the available volumes ought to be the other way around, the positions equal out. + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party4 | ETH/MAR22 | buy | 31 | 110 | 2 | TYPE_LIMIT | TIF_GTC | + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | party4 | 105 | 16 | vamm1-id | true | + | party4 | 104 | 15 | vamm2-id | true | + When the network moves ahead "1" blocks + Then the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | + | 104 | TRADING_MODE_CONTINUOUS | 108 | 108 | + And the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party4 | 36 | 4 | 0 | | + | vamm1-id | -19 | 4 | 0 | true | + | vamm2-id | -17 | -8 | 0 | true | diff --git a/core/integration/features/amm/amend-amm-single-side.feature b/core/integration/features/amm/amend-amm-single-side.feature new file mode 100644 index 00000000000..b92e72ab519 --- /dev/null +++ b/core/integration/features/amm/amend-amm-single-side.feature @@ -0,0 +1,285 @@ +Feature: vAMM amend single-sided commitments + + Background: + Given the average block duration is "1" + And the margin calculator named "margin-calculator-1": + | search factor | initial factor | release factor | + | 1.2 | 1.5 | 1.7 | + And the log normal risk model named "log-normal-risk-model": + | risk aversion | tau | mu | r | sigma | + | 0.001 | 0.0011407711613050422 | 0 | 0.9 | 3.0 | + And the liquidity monitoring parameters: + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.00 | 20s | 1 | + + And the following network parameters are set: + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 0s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | + | spam.protection.max.stopOrdersPerMarket | 5 | + | market.liquidity.equityLikeShareFeeFraction | 1 | + | market.amm.minCommitmentQuantum | 1 | + | market.liquidity.bondPenaltyParameter | 0.2 | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | + | market.liquidity.maximumLiquidityFeeFactorLevel | 0.25 | + #risk factor short:3.5569036 + #risk factor long:0.801225765 + And the following assets are registered: + | id | decimal places | + | USD | 0 | + And the fees configuration named "fees-config-1": + | maker fee | infrastructure fee | + | 0.0004 | 0.001 | + + And the liquidity sla params named "SLA-22": + | price range | commitment min time fraction | performance hysteresis epochs | sla competition factor | + | 0.5 | 0.6 | 1 | 1.0 | + + And the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | + | ETH/MAR22 | USD | USD | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | default-none | default-eth-for-future | 1e0 | 0 | SLA-22 | + | ETH/MAR23 | USD | USD | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | default-none | default-eth-for-future | 1e0 | 0 | SLA-22 | + + # Setting up the accounts and vAMM submission now is part of the background, because we'll be running scenarios 0090-VAMM-006 through 0090-VAMM-014 on this setup + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lp1 | USD | 1000000 | + | lp2 | USD | 1000000 | + | lp3 | USD | 1000000 | + | party1 | USD | 1000000 | + | party2 | USD | 1000000 | + | party3 | USD | 1000000 | + | party4 | USD | 1000000 | + | party5 | USD | 1000000 | + | vamm1 | USD | 1000000 | + | vamm2 | USD | 1000000 | + + When the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR22 | 10000 | 0.02 | submission | + | lp_2 | lp2 | ETH/MAR23 | 10000 | 0.02 | submission | + + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | lp1 | ETH/MAR22 | buy | 20 | 40 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + | lp2 | ETH/MAR23 | buy | 20 | 40 | 0 | TYPE_LIMIT | TIF_GTC | lp2-b | + | party1 | ETH/MAR22 | buy | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | party3 | ETH/MAR23 | buy | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | ETH/MAR22 | sell | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | party4 | ETH/MAR23 | sell | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | lp1 | ETH/MAR22 | sell | 10 | 160 | 0 | TYPE_LIMIT | TIF_GTC | lp1-s | + | lp2 | ETH/MAR23 | sell | 10 | 160 | 0 | TYPE_LIMIT | TIF_GTC | lp2-s | + + # End opening auction for both markets. + When the network moves ahead "3" blocks + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 100 | 1 | party2 | + | party3 | 100 | 1 | party4 | + + Then the network moves ahead "1" epochs + And the current epoch is "1" + + Then the parties submit the following AMM: + | party | market id | amount | slippage | base | upper bound | proposed fee | + | vamm1 | ETH/MAR22 | 100000 | 0.05 | 100 | 110 | 0.03 | + And the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | proposed fee | + | vamm2 | ETH/MAR23 | 100000 | 0.05 | 100 | 90 | 110 | 0.03 | + Then the AMM pool status should be: + | party | market id | amount | status | base | upper bound | lower bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 110 | | + | vamm2 | ETH/MAR23 | 100000 | STATUS_ACTIVE | 100 | 110 | 90 | + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-id | + | vamm2 | ETH/MAR23 | vamm2-id | + + + @VAMM + Scenario: vAMM is amended from BUY to SELL side + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR22 | buy | 1 | 99 | 0 | TYPE_LIMIT | TIF_GTC | + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 99 | 1 | + | sell | 160 | 10 | + And the parties amend the following AMM: + | party | market id | slippage | base | lower bound | + | vamm1 | ETH/MAR22 | 0.05 | 100 | 90 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 90 | | + # Make sure nothing changes when we move to the next block + When the network moves ahead "1" blocks + And the parties amend the following AMM: + | party | market id | slippage | base | lower bound | + | vamm1 | ETH/MAR22 | 0.05 | 100 | 90 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 90 | | + And the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 99 | 1 | + | sell | 160 | 10 | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR22 | sell | 1 | 101 | 0 | TYPE_LIMIT | TIF_GTC | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 90 | | + And the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 99 | 1 | + | sell | 101 | 1 | + | sell | 160 | 10 | + When the network moves ahead "1" blocks + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 90 | | + And the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 99 | 1 | + | sell | 101 | 1 | + | sell | 160 | 10 | + + @VAMM + Scenario: vAMM is amended from having a lower and upper bound to just lower or upper bound. The vAMM does not hold any positions. + When the parties amend the following AMM: + | party | market id | slippage | base | upper bound | + | vamm2 | ETH/MAR23 | 0.05 | 100 | 110 | + # No trades because the sell vAMM places no sell orders. + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR23 | buy | 1 | 99 | 0 | TYPE_LIMIT | TIF_GTC | + And the AMM pool status should be: + | party | market id | amount | status | base | upper bound | lower bound | + | vamm2 | ETH/MAR23 | 100000 | STATUS_ACTIVE | 100 | 110 | | + + When the network moves ahead "1" blocks + And the parties amend the following AMM: + | party | market id | slippage | base | lower bound | + | vamm2 | ETH/MAR23 | 0.05 | 100 | 90 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | + | vamm2 | ETH/MAR23 | 100000 | STATUS_ACTIVE | 100 | 90 | | + And the order book should have the following volumes for market "ETH/MAR23": + | side | price | volume | + | buy | 40 | 20 | + | buy | 99 | 1 | + | sell | 160 | 10 | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party4 | ETH/MAR23 | sell | 1 | 101 | 0 | TYPE_LIMIT | TIF_GTC | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | + | vamm2 | ETH/MAR23 | 100000 | STATUS_ACTIVE | 100 | 90 | | + And the order book should have the following volumes for market "ETH/MAR23": + | side | price | volume | + | buy | 40 | 20 | + | buy | 99 | 1 | + | sell | 101 | 1 | + | sell | 160 | 10 | + When the network moves ahead "1" blocks + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | + | vamm2 | ETH/MAR23 | 100000 | STATUS_ACTIVE | 100 | 90 | | + And the order book should have the following volumes for market "ETH/MAR23": + | side | price | volume | + | buy | 40 | 20 | + | buy | 99 | 1 | + | sell | 101 | 1 | + | sell | 160 | 10 | + + @VAMM + Scenario: vAMM can't amended from having a lower and upper bound to just lower or upper bound when the vAMM holds a long position. + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party3 | ETH/MAR23 | buy | 1 | 101 | 1 | TYPE_LIMIT | TIF_GTC | + When the parties amend the following AMM: + | party | market id | slippage | base | upper bound | + | vamm2 | ETH/MAR23 | 0.05 | 100 | 110 | + And the AMM pool status should be: + | party | market id | amount | status | base | upper bound | lower bound | + | vamm2 | ETH/MAR23 | 100000 | STATUS_ACTIVE | 100 | 110 | | + + When the network moves ahead "1" blocks + Then the parties amend the following AMM: + | party | market id | slippage | base | lower bound | error | + | vamm2 | ETH/MAR23 | 0.05 | 100 | 90 | cannot remove upper bound when AMM is short | + And the AMM pool status should be: + | party | market id | amount | status | base | upper bound | lower bound | + | vamm2 | ETH/MAR23 | 100000 | STATUS_ACTIVE | 100 | 110 | | + + @VAMM + Scenario: vAMM can't amended from having a lower and upper bound to just lower or upper bound when the vAMM holds a short position. + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party4 | ETH/MAR23 | sell | 1 | 99 | 1 | TYPE_LIMIT | TIF_GTC | + Then the parties amend the following AMM: + | party | market id | slippage | base | lower bound | + | vamm2 | ETH/MAR23 | 0.05 | 100 | 90 | + And the AMM pool status should be: + | party | market id | amount | status | base | upper bound | lower bound | + | vamm2 | ETH/MAR23 | 100000 | STATUS_ACTIVE | 100 | | 90 | + + When the network moves ahead "1" blocks + Then the parties amend the following AMM: + | party | market id | slippage | base | upper bound | error | + | vamm2 | ETH/MAR23 | 0.05 | 100 | 110 | cannot remove lower bound when AMM is long | + And the AMM pool status should be: + | party | market id | amount | status | base | upper bound | lower bound | + | vamm2 | ETH/MAR23 | 100000 | STATUS_ACTIVE | 100 | | 90 | + + @VAMM + Scenario: vAMM can't amended from having a single bound to the other side, when the vAMM holds a long position. + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party1 | ETH/MAR22 | buy | 1 | 101 | 1 | TYPE_LIMIT | TIF_GTC | + And the AMM pool status should be: + | party | market id | amount | status | base | upper bound | lower bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 110 | | + + When the network moves ahead "1" blocks + Then the parties amend the following AMM: + | party | market id | slippage | base | lower bound | error | + | vamm1 | ETH/MAR22 | 0.05 | 100 | 90 | cannot remove upper bound when AMM is short | + And the AMM pool status should be: + | party | market id | amount | status | base | upper bound | lower bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 110 | | + + @VAMM + Scenario: vAMM can't amended from having a single bound to the other side, when the vAMM holds a short position. + When the parties amend the following AMM: + | party | market id | slippage | base | lower bound | + | vamm1 | ETH/MAR22 | 0.05 | 100 | 90 | + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party2 | ETH/MAR22 | sell | 1 | 99 | 1 | TYPE_LIMIT | TIF_GTC | + And the AMM pool status should be: + | party | market id | amount | status | base | upper bound | lower bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | | 90 | + + When the network moves ahead "1" blocks + Then the parties amend the following AMM: + | party | market id | slippage | base | upper bound | error | + | vamm1 | ETH/MAR22 | 0.05 | 100 | 110 | cannot remove lower bound when AMM is long | + And the AMM pool status should be: + | party | market id | amount | status | base | upper bound | lower bound | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | | 90 | diff --git a/core/integration/features/amm/pegged-orders-market-tick.feature b/core/integration/features/amm/pegged-orders-market-tick.feature new file mode 100644 index 00000000000..450c6e7950a --- /dev/null +++ b/core/integration/features/amm/pegged-orders-market-tick.feature @@ -0,0 +1,154 @@ +Feature: 0090-VAMM-036: With an existing book consisting solely of vAMM orders, pegged orders referencing best bid/best ask remain deployed, pegged to their pegs, where the best buy/sell vAMM order price acts as the best bid, or best ask peg respectively. + + Background: + Given the average block duration is "1" + And the margin calculator named "margin-calculator-1": + | search factor | initial factor | release factor | + | 1.2 | 1.5 | 1.7 | + And the log normal risk model named "log-normal-risk-model": + | risk aversion | tau | mu | r | sigma | + | 0.001 | 0.0011407711613050422 | 0 | 0.9 | 3.0 | + And the liquidity monitoring parameters: + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.00 | 20s | 1 | + + And the following network parameters are set: + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 0s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | + | spam.protection.max.stopOrdersPerMarket | 5 | + | market.liquidity.equityLikeShareFeeFraction | 1 | + | market.amm.minCommitmentQuantum | 1 | + | market.liquidity.bondPenaltyParameter | 0.2 | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.1 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | + | market.liquidity.maximumLiquidityFeeFactorLevel | 0.25 | + #risk factor short:3.5569036 + #risk factor long:0.801225765 + And the following assets are registered: + | id | decimal places | + | USD | 2 | + And the fees configuration named "fees-config-1": + | maker fee | infrastructure fee | + | 0.0004 | 0.001 | + + And the liquidity sla params named "SLA-22": + | price range | commitment min time fraction | performance hysteresis epochs | sla competition factor | + | 0.5 | 0.6 | 1 | 1.0 | + + And the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | decimal places | + | ETH/MAR22 | USD | USD | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | default-none | default-eth-for-future | 1e0 | 0 | SLA-22 | 1 | + + # Setting up the accounts and vAMM submission now is part of the background, because we'll be running scenarios 0090-VAMM-006 through 0090-VAMM-014 on this setup + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lp1 | USD | 1000000 | + | lp2 | USD | 1000000 | + | lp3 | USD | 1000000 | + | party1 | USD | 1000000 | + | party2 | USD | 1000000 | + | party3 | USD | 1000000 | + | party4 | USD | 1000000 | + | party5 | USD | 1000000 | + | vamm1 | USD | 1000000 | + + When the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR22 | 600 | 0.02 | submission | + | lp_2 | lp2 | ETH/MAR22 | 400 | 0.015 | submission | + Then the network moves ahead "4" blocks + And the current epoch is "0" + + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | lp1 | ETH/MAR22 | buy | 20 | 40 | 0 | TYPE_LIMIT | TIF_GTC | LP1BO | + | party1 | ETH/MAR22 | buy | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | ETH/MAR22 | sell | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | lp1 | ETH/MAR22 | sell | 10 | 160 | 0 | TYPE_LIMIT | TIF_GTC | LP1SO | + When the opening auction period ends for market "ETH/MAR22" + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 100 | 1 | party2 | + + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | target stake | supplied stake | open interest | ref price | mid price | static mid price | + | 100 | TRADING_MODE_CONTINUOUS | 399 | 1000 | 1 | 100 | 100 | 100 | + When the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | + | vamm1 | ETH/MAR22 | 1000000 | 0.01 | 100 | 85 | 150 | 4 | 4 | 0.01 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 1000000 | STATUS_ACTIVE | 100 | 85 | 150 | 4 | 4 | + + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-id | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 1000000 | USD | true | TRANSFER_TYPE_AMM_LOW | + + @VAMM + Scenario: Simply submit pegged orders, cancel all orders on the orderbook, the pegged orders should be pegged to the AMM orders, AMM orders may not stick to market ticks. + When the parties place the following pegged iceberg orders: + | party | market id | side | volume | peak size | minimum visible size | pegged reference | offset | + | lp1 | ETH/MAR22 | buy | 100 | 10 | 2 | BID | 5 | + | lp1 | ETH/MAR22 | sell | 100 | 10 | 2 | ASK | 5 | + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 94 | 10 | + | sell | 106 | 10 | + | sell | 160 | 10 | + + # ensure moving the network by 1 block doesn't change a thing + When the network moves ahead "1" blocks + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 94 | 10 | + | sell | 106 | 10 | + | sell | 160 | 10 | + + # Now cancel the LP1BO and LP1SO orders, the pegged orders should remain where they are. + When the parties cancel the following orders: + | party | reference | + | lp1 | LP1BO | + | lp1 | LP1SO | + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 0 | + | buy | 94 | 10 | + | sell | 106 | 10 | + | sell | 160 | 0 | + + # We pass through block end CheckBook call without problems, and the book volumes still check out. + When the network moves ahead "1" blocks + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 0 | + | buy | 94 | 10 | + | sell | 106 | 10 | + | sell | 160 | 0 | + + # Now trade, but not necessarily at market tick-compatible price, let's see what happens to the pegged orders. Based on the book, we know there's a buy order at 99, and a sell order at 101 + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | party2 | ETH/MAR22 | sell | 5 | 99 | 1 | TYPE_LIMIT | TIF_GTC | | + And the network moves ahead "1" blocks + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 0 | + | buy | 93 | 10 | + | sell | 99 | 0 | + | sell | 105 | 10 | + | sell | 160 | 0 | + diff --git a/core/integration/features/amm/pegged-orders.feature b/core/integration/features/amm/pegged-orders.feature new file mode 100644 index 00000000000..61ef9296349 --- /dev/null +++ b/core/integration/features/amm/pegged-orders.feature @@ -0,0 +1,140 @@ +Feature: 0090-VAMM-036: With an existing book consisting solely of vAMM orders, pegged orders referencing best bid/best ask remain deployed, pegged to their pegs, where the best buy/sell vAMM order price acts as the best bid, or best ask peg respectively. + + Background: + Given the average block duration is "1" + And the margin calculator named "margin-calculator-1": + | search factor | initial factor | release factor | + | 1.2 | 1.5 | 1.7 | + And the log normal risk model named "log-normal-risk-model": + | risk aversion | tau | mu | r | sigma | + | 0.001 | 0.0011407711613050422 | 0 | 0.9 | 3.0 | + And the liquidity monitoring parameters: + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.00 | 20s | 1 | + + And the following network parameters are set: + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 0s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | + | spam.protection.max.stopOrdersPerMarket | 5 | + | market.liquidity.equityLikeShareFeeFraction | 1 | + | market.amm.minCommitmentQuantum | 1 | + | market.liquidity.bondPenaltyParameter | 0.2 | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.1 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | + | market.liquidity.maximumLiquidityFeeFactorLevel | 0.25 | + #risk factor short:3.5569036 + #risk factor long:0.801225765 + And the following assets are registered: + | id | decimal places | + | USD | 0 | + And the fees configuration named "fees-config-1": + | maker fee | infrastructure fee | + | 0.0004 | 0.001 | + + And the liquidity sla params named "SLA-22": + | price range | commitment min time fraction | performance hysteresis epochs | sla competition factor | + | 0.5 | 0.6 | 1 | 1.0 | + + And the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | + | ETH/MAR22 | USD | USD | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | default-none | default-eth-for-future | 1e0 | 0 | SLA-22 | + + # Setting up the accounts and vAMM submission now is part of the background, because we'll be running scenarios 0090-VAMM-006 through 0090-VAMM-014 on this setup + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lp1 | USD | 1000000 | + | lp2 | USD | 1000000 | + | lp3 | USD | 1000000 | + | party1 | USD | 1000000 | + | party2 | USD | 1000000 | + | party3 | USD | 1000000 | + | party4 | USD | 1000000 | + | party5 | USD | 1000000 | + | vamm1 | USD | 1000000 | + + When the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR22 | 600 | 0.02 | submission | + | lp_2 | lp2 | ETH/MAR22 | 400 | 0.015 | submission | + Then the network moves ahead "4" blocks + And the current epoch is "0" + + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | lp1 | ETH/MAR22 | buy | 20 | 40 | 0 | TYPE_LIMIT | TIF_GTC | LP1BO | + | party1 | ETH/MAR22 | buy | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | ETH/MAR22 | sell | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | lp1 | ETH/MAR22 | sell | 10 | 160 | 0 | TYPE_LIMIT | TIF_GTC | LP1SO | + When the opening auction period ends for market "ETH/MAR22" + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 100 | 1 | party2 | + + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | target stake | supplied stake | open interest | ref price | mid price | static mid price | + | 100 | TRADING_MODE_CONTINUOUS | 39 | 1000 | 1 | 100 | 100 | 100 | + When the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | + | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 150 | 4 | 4 | 0.01 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 150 | 4 | 4 | + + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-id | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | + + @VAMM + Scenario: Simply submit pegged orders, cancel all orders on the orderbook, the pegged orders should be pegged to the AMM orders. + When the parties place the following pegged iceberg orders: + | party | market id | side | volume | peak size | minimum visible size | pegged reference | offset | + | lp1 | ETH/MAR22 | buy | 100 | 10 | 2 | BID | 5 | + | lp1 | ETH/MAR22 | sell | 100 | 10 | 2 | ASK | 5 | + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 94 | 10 | + | sell | 106 | 10 | + | sell | 160 | 10 | + + # ensure moving the network by 1 block doesn't change a thing + When the network moves ahead "1" blocks + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 20 | + | buy | 94 | 10 | + | sell | 106 | 10 | + | sell | 160 | 10 | + + # Now cancel the LP1BO and LP1SO orders, the pegged orders should remain where they are. + When the parties cancel the following orders: + | party | reference | + | lp1 | LP1BO | + | lp1 | LP1SO | + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 0 | + | buy | 94 | 10 | + | sell | 106 | 10 | + | sell | 160 | 0 | + + # We pass through block end CheckBook call without problems, and the book volumes still check out. + When the network moves ahead "1" blocks + Then the order book should have the following volumes for market "ETH/MAR22": + | side | price | volume | + | buy | 40 | 0 | + | buy | 94 | 10 | + | sell | 106 | 10 | + | sell | 160 | 0 | diff --git a/core/integration/features/amm/reduce-only-single-sided.feature b/core/integration/features/amm/reduce-only-single-sided.feature new file mode 100644 index 00000000000..6f7fafcd5cc --- /dev/null +++ b/core/integration/features/amm/reduce-only-single-sided.feature @@ -0,0 +1,258 @@ +Feature: Cover more complex scenarios. + + Background: + Given the average block duration is "1" + And the margin calculator named "margin-calculator-1": + | search factor | initial factor | release factor | + | 1.2 | 1.5 | 1.7 | + And the log normal risk model named "log-normal-risk-model": + | risk aversion | tau | mu | r | sigma | + | 0.001 | 0.0011407711613050422 | 0 | 0.9 | 3.0 | + And the liquidity monitoring parameters: + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.00 | 20s | 1 | + + And the following network parameters are set: + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 2s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | + | spam.protection.max.stopOrdersPerMarket | 5 | + | market.liquidity.equityLikeShareFeeFraction | 1 | + | market.amm.minCommitmentQuantum | 1 | + | market.liquidity.bondPenaltyParameter | 0.2 | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.1 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | + | market.liquidity.maximumLiquidityFeeFactorLevel | 0.25 | + #risk factor short:3.5569036 + #risk factor long:0.801225765 + And the following assets are registered: + | id | decimal places | + | USD | 0 | + And the fees configuration named "fees-config-1": + | maker fee | infrastructure fee | + | 0.0004 | 0.001 | + + And the liquidity sla params named "SLA-22": + | price range | commitment min time fraction | performance hysteresis epochs | sla competition factor | + | 0.5 | 0.6 | 1 | 1.0 | + + And the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | + | ETH/MAR22 | USD | USD | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | default-none | default-eth-for-future | 1e0 | 0 | SLA-22 | + + # Setting up the accounts and vAMM submission now is part of the background, because we'll be running scenarios 0090-VAMM-006 through 0090-VAMM-014 on this setup + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lp1 | USD | 1000000 | + | lp2 | USD | 1000000 | + | lp3 | USD | 1000000 | + | party1 | USD | 1000000 | + | party2 | USD | 1000000 | + | party3 | USD | 1000000 | + | party4 | USD | 1000000 | + | party5 | USD | 1000000 | + | vamm1 | USD | 1000000 | + + When the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR22 | 600 | 0.02 | submission | + | lp_2 | lp2 | ETH/MAR22 | 400 | 0.015 | submission | + Then the network moves ahead "4" blocks + And the current epoch is "0" + + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | lp1 | ETH/MAR22 | buy | 20 | 40 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + | party1 | ETH/MAR22 | buy | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | ETH/MAR22 | sell | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | lp1 | ETH/MAR22 | sell | 10 | 160 | 0 | TYPE_LIMIT | TIF_GTC | lp1-s | + When the opening auction period ends for market "ETH/MAR22" + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 100 | 1 | party2 | + + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | target stake | supplied stake | open interest | ref price | mid price | static mid price | + | 100 | TRADING_MODE_CONTINUOUS | 39 | 1000 | 1 | 100 | 100 | 100 | + When the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | + | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 150 | 4 | 4 | 0.01 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 150 | 4 | 4 | + + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-id | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | + + + @VAMM + Scenario: 0090-VAMM-020: If a vAMM is cancelled and set in Reduce-Only mode when it is currently long, then It creates no further buy orders even if the current price is above the configured lower price. When one of it's sell orders is executed it still does not produce buy orders, and correctly quotes sell orders from a higher price. When the position reaches 0 the vAMM is closed and all funds are released to the user after the next mark to market. + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party4 | ETH/MAR22 | sell | 350 | 90 | 1 | TYPE_LIMIT | TIF_GTC | + # see the trades that make the vAMM go long + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | vamm1-id | 95 | 350 | party4 | true | + # move ahead until MTM settlement occurs + And the network moves ahead "3" blocks + Then the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | + | 95 | TRADING_MODE_CONTINUOUS | 90 | 90 | + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party4 | -350 | 0 | 0 | | + | vamm1-id | 350 | 0 | 0 | true | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 133 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | + | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_MARGIN | ETH/MAR22 | 64462 | USD | true | TRANSFER_TYPE_MARGIN_LOW | + And the parties should have the following account balances: + | party | asset | market id | general | margin | is amm | + | vamm1 | USD | | 900000 | | | + | vamm1-id | USD | ETH/MAR22 | 35671 | 64462 | true | + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | best offer price | best bid price | + | 95 | TRADING_MODE_CONTINUOUS | 90 | 90 | 91 | 89 | + + # Next: cancel the vAMM with reduce-only + When the parties cancel the following AMM: + | party | market id | method | + | vamm1 | ETH/MAR22 | METHOD_REDUCE_ONLY | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 100000 | STATUS_REDUCE_ONLY | 100 | 85 | 150 | 4 | 4 | + # Check if the vAMM doesn't place any more buy orders: submit sell orders at previous best bid, ask, and mid prices: + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party4 | ETH/MAR22 | sell | 10 | 89 | 0 | TYPE_LIMIT | TIF_GTC | + | party4 | ETH/MAR22 | sell | 10 | 90 | 0 | TYPE_LIMIT | TIF_GTC | + | party4 | ETH/MAR22 | sell | 10 | 91 | 0 | TYPE_LIMIT | TIF_GTC | + Then the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | best offer price | best bid price | + | 95 | TRADING_MODE_CONTINUOUS | 64 | 64 | 89 | 40 | + + # Now start checking if the vAMM still quotes sell orders + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party5 | ETH/MAR22 | buy | 280 | 110 | 5 | TYPE_LIMIT | TIF_GTC | + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | party5 | 89 | 10 | party4 | | + | party5 | 90 | 10 | party4 | | + | party5 | 90 | 19 | vamm1-id | true | + | party5 | 91 | 10 | party4 | | + | party5 | 90 | 19 | vamm1-id | true | + | party5 | 94 | 231 | vamm1-id | true | + + # check the state of the market, trigger MTM settlement and check balances before closing out the last 100 for the vAMM + When the network moves ahead "2" blocks + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party4 | -380 | 230 | 0 | | + | party5 | 280 | 196 | 0 | | + | vamm1-id | 100 | -100 | -326 | true | + # vAMM is still quoting bid price, though it is in reduce-only mode, and therefore doesn't place those orders. + # The best bid should be 40 here? + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | best offer price | best bid price | + | 94 | TRADING_MODE_CONTINUOUS | 69 | 69 | 98 | 40 | + # vAMM receives some fees, but pays MTM loss, excess margin is released + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 87 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | + | vamm1-id | ACCOUNT_TYPE_MARGIN | | ACCOUNT_TYPE_SETTLEMENT | ETH/MAR22 | 426 | USD | true | TRANSFER_TYPE_MTM_LOSS | + | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 45811 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | + # After receiving fees, and excess margin is correctly released, the balances of the vAMM sub-accounts match the position: + And the parties should have the following account balances: + | party | asset | market id | general | margin | is amm | + | vamm1 | USD | | 900000 | | | + | vamm1-id | USD | ETH/MAR22 | 81576 | 18225 | true | + + # Now make sure the vAMM, though clearly having sufficient balance to increase its position, still doesn't place any buy orders (reduce only check 2) + # Like before, place orders at mid, offer, and bid prices + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | party4 | ETH/MAR22 | sell | 10 | 96 | 0 | TYPE_LIMIT | TIF_GTC | p4-c1 | + | party4 | ETH/MAR22 | sell | 10 | 97 | 0 | TYPE_LIMIT | TIF_GTC | p4-c2 | + | party4 | ETH/MAR22 | sell | 10 | 98 | 0 | TYPE_LIMIT | TIF_GTC | p4-c3 | + # we've confirmed the vAMM does not reduce its position at all, so cancel these orders to keep things simple + Then the parties cancel the following orders: + | party | reference | + | party4 | p4-c1 | + | party4 | p4-c2 | + | party4 | p4-c3 | + # party5 places a buy order large enough to trade with party4 and reduce the vAMM position down to 0, and no more. + # we'll do a v2 of this test where this buy order is "over-sized" to ensure the vAMM doesn't flip from long to short. + And the parties place the following orders with ticks: + | party | market id | side | volume | price | resulting trades | type | tif | + | party5 | ETH/MAR22 | buy | 100 | 110 | 1 | TYPE_LIMIT | TIF_GTC | + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | party5 | 98 | 100 | vamm1-id | true | + # Confirm the vAMM is no longer quoting anything + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | best offer price | best bid price | + | 94 | TRADING_MODE_CONTINUOUS | 100 | 100 | 160 | 40 | + And the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 100000 | STATUS_REDUCE_ONLY | 100 | 85 | 150 | 4 | 4 | + And the parties place the following orders with ticks: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | party5 | ETH/MAR22 | buy | 1 | 110 | 0 | TYPE_LIMIT | TIF_GTC | p5-check-ref | + + # Move 1 block, the status still shows reduce only, although the position is closed (so it should be cancelled) + # apparently we need to MTM before setting the AMM to cancelled. What happens if we submit more orders, though? + When the network moves ahead "1" blocks + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 100000 | STATUS_REDUCE_ONLY | 100 | 85 | 150 | 4 | 4 | + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | best offer price | best bid price | + | 94 | TRADING_MODE_CONTINUOUS | 135 | 135 | 160 | 110 | + + When the parties cancel the following orders: + | party | reference | + | party5 | p5-check-ref | + Then the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | best offer price | best bid price | + | 94 | TRADING_MODE_CONTINUOUS | 100 | 100 | 160 | 40 | + + # Now move ahead 1 more block, we should now perform the MTM settlement + # Check the final PnL for the vAMM, check the transfers and balances + When the network moves ahead "1" blocks + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party4 | -380 | -1290 | 0 | | + | party5 | 380 | 1316 | 0 | | + | vamm1-id | 0 | 0 | -26 | true | + And the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 100000 | STATUS_CANCELLED | 100 | 85 | 150 | 4 | 4 | + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | mid price | static mid price | best offer price | best bid price | + | 98 | TRADING_MODE_CONTINUOUS | 100 | 100 | 160 | 40 | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | | ACCOUNT_TYPE_FEES_MAKER | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 40 | USD | true | TRANSFER_TYPE_MAKER_FEE_RECEIVE | + | | ACCOUNT_TYPE_SETTLEMENT | vamm1-id | ACCOUNT_TYPE_MARGIN | ETH/MAR22 | 400 | USD | true | TRANSFER_TYPE_MTM_WIN | + | vamm1-id | ACCOUNT_TYPE_MARGIN | vamm1-id | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 18625 | USD | true | TRANSFER_TYPE_MARGIN_HIGH | + | vamm1-id | ACCOUNT_TYPE_GENERAL | vamm1 | ACCOUNT_TYPE_GENERAL | ETH/MAR22 | 100241 | USD | true | TRANSFER_TYPE_AMM_RELEASE | + And the parties should have the following account balances: + | party | asset | market id | general | margin | is amm | + | vamm1 | USD | | 1000241 | | | + | vamm1-id | USD | ETH/MAR22 | 0 | 0 | true | + + diff --git a/core/integration/features/amm/vamm-wash-trade.feature b/core/integration/features/amm/vamm-wash-trade.feature new file mode 100644 index 00000000000..75f4a60fd70 --- /dev/null +++ b/core/integration/features/amm/vamm-wash-trade.feature @@ -0,0 +1,227 @@ +Feature: Derived key trades with its primary key. + Background: + Given the average block duration is "1" + And the margin calculator named "margin-calculator-1": + | search factor | initial factor | release factor | + | 1.2 | 1.5 | 1.7 | + And the log normal risk model named "log-normal-risk-model": + | risk aversion | tau | mu | r | sigma | + | 0.001 | 0.0011407711613050422 | 0 | 0.9 | 3.0 | + And the liquidity monitoring parameters: + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.00 | 20s | 1 | + + And the following network parameters are set: + | name | value | + | market.value.windowLength | 60s | + | network.markPriceUpdateMaximumFrequency | 0s | + | limits.markets.maxPeggedOrders | 6 | + | market.auction.minimumDuration | 1 | + | market.fee.factors.infrastructureFee | 0.001 | + | market.fee.factors.makerFee | 0.004 | + | spam.protection.max.stopOrdersPerMarket | 5 | + | market.liquidity.equityLikeShareFeeFraction | 1 | + | market.amm.minCommitmentQuantum | 1 | + | market.liquidity.bondPenaltyParameter | 0.2 | + | market.liquidity.stakeToCcyVolume | 1 | + | market.liquidity.successorLaunchWindowLength | 1h | + | market.liquidity.sla.nonPerformanceBondPenaltySlope | 0.1 | + | market.liquidity.sla.nonPerformanceBondPenaltyMax | 0.6 | + | validators.epoch.length | 10s | + | market.liquidity.earlyExitPenalty | 0.25 | + | market.liquidity.maximumLiquidityFeeFactorLevel | 0.25 | + #risk factor short:3.5569036 + #risk factor long:0.801225765 + And the following assets are registered: + | id | decimal places | + | USD | 0 | + And the fees configuration named "fees-config-1": + | maker fee | infrastructure fee | + | 0.0004 | 0.001 | + + And the liquidity sla params named "SLA-22": + | price range | commitment min time fraction | performance hysteresis epochs | sla competition factor | + | 0.5 | 0.6 | 1 | 1.0 | + + # Create 2 identical markets, one will be used to test moving the mid price in steps of one, the other will do the same in a single trade. + And the markets: + | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | + | ETH/MAR22 | USD | USD | lqm-params | log-normal-risk-model | margin-calculator-1 | 2 | fees-config-1 | default-none | default-eth-for-future | 1e0 | 0 | SLA-22 | + + # Setting up the accounts and vAMM submission now is part of the background, because we'll be running scenarios 0090-VAMM-006 through 0090-VAMM-014 on this setup + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lp1 | USD | 1000000 | + | lp2 | USD | 1000000 | + | lp3 | USD | 1000000 | + | lp4 | USD | 1000000 | + | party1 | USD | 1000000 | + | party2 | USD | 1000000 | + | party3 | USD | 1000000 | + | party4 | USD | 1000000 | + | party5 | USD | 1000000 | + | party6 | USD | 1000000 | + | vamm1 | USD | 1000000 | + | vamm2 | USD | 1000000 | + + When the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp_1 | lp1 | ETH/MAR22 | 600 | 0.02 | submission | + | lp_2 | lp2 | ETH/MAR22 | 400 | 0.015 | submission | + Then the network moves ahead "4" blocks + And the current epoch is "0" + + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | lp1 | ETH/MAR22 | buy | 20 | 40 | 0 | TYPE_LIMIT | TIF_GTC | lp1-b | + | party1 | ETH/MAR22 | buy | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | party2 | ETH/MAR22 | sell | 1 | 100 | 0 | TYPE_LIMIT | TIF_GTC | | + | lp1 | ETH/MAR22 | sell | 10 | 160 | 0 | TYPE_LIMIT | TIF_GTC | lp1-s | + When the opening auction period ends for market "ETH/MAR22" + Then the following trades should be executed: + | buyer | price | size | seller | + | party1 | 100 | 1 | party2 | + And the market data for the market "ETH/MAR22" should be: + | mark price | trading mode | target stake | supplied stake | open interest | ref price | mid price | static mid price | + | 100 | TRADING_MODE_CONTINUOUS | 39 | 1000 | 1 | 100 | 100 | 100 | + + When the parties submit the following AMM: + | party | market id | amount | slippage | base | lower bound | upper bound | lower leverage | upper leverage | proposed fee | + | vamm1 | ETH/MAR22 | 100000 | 0.1 | 100 | 85 | 150 | 4 | 4 | 0.01 | + Then the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 100000 | STATUS_ACTIVE | 100 | 85 | 150 | 4 | 4 | + + And set the following AMM sub account aliases: + | party | market id | alias | + | vamm1 | ETH/MAR22 | vamm1-id | + And the following transfers should happen: + | from | from account | to | to account | market id | amount | asset | is amm | type | + | vamm1 | ACCOUNT_TYPE_GENERAL | vamm1-id | ACCOUNT_TYPE_GENERAL | | 100000 | USD | true | TRANSFER_TYPE_AMM_LOW | + + @VAMM + Scenario: Simply have the vamm1 submit an order to the book that uncrosses with its own derived key. + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | vamm1 | ETH/MAR22 | buy | 1 | 101 | 1 | TYPE_LIMIT | TIF_GTC | vamm1-b | + And the network moves ahead "1" blocks + # trade with own derived key + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | vamm1 | 100 | 1 | vamm1-id | true | + And the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party1 | 1 | 0 | 0 | | + | party2 | -1 | 0 | 0 | | + | vamm1 | 1 | 0 | 0 | | + | vamm1-id | -1 | 0 | 0 | true | + + # Now assume someone managed to submit an order on behalf of the derived key + When the parties place the following hacked orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | is amm | + | vamm1-id | ETH/MAR22 | buy | 1 | 0 | 1 | TYPE_MARKET | TIF_FOK | vamm-b | true | + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | vamm1-id | 160 | 1 | lp1 | true | + + When the network moves ahead "1" blocks + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party1 | 1 | 60 | 0 | | + | party2 | -1 | -60 | 0 | | + | vamm1 | 1 | 60 | 0 | | + | vamm1-id | 0 | 0 | -60 | true | + | lp1 | -1 | 0 | 0 | | + + # let's re-open the position for the vAMM, and cancel it using the reduce only method + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | vamm1 | ETH/MAR22 | buy | 2 | 101 | 1 | TYPE_LIMIT | TIF_GTC | vamm1-b2 | + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | vamm1 | 100 | 2 | vamm1-id | true | + + # Check the positions + When the network moves ahead "1" blocks + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party1 | 1 | 0 | 0 | | + | party2 | -1 | 0 | 0 | | + | vamm1 | 3 | 0 | 0 | | + | vamm1-id | -2 | 0 | -60 | true | + | lp1 | -1 | 60 | 0 | | + + # Now the vamm shouldn't generate any more sell orders + When the parties cancel the following AMM: + | party | market id | method | + | vamm1 | ETH/MAR22 | METHOD_REDUCE_ONLY | + # ensure no sell trades + Then the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | party1 | ETH/MAR22 | buy | 1 | 60 | 0 | TYPE_LIMIT | TIF_GTC | p1-b2 | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | party2 | ETH/MAR22 | sell | 1 | 90 | 1 | TYPE_LIMIT | TIF_GTC | p2-s2 | + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | vamm1-id | 100 | 1 | party2 | true | + + # ensure the vAMM position is indeed reduced + When the network moves ahead "1" blocks + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party1 | 1 | 0 | 0 | | + | party2 | -2 | 0 | 0 | | + | vamm1 | 3 | 0 | 0 | | + | vamm1-id | -1 | 0 | -60 | true | + | lp1 | -1 | 60 | 0 | | + + # Now let's see what happens if someone manages to submit a sell order for a reduce-only AMM key + When the parties place the following hacked orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | is amm | + | vamm1-id | ETH/MAR22 | buy | 2 | 0 | 1 | TYPE_MARKET | TIF_FOK | vamm-c | true | + # indeed, the order the vAMM should never create is accepted, and gets executed. + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | vamm1-id | 160 | 2 | lp1 | true | + When the network moves ahead "1" blocks + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party1 | 1 | 60 | 0 | | + | party2 | -2 | -120 | 0 | | + | vamm1 | 3 | 180 | 0 | | + | vamm1-id | 1 | 0 | -120 | true | + | lp1 | -3 | 0 | 0 | | + + # Now the vAMM has switched to long, so it should not trade with a sell order. + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | party2 | ETH/MAR22 | sell | 1 | 90 | 0 | TYPE_LIMIT | TIF_GTC | p2-s3 | + + # But it'll use the buy order to close its own position + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | + | party1 | ETH/MAR22 | buy | 2 | 100 | 2 | TYPE_LIMIT | TIF_GTC | p1-b3 | + Then the following trades should be executed: + | buyer | price | size | seller | is amm | + | party1 | 90 | 1 | party2 | | + | party1 | 99 | 1 | vamm1-id | true | + + When the network moves ahead "1" blocks + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | is amm | + | party1 | 3 | 8 | 0 | | + | party2 | -3 | -7 | 0 | | + | vamm1 | 3 | -3 | 0 | | + | vamm1-id | 0 | 0 | -181 | true | + | lp1 | -3 | 183 | 0 | | + # The AMM pool is indeed cancelled. + And the AMM pool status should be: + | party | market id | amount | status | base | lower bound | upper bound | lower leverage | upper leverage | + | vamm1 | ETH/MAR22 | 100000 | STATUS_CANCELLED | 100 | 85 | 150 | 4 | 4 | + + # Trying to place a vAMM order again results in a margin check failure (the accounts have been drained) + When the parties place the following hacked orders: + | party | market id | side | volume | price | resulting trades | type | tif | reference | is amm | error | + | vamm1-id | ETH/MAR22 | buy | 2 | 0 | 0 | TYPE_MARKET | TIF_FOK | vamm-d | true | margin check failed | diff --git a/core/integration/features/auctions/0094-PRAC-008.feature b/core/integration/features/auctions/0094-PRAC-008.feature index 239d6c15d9b..c0fdbfad474 100644 --- a/core/integration/features/auctions/0094-PRAC-008.feature +++ b/core/integration/features/auctions/0094-PRAC-008.feature @@ -1,4 +1,5 @@ -Feature: When a market's trigger and extension_trigger are set to represent that the market went into auction due to the price monitoring mechanism and was later extended by the same mechanism and the auction is meant to finish at 11am, but now a long block auction is being triggered so that it ends at 10am then this market is unaffected in any way. (0094-PRAC-008) +Feature: When a market's trigger and extension_trigger are set to represent that the market went into auction due to the price monitoring mechanism and was later extended by the same mechanism and the auction is meant to finish at 11am, but now a long block auction is being triggered so that it ends at 10am then this market is unaffected in any way. (0094-PRAC-008) When market is in a price monitoring auction which is meant to finish at 10am, but prior to that time a long block auction finishing at 11am gets triggered then the market stays in auction till 11am, it's auction trigger is listed as price monitoring auction and it's extension trigger is listed as long block auction. (0094-PRAC-006). + Background: Given the following assets are registered: @@ -55,7 +56,7 @@ Feature: When a market's trigger and extension_trigger are set to represent that | lpprov2 | ETH/DEC19 | 2 | 1 | sell | MID | 50 | 100 | @LBA - Scenario: 0094-PRAC-008: Long block auction exceeds the price monitoring auction duration, the auction gets extended. + Scenario: When market is in a price monitoring auction which is meant to finish at 10am, but prior to that time a long block auction finishing at 11am gets triggered then the market stays in auction till 11am, it's auction trigger is listed as price monitoring auction and it's extension trigger is listed as long block auction. (0094-PRAC-006). 0094-PRAC-008: Long block auction exceeds the price monitoring auction duration, the auction gets extended. # place orders and generate trades - slippage 100 When the parties place the following orders: | party | market id | side | volume | price | resulting trades | type | tif | reference | @@ -124,6 +125,9 @@ Feature: When a market's trigger and extension_trigger are set to represent that | party | market id | side | volume | price | resulting trades | type | tif | reference | | party1 | ETH/DEC20 | buy | 1 | 999998 | 0 | TYPE_LIMIT | TIF_GTC | t1-b-3 | | party2 | ETH/DEC20 | sell | 1 | 999998 | 0 | TYPE_LIMIT | TIF_GTC | t2-s-2 | + + # ETH/DEC19 - demonstrates 0094-PRAC-008 + # ETH/DEC20 - demonstrates 0094-PRAC-006 Then the trading mode should be "TRADING_MODE_MONITORING_AUCTION" for the market "ETH/DEC20" And the trading mode should be "TRADING_MODE_LONG_BLOCK_AUCTION" for the market "ETH/DEC19" And the market data for the market "ETH/DEC20" should be: diff --git a/core/integration/features/fees/0029-FEES-high_volume_maker.feature b/core/integration/features/fees/0029-FEES-high_volume_maker.feature new file mode 100644 index 00000000000..21956a6cbcd --- /dev/null +++ b/core/integration/features/fees/0029-FEES-high_volume_maker.feature @@ -0,0 +1,169 @@ +Feature: high volume maker fee rebate + + Background: + + # Initialise timings + Given time is updated to "2023-01-01T00:00:00Z" + And the average block duration is "1" + And the margin calculator named "margin-calculator-1": + | search factor | initial factor | release factor | + | 1.2 | 1.5 | 1.7 | + And the log normal risk model named "log-normal-risk-model": + | risk aversion | tau | mu | r | sigma | + | 0.000001 | 0.1 | 0 | 0 | 1.0 | + And the price monitoring named "price-monitoring": + | horizon | probability | auction extension | + | 3600 | 0.99 | 15 | + + # Initialise the markets and network parameters + Given the following network parameters are set: + | name | value | + | market.fee.factors.infrastructureFee | 0.01 | + | market.fee.factors.makerFee | 0.01 | + | market.fee.factors.buybackFee | 0.001 | + | market.fee.factors.treasuryFee | 0.002 | + | market.auction.minimumDuration | 1 | + | limits.markets.maxPeggedOrders | 4 | + | referralProgram.minStakedVegaTokens | 0 | + | referralProgram.maxPartyNotionalVolumeByQuantumPerEpoch | 1000000000 | + | referralProgram.maxReferralRewardProportion | 0.1 | + | validators.epoch.length | 10s | + + And the volume rebate program tiers named "vrt": + | fraction | rebate | + | 0.2 | 0.001 | + | 0.3 | 0.002 | + + And the volume rebate program: + | id | tiers | closing timestamp | window length | + | id1 | vrt | 0 | 2 | + + And the network moves ahead "1" epochs + + # Initialse the assets and markets + And the following assets are registered: + | id | decimal places | quantum | + | USD | 1 | 1 | + And the markets: + | id | quote name | asset | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | decimal places | position decimal places | + | ETH/USD | ETH | USD | log-normal-risk-model | margin-calculator-1 | 1 | default-none | price-monitoring | default-eth-for-future | 1e-3 | 0 | default-futures | 1 | 1 | + And the liquidity monitoring parameters: + | name | triggering ratio | time window | scaling factor | + | lqm-params | 1.0 | 3600s | 1 | + When the markets are updated: + | id | liquidity monitoring | linear slippage factor | quadratic slippage factor | + | ETH/USD | lqm-params | 1e-3 | 0 | + + # Initialise the parties + Given the parties deposit on asset's general account the following amount: + | party | asset | amount | + | lpprov | USD | 1000000000 | + | lpprov2 | USD | 1000000000 | + | aux1 | USD | 1000000000 | + | aux2 | USD | 1000000000 | + | trader1 | USD | 1000000000 | + | trader2 | USD | 1000000000 | + | trader3 | USD | 1000000000 | + + # Exit the opening auction + Given the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp1 | lpprov | ETH/USD | 1000000 | 0.01 | submission | + + And the parties place the following pegged iceberg orders: + | party | market id | peak size | minimum visible size | side | pegged reference | volume | offset | + | lpprov | ETH/USD | 5000 | 1000 | buy | BID | 10000 | 1 | + | lpprov | ETH/USD | 5000 | 1000 | sell | ASK | 10000 | 1 | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | aux1 | ETH/USD | buy | 1 | 990 | 0 | TYPE_LIMIT | TIF_GTC | + | aux1 | ETH/USD | buy | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | aux2 | ETH/USD | sell | 1 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | aux2 | ETH/USD | sell | 1 | 1100 | 0 | TYPE_LIMIT | TIF_GTC | + + Then the opening auction period ends for market "ETH/USD" + And the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH/USD" + + Scenario: When there is `high_volume_market_maker_rebate`, `high_volume_maker_fee` should be taken from the `treasury/buyback_fee` components with value `high_volume_maker_fee = high_volume_factor * trade_value_for_fee_purposes` (0029-FEES-042, 0029-FEES-043, 0029-FEES-044, 0029-FEES-047) + + Given the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | trader1 | ETH/USD | sell | 210 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | trader2 | ETH/USD | sell | 310 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | trader3 | ETH/USD | sell | 480 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | aux1 | ETH/USD | buy | 1000 | 1000 | 3 | TYPE_LIMIT | TIF_GTC | + + And the network moves ahead "1" epochs + Given the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | trader1 | ETH/USD | sell | 210 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | trader2 | ETH/USD | sell | 310 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | trader3 | ETH/USD | sell | 480 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | aux1 | ETH/USD | buy | 1000 | 1000 | 3 | TYPE_LIMIT | TIF_GTC | + + And the network moves ahead "1" epochs + Given the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | trader1 | ETH/USD | sell | 210 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | trader2 | ETH/USD | sell | 310 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | trader3 | ETH/USD | sell | 480 | 1000 | 0 | TYPE_LIMIT | TIF_GTC | + | aux1 | ETH/USD | buy | 1000 | 1000 | 3 | TYPE_LIMIT | TIF_GTC | + + Then the following trades should be executed: + | buyer | price | size | seller | + | aux1 | 1000 | 210 | trader1 | + | aux1 | 1000 | 310 | trader2 | + | aux1 | 1000 | 480 | trader3 | + + And the network moves ahead "1" epochs + + # trade_value_for_fee_purposes for trade between aux1 and trader1 = size_of_trade * price_of_trade = 21 * 100 = 2100 + # treasury_fee = 0.002*2100=4.2 + # buyback_fee = 0.001*2100=2.1 + # treasury_fee = treasury_fee *(1 - high_volume_maker_fee / (treasury_fee + buyback_fee)) = 4.2*(1-2.1/(4.2+2.1))=2.8 + # buyback_fee = buyback_fee*(1 - high_volume_maker_fee / (treasury_fee + buyback_fee)) =2.1*(1-2.1/(4.2+2.1))=1.4 + + # trade_value_for_fee_purposes for trade between aux1 and trader2 = size_of_trade * price_of_trade = 31 * 100 = 3100 + # treasury_fee = 0.002*3100=6.2 + # buyback_fee = 0.001*3100=3.1 + # treasury_fee = treasury_fee *(1 - high_volume_maker_fee / (treasury_fee + buyback_fee)) = 6.2*(1-6.2/(6.2+3.1))=2.07 + # buyback_fee = buyback_fee*(1 - high_volume_maker_fee / (treasury_fee + buyback_fee)) =3.1*(1-6.2/(6.2+3.1))=1.03 + + + # trade_value_for_fee_purposes for trade between aux1 and trader3 = size_of_trade * price_of_trade = 48 * 100 = 4800 + # treasury_fee = 0.002*4800=9.6 + # buyback_fee = 0.001*4800=4.8 + # treasury_fee = treasury_fee *(1 - high_volume_maker_fee / (treasury_fee + buyback_fee)) = 9.6*(1-9.6/(9.6+4.8))=3.2 + # buyback_fee = buyback_fee*(1 - high_volume_maker_fee / (treasury_fee + buyback_fee)) =4.8*(1-9.6/(9.6+4.8))=1.6 + + # trade_value_for_fee_purposes for trader1 = size_of_trade * price_of_trade = 21 * 100 = 2100 + # maker_fee = fee_factor[maker] * trade_value_for_fee_purposes = 0.01 * 2100 = 21 + # high_marker_fee_rebate_receive=0.001 * 2100=2.1 + + # trade_value_for_fee_purposes for trader2 = size_of_trade * price_of_trade = 31 * 100 = 3100 + # maker_fee = fee_factor[maker] * trade_value_for_fee_purposes = 0.01 * 3100 = 31 + # high_marker_fee_rebate_receive=0.002 * 3100=6.2 + + # trade_value_for_fee_purposes for trader3 = size_of_trade * price_of_trade = 48 * 100 = 4800 + # maker_fee = fee_factor[maker] * trade_value_for_fee_purposes = 0.01 * 4800 = 48 + # high_marker_fee_rebate_receive=0.002 * 4800=9.6 + + And the following transfers should happen: + | from | to | from account | to account | market id | amount | asset | + | aux1 | | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_NETWORK_TREASURY | | 28 | USD | + | aux1 | | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_NETWORK_TREASURY | | 20 | USD | + | aux1 | | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_NETWORK_TREASURY | | 31 | USD | + | aux1 | | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_BUY_BACK_FEES | | 14 | USD | + | aux1 | | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_BUY_BACK_FEES | | 10 | USD | + | aux1 | | ACCOUNT_TYPE_GENERAL | ACCOUNT_TYPE_BUY_BACK_FEES | | 15 | USD | + | market | trader1 | ACCOUNT_TYPE_FEES_MAKER | ACCOUNT_TYPE_GENERAL | ETH/USD | 210 | USD | + | market | trader1 | ACCOUNT_TYPE_FEES_MAKER | ACCOUNT_TYPE_GENERAL | ETH/USD | 21 | USD | + | market | trader2 | ACCOUNT_TYPE_FEES_MAKER | ACCOUNT_TYPE_GENERAL | ETH/USD | 310 | USD | + | market | trader2 | ACCOUNT_TYPE_FEES_MAKER | ACCOUNT_TYPE_GENERAL | ETH/USD | 62 | USD | + | market | trader3 | ACCOUNT_TYPE_FEES_MAKER | ACCOUNT_TYPE_GENERAL | ETH/USD | 480 | USD | + | market | trader3 | ACCOUNT_TYPE_FEES_MAKER | ACCOUNT_TYPE_GENERAL | ETH/USD | 96 | USD | + + + + diff --git a/core/integration/features/fees/0042-LIQF-fees_settings.feature b/core/integration/features/fees/0042-LIQF-fees_settings.feature index fa5288d0346..1e466695d71 100644 --- a/core/integration/features/fees/0042-LIQF-fees_settings.feature +++ b/core/integration/features/fees/0042-LIQF-fees_settings.feature @@ -36,7 +36,7 @@ Feature: Test liquidity fee settings, using 3 different methods | id | quote name | asset | liquidity monitoring | risk model | margin calculator | auction duration | fees | price monitoring | data source config | linear slippage factor | quadratic slippage factor | sla params | | ETH/MAR22 | USD | USD | lqm-params | simple-risk-model-1 | default-margin-calculator | 2 | fees-config-1 | price-monitoring | default-eth-for-future | 0.2 | 0 | SLA | - Scenario: 001 Liquidity fee setting to METHOD_CONSTANT(0042-LIQF-058), METHOD_MARGINAL_COST, and METHOD_WEIGHTED_AVERAGE(0042-LIQF-057) + Scenario: 001 Liquidity fee setting to METHOD_CONSTANT(0042-LIQF-058, 0042-LIQF-061), METHOD_MARGINAL_COST(0042-LIQF-062), and METHOD_WEIGHTED_AVERAGE(0042-LIQF-057, 0042-LIQF-056) # setup accounts Given the parties deposit on asset's general account the following amount: | party | asset | amount | diff --git a/core/integration/features/orders/stoporders.feature b/core/integration/features/orders/stoporders.feature index 70289f28aa4..aa564fd3870 100644 --- a/core/integration/features/orders/stoporders.feature +++ b/core/integration/features/orders/stoporders.feature @@ -1885,8 +1885,7 @@ Feature: stop orders | party2 | ETH/DEC19 | STATUS_EXPIRED | stop2-1 | - Scenario: A party with a long position cannot enter a buy stop order, - and a party with a short position cannot enter a sell stop order. (0014-ORDT-137) + Scenario: A party with a long or short position CAN increase their position with stop orders. (0014-ORDT-137) # setup accounts Given time is updated to "2019-11-30T00:00:00Z" @@ -1933,14 +1932,77 @@ Feature: stop orders # We should not be able to place a but stop order for party2 as they have a long position and it would make it more long When the parties place the following orders: - | party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | fb price trigger | reference | error | - | party2 | ETH/DEC19 | buy | 1 | 0 | 0 | TYPE_MARKET | TIF_IOC | reduce | 75 | 25 | stop | side used in stop order does not close the position | + | party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | fb price trigger | reference | error | + | party2 | ETH/DEC19 | buy | 1 | 0 | 0 | TYPE_MARKET | TIF_IOC | reduce | 75 | 25 | stop | | # We should not be able to place a sell stop order for party1 as they have a short position and it would make it more short When the parties place the following orders: - | party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | fb price trigger | reference | error | - | party1 | ETH/DEC19 | sell | 1 | 0 | 0 | TYPE_MARKET | TIF_IOC | reduce | 75 | 25 | stop | side used in stop order does not close the position | + | party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | fb price trigger | reference | error | + | party1 | ETH/DEC19 | sell | 1 | 0 | 0 | TYPE_MARKET | TIF_IOC | reduce | 75 | 25 | stop | | + Scenario: A party with a long position cannot flip to short by placing a stop order. + Given time is updated to "2019-11-30T00:00:00Z" + And the parties deposit on asset's general account the following amount: + | party | asset | amount | + | party1 | BTC | 10000000 | + | party2 | BTC | 10000000 | + | party3 | BTC | 10000000 | + | aux | BTC | 10000000 | + | aux2 | BTC | 10000000 | + | aux3 | BTC | 100000 | + | lpprov | BTC | 90000000 | + + And the parties submit the following liquidity provision: + | id | party | market id | commitment amount | fee | lp type | + | lp1 | lpprov | ETH/DEC19 | 90000000 | 0.1 | submission | + | lp1 | lpprov | ETH/DEC19 | 90000000 | 0.1 | submission | + And the parties place the following pegged iceberg orders: + | party | market id | peak size | minimum visible size | side | pegged reference | volume | offset | + | lpprov | ETH/DEC19 | 2 | 1 | buy | BID | 50 | 100 | + | lpprov | ETH/DEC19 | 2 | 1 | sell | ASK | 50 | 100 | + + # place auxiliary orders so we always have best bid and best offer as to not trigger the liquidity auction + And the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | aux | ETH/DEC19 | buy | 100 | 1 | 0 | TYPE_LIMIT | TIF_GTC | + | aux | ETH/DEC19 | sell | 100 | 10001 | 0 | TYPE_LIMIT | TIF_GTC | + | aux2 | ETH/DEC19 | buy | 5 | 50 | 0 | TYPE_LIMIT | TIF_GTC | + | aux3 | ETH/DEC19 | sell | 5 | 50 | 0 | TYPE_LIMIT | TIF_GTC | + + When the opening auction period ends for market "ETH/DEC19" + Then the trading mode should be "TRADING_MODE_CONTINUOUS" for the market "ETH/DEC19" + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party1 | ETH/DEC19 | sell | 10 | 50 | 0 | TYPE_LIMIT | TIF_GTC | + | party2 | ETH/DEC19 | buy | 10 | 50 | 1 | TYPE_LIMIT | TIF_GTC | + Then the following trades should be executed: + | buyer | seller | price | size | + | party2 | party1 | 50 | 10 | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | + | party1 | ETH/DEC19 | sell | 1 | 75 | 0 | TYPE_LIMIT | TIF_GTC | + | party3 | ETH/DEC19 | buy | 50 | 75 | 1 | TYPE_LIMIT | TIF_GTC | + And the network moves ahead "1" blocks + Then the following trades should be executed: + | buyer | seller | price | size | + | party3 | party1 | 75 | 1 | + + When the parties place the following orders: + | party | market id | side | volume | price | resulting trades | type | tif | only | ra price trigger | fb price trigger | reference | error | + | party2 | ETH/DEC19 | sell | 20 | 0 | 1 | TYPE_MARKET | TIF_IOC | reduce | 75 | 25 | stop | | + Then the following trades should be executed: + | buyer | seller | price | size | + | party3 | party2 | 75 | 10 | + + # Ensure the party has closed its position, despite the stop order being for a larger volume than their open position. + When the network moves ahead "1" blocks + Then the parties should have the following profit and loss: + | party | volume | unrealised pnl | realised pnl | + | party1 | -11 | -250 | 0 | + | party2 | 0 | 0 | 250 | + | party3 | 11 | 0 | 0 | Scenario: If a stop order is placed with a position_fraction equal to 0.5 and the position size is 5 then the rounding should be equal to 3 (0014-ORDT-138) diff --git a/core/integration/features/referrals/0083-RFPR-benefit_factors.feature b/core/integration/features/referrals/0083-RFPR-benefit_factors.feature index 5b9a65cb655..1560edfd046 100644 --- a/core/integration/features/referrals/0083-RFPR-benefit_factors.feature +++ b/core/integration/features/referrals/0083-RFPR-benefit_factors.feature @@ -28,9 +28,9 @@ Feature: Setting and applying referee benefit factors # Initalise the referral program then move forwards an epoch to start the program Given the referral benefit tiers "rbt": - | minimum running notional taker volume | minimum epochs | referral reward factor | referral discount factor | - | 2000 | 2 | 0.02 | 0.02 | - | 3000 | 3 | 0.20 | 0.20 | + | minimum running notional taker volume | minimum epochs | referral reward infra factor | referral reward maker factor | referral reward liquidity factor | referral discount infra factor | referral discount maker factor | referral discount liquidity factor | + | 2000 | 2 | 0.021 | 0.022 | 0.023 | 0.024 | 0.025 | 0.026 | + | 3000 | 3 | 0.21 | 0.22 | 0.23 | 0.24 | 0.25 | 0.26 | And the referral staking tiers "rst": | minimum staked tokens | referral reward multiplier | | 1 | 1 | @@ -125,8 +125,8 @@ Feature: Setting and applying referee benefit factors | referee1 | ETH/USD.1.1 | sell | | 1000 | 1 | TYPE_LIMIT | TIF_GTC | When the network moves ahead