diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/affiliates.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/affiliates.ts index f6ded433c8..ace13fa1bc 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/affiliates.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/affiliates.ts @@ -74,6 +74,63 @@ export interface AffiliateWhitelist_TierSDKType { taker_fee_share_ppm: number; } +<<<<<<< HEAD +======= +/** AffiliateParameters defines the parameters for the affiliate program. */ + +export interface AffiliateParameters { + /** + * Maximum attributable volume for a referred user in a 30d rolling window in + * notional + */ + maximum_30dAttributableVolumePerReferredUserQuoteQuantums: Long; + /** Referred user automatically gets set to this fee tier */ + + refereeMinimumFeeTierIdx: number; + /** + * Maximum affiliate revenue for a referred user in a 30d rolling window in + * quote quantums + */ + + maximum_30dAffiliateRevenuePerReferredUserQuoteQuantums: Long; +} +/** AffiliateParameters defines the parameters for the affiliate program. */ + +export interface AffiliateParametersSDKType { + /** + * Maximum attributable volume for a referred user in a 30d rolling window in + * notional + */ + maximum_30d_attributable_volume_per_referred_user_quote_quantums: Long; + /** Referred user automatically gets set to this fee tier */ + + referee_minimum_fee_tier_idx: number; + /** + * Maximum affiliate revenue for a referred user in a 30d rolling window in + * quote quantums + */ + + maximum_30d_affiliate_revenue_per_referred_user_quote_quantums: Long; +} +/** AffiliateOverrides defines the affiliate whitelist. */ + +export interface AffiliateOverrides { + /** + * List of unique whitelisted addresses. + * These are automatically put at the maximum affiliate tier + */ + addresses: string[]; +} +/** AffiliateOverrides defines the affiliate whitelist. */ + +export interface AffiliateOverridesSDKType { + /** + * List of unique whitelisted addresses. + * These are automatically put at the maximum affiliate tier + */ + addresses: string[]; +} +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) function createBaseAffiliateTiers(): AffiliateTiers { return { @@ -283,4 +340,117 @@ export const AffiliateWhitelist_Tier = { return message; } +<<<<<<< HEAD +======= +}; + +function createBaseAffiliateParameters(): AffiliateParameters { + return { + maximum_30dAttributableVolumePerReferredUserQuoteQuantums: Long.UZERO, + refereeMinimumFeeTierIdx: 0, + maximum_30dAffiliateRevenuePerReferredUserQuoteQuantums: Long.UZERO + }; +} + +export const AffiliateParameters = { + encode(message: AffiliateParameters, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (!message.maximum_30dAttributableVolumePerReferredUserQuoteQuantums.isZero()) { + writer.uint32(8).uint64(message.maximum_30dAttributableVolumePerReferredUserQuoteQuantums); + } + + if (message.refereeMinimumFeeTierIdx !== 0) { + writer.uint32(16).uint32(message.refereeMinimumFeeTierIdx); + } + + if (!message.maximum_30dAffiliateRevenuePerReferredUserQuoteQuantums.isZero()) { + writer.uint32(24).uint64(message.maximum_30dAffiliateRevenuePerReferredUserQuoteQuantums); + } + + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AffiliateParameters { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAffiliateParameters(); + + while (reader.pos < end) { + const tag = reader.uint32(); + + switch (tag >>> 3) { + case 1: + message.maximum_30dAttributableVolumePerReferredUserQuoteQuantums = (reader.uint64() as Long); + break; + + case 2: + message.refereeMinimumFeeTierIdx = reader.uint32(); + break; + + case 3: + message.maximum_30dAffiliateRevenuePerReferredUserQuoteQuantums = (reader.uint64() as Long); + break; + + default: + reader.skipType(tag & 7); + break; + } + } + + return message; + }, + + fromPartial(object: DeepPartial): AffiliateParameters { + const message = createBaseAffiliateParameters(); + message.maximum_30dAttributableVolumePerReferredUserQuoteQuantums = object.maximum_30dAttributableVolumePerReferredUserQuoteQuantums !== undefined && object.maximum_30dAttributableVolumePerReferredUserQuoteQuantums !== null ? Long.fromValue(object.maximum_30dAttributableVolumePerReferredUserQuoteQuantums) : Long.UZERO; + message.refereeMinimumFeeTierIdx = object.refereeMinimumFeeTierIdx ?? 0; + message.maximum_30dAffiliateRevenuePerReferredUserQuoteQuantums = object.maximum_30dAffiliateRevenuePerReferredUserQuoteQuantums !== undefined && object.maximum_30dAffiliateRevenuePerReferredUserQuoteQuantums !== null ? Long.fromValue(object.maximum_30dAffiliateRevenuePerReferredUserQuoteQuantums) : Long.UZERO; + return message; + } + +}; + +function createBaseAffiliateOverrides(): AffiliateOverrides { + return { + addresses: [] + }; +} + +export const AffiliateOverrides = { + encode(message: AffiliateOverrides, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + for (const v of message.addresses) { + writer.uint32(10).string(v!); + } + + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AffiliateOverrides { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAffiliateOverrides(); + + while (reader.pos < end) { + const tag = reader.uint32(); + + switch (tag >>> 3) { + case 1: + message.addresses.push(reader.string()); + break; + + default: + reader.skipType(tag & 7); + break; + } + } + + return message; + }, + + fromPartial(object: DeepPartial): AffiliateOverrides { + const message = createBaseAffiliateOverrides(); + message.addresses = object.addresses?.map(e => e) || []; + return message; + } + +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) }; \ No newline at end of file diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/genesis.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/genesis.ts index 17ec53fdf6..285720a405 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/genesis.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/genesis.ts @@ -1,4 +1,4 @@ -import { AffiliateTiers, AffiliateTiersSDKType } from "./affiliates"; +import { AffiliateTiers, AffiliateTiersSDKType, AffiliateParameters, AffiliateParametersSDKType } from "./affiliates"; import * as _m0 from "protobufjs/minimal"; import { DeepPartial } from "../../helpers"; /** GenesisState defines generis state of `x/affiliates` */ @@ -6,17 +6,24 @@ import { DeepPartial } from "../../helpers"; export interface GenesisState { /** The list of affiliate tiers */ affiliateTiers?: AffiliateTiers; + /** The affiliate parameters */ + + affiliateParameters?: AffiliateParameters; } /** GenesisState defines generis state of `x/affiliates` */ export interface GenesisStateSDKType { /** The list of affiliate tiers */ affiliate_tiers?: AffiliateTiersSDKType; + /** The affiliate parameters */ + + affiliate_parameters?: AffiliateParametersSDKType; } function createBaseGenesisState(): GenesisState { return { - affiliateTiers: undefined + affiliateTiers: undefined, + affiliateParameters: undefined }; } @@ -26,6 +33,10 @@ export const GenesisState = { AffiliateTiers.encode(message.affiliateTiers, writer.uint32(10).fork()).ldelim(); } + if (message.affiliateParameters !== undefined) { + AffiliateParameters.encode(message.affiliateParameters, writer.uint32(18).fork()).ldelim(); + } + return writer; }, @@ -42,6 +53,10 @@ export const GenesisState = { message.affiliateTiers = AffiliateTiers.decode(reader, reader.uint32()); break; + case 2: + message.affiliateParameters = AffiliateParameters.decode(reader, reader.uint32()); + break; + default: reader.skipType(tag & 7); break; @@ -54,6 +69,7 @@ export const GenesisState = { fromPartial(object: DeepPartial): GenesisState { const message = createBaseGenesisState(); message.affiliateTiers = object.affiliateTiers !== undefined && object.affiliateTiers !== null ? AffiliateTiers.fromPartial(object.affiliateTiers) : undefined; + message.affiliateParameters = object.affiliateParameters !== undefined && object.affiliateParameters !== null ? AffiliateParameters.fromPartial(object.affiliateParameters) : undefined; return message; } diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/query.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/query.ts index 1cdebd2f25..4ccbb18ada 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/query.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/query.ts @@ -39,10 +39,15 @@ export interface AffiliateInfoResponse { feeSharePpm: number; /** The affiliate's all-time referred volume in quote quantums. */ + /** @deprecated */ + referredVolume: Uint8Array; /** The affiliate's currently staked native tokens (in whole coins). */ stakedAmount: Uint8Array; + /** The affiliate's 30d referred volume in quote quantums. */ + + referredVolume_30dRolling: Uint8Array; } /** * AffiliateInfoResponse is the response type for the Query/AffiliateInfo RPC @@ -66,10 +71,15 @@ export interface AffiliateInfoResponseSDKType { fee_share_ppm: number; /** The affiliate's all-time referred volume in quote quantums. */ + /** @deprecated */ + referred_volume: Uint8Array; /** The affiliate's currently staked native tokens (in whole coins). */ staked_amount: Uint8Array; + /** The affiliate's 30d referred volume in quote quantums. */ + + referred_volume_30d_rolling: Uint8Array; } /** ReferredByRequest is the request type for the Query/ReferredBy RPC method. */ @@ -205,7 +215,8 @@ function createBaseAffiliateInfoResponse(): AffiliateInfoResponse { tier: 0, feeSharePpm: 0, referredVolume: new Uint8Array(), - stakedAmount: new Uint8Array() + stakedAmount: new Uint8Array(), + referredVolume_30dRolling: new Uint8Array() }; } @@ -231,6 +242,10 @@ export const AffiliateInfoResponse = { writer.uint32(42).bytes(message.stakedAmount); } + if (message.referredVolume_30dRolling.length !== 0) { + writer.uint32(50).bytes(message.referredVolume_30dRolling); + } + return writer; }, @@ -263,6 +278,10 @@ export const AffiliateInfoResponse = { message.stakedAmount = reader.bytes(); break; + case 6: + message.referredVolume_30dRolling = reader.bytes(); + break; + default: reader.skipType(tag & 7); break; @@ -279,6 +298,7 @@ export const AffiliateInfoResponse = { message.feeSharePpm = object.feeSharePpm ?? 0; message.referredVolume = object.referredVolume ?? new Uint8Array(); message.stakedAmount = object.stakedAmount ?? new Uint8Array(); + message.referredVolume_30dRolling = object.referredVolume_30dRolling ?? new Uint8Array(); return message; } diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/stats.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/stats.ts index 1f41aed7f6..f6ae2bb960 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/stats.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/stats.ts @@ -105,6 +105,15 @@ export interface UserStats { /** Maker USDC in quantums */ makerNotional: Long; +<<<<<<< HEAD +======= + /** Affiliate revenue generated in quantums with this user being a referee */ + + affiliate_30dRevenueGeneratedQuantums: Long; + /** Referred volume in quote quantums with this user being an affiliate */ + + affiliate_30dReferredVolumeQuoteQuantums: Long; +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) } /** UserStats stores stats for a User */ @@ -114,6 +123,15 @@ export interface UserStatsSDKType { /** Maker USDC in quantums */ maker_notional: Long; +<<<<<<< HEAD +======= + /** Affiliate revenue generated in quantums with this user being a referee */ + + affiliate_30d_revenue_generated_quantums: Long; + /** Referred volume in quote quantums with this user being an affiliate */ + + affiliate_30d_referred_volume_quote_quantums: Long; +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) } /** CachedStakeAmount stores the last calculated total staked amount for address */ @@ -453,7 +471,13 @@ export const GlobalStats = { function createBaseUserStats(): UserStats { return { takerNotional: Long.UZERO, +<<<<<<< HEAD makerNotional: Long.UZERO +======= + makerNotional: Long.UZERO, + affiliate_30dRevenueGeneratedQuantums: Long.UZERO, + affiliate_30dReferredVolumeQuoteQuantums: Long.UZERO +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) }; } @@ -467,6 +491,17 @@ export const UserStats = { writer.uint32(16).uint64(message.makerNotional); } +<<<<<<< HEAD +======= + if (!message.affiliate_30dRevenueGeneratedQuantums.isZero()) { + writer.uint32(24).uint64(message.affiliate_30dRevenueGeneratedQuantums); + } + + if (!message.affiliate_30dReferredVolumeQuoteQuantums.isZero()) { + writer.uint32(32).uint64(message.affiliate_30dReferredVolumeQuoteQuantums); + } + +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) return writer; }, @@ -487,6 +522,17 @@ export const UserStats = { message.makerNotional = (reader.uint64() as Long); break; +<<<<<<< HEAD +======= + case 3: + message.affiliate_30dRevenueGeneratedQuantums = (reader.uint64() as Long); + break; + + case 4: + message.affiliate_30dReferredVolumeQuoteQuantums = (reader.uint64() as Long); + break; + +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) default: reader.skipType(tag & 7); break; @@ -500,6 +546,11 @@ export const UserStats = { const message = createBaseUserStats(); message.takerNotional = object.takerNotional !== undefined && object.takerNotional !== null ? Long.fromValue(object.takerNotional) : Long.UZERO; message.makerNotional = object.makerNotional !== undefined && object.makerNotional !== null ? Long.fromValue(object.makerNotional) : Long.UZERO; +<<<<<<< HEAD +======= + message.affiliate_30dRevenueGeneratedQuantums = object.affiliate_30dRevenueGeneratedQuantums !== undefined && object.affiliate_30dRevenueGeneratedQuantums !== null ? Long.fromValue(object.affiliate_30dRevenueGeneratedQuantums) : Long.UZERO; + message.affiliate_30dReferredVolumeQuoteQuantums = object.affiliate_30dReferredVolumeQuoteQuantums !== undefined && object.affiliate_30dReferredVolumeQuoteQuantums !== null ? Long.fromValue(object.affiliate_30dReferredVolumeQuoteQuantums) : Long.UZERO; +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) return message; } diff --git a/proto/dydxprotocol/affiliates/affiliates.proto b/proto/dydxprotocol/affiliates/affiliates.proto index aba3335b9d..c577bf0554 100644 --- a/proto/dydxprotocol/affiliates/affiliates.proto +++ b/proto/dydxprotocol/affiliates/affiliates.proto @@ -33,4 +33,30 @@ message AffiliateWhitelist { } // All affiliate whitelist tiers. repeated Tier tiers = 1 [ (gogoproto.nullable) = false ]; -} \ No newline at end of file +<<<<<<< HEAD +} +======= +} + +// AffiliateParameters defines the parameters for the affiliate program. +message AffiliateParameters { + // Maximum attributable volume for a referred user in a 30d rolling window in + // notional + uint64 maximum_30d_attributable_volume_per_referred_user_quote_quantums = 1; + + // Referred user automatically gets set to this fee tier + uint32 referee_minimum_fee_tier_idx = 2; + + // Maximum affiliate revenue for a referred user in a 30d rolling window in + // quote quantums + uint64 maximum_30d_affiliate_revenue_per_referred_user_quote_quantums = 3; +} + +// AffiliateOverrides defines the affiliate whitelist. +message AffiliateOverrides { + // List of unique whitelisted addresses. + // These are automatically put at the maximum affiliate tier + repeated string addresses = 1 + [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; +} +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) diff --git a/proto/dydxprotocol/affiliates/genesis.proto b/proto/dydxprotocol/affiliates/genesis.proto index 4dd0dd2291..a653747ed2 100644 --- a/proto/dydxprotocol/affiliates/genesis.proto +++ b/proto/dydxprotocol/affiliates/genesis.proto @@ -9,4 +9,7 @@ option go_package = "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/type message GenesisState { // The list of affiliate tiers AffiliateTiers affiliate_tiers = 1 [ (gogoproto.nullable) = false ]; + + // The affiliate parameters + AffiliateParameters affiliate_parameters = 2 [ (gogoproto.nullable) = false ]; } \ No newline at end of file diff --git a/proto/dydxprotocol/affiliates/query.proto b/proto/dydxprotocol/affiliates/query.proto index 9e2c612e70..8d2d4fd6e7 100644 --- a/proto/dydxprotocol/affiliates/query.proto +++ b/proto/dydxprotocol/affiliates/query.proto @@ -55,7 +55,8 @@ message AffiliateInfoResponse { bytes referred_volume = 4 [ (gogoproto.customtype) = "github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt", - (gogoproto.nullable) = false + (gogoproto.nullable) = false, + deprecated = true ]; // The affiliate's currently staked native tokens (in whole coins). bytes staked_amount = 5 [ @@ -63,6 +64,12 @@ message AffiliateInfoResponse { "github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt", (gogoproto.nullable) = false ]; + // The affiliate's 30d referred volume in quote quantums. + bytes referred_volume_30d_rolling = 6 [ + (gogoproto.customtype) = + "github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt", + (gogoproto.nullable) = false + ]; } // ReferredByRequest is the request type for the Query/ReferredBy RPC method. diff --git a/proto/dydxprotocol/stats/stats.proto b/proto/dydxprotocol/stats/stats.proto index 4d1558fa1b..53b15219a7 100644 --- a/proto/dydxprotocol/stats/stats.proto +++ b/proto/dydxprotocol/stats/stats.proto @@ -60,6 +60,15 @@ message UserStats { // Maker USDC in quantums uint64 maker_notional = 2; +<<<<<<< HEAD +======= + + // Affiliate revenue generated in quantums with this user being a referee + uint64 affiliate_30d_revenue_generated_quantums = 3; + + // Referred volume in quote quantums with this user being an affiliate + uint64 affiliate_30d_referred_volume_quote_quantums = 4; +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) } // CachedStakeAmount stores the last calculated total staked amount for address diff --git a/protocol/app/app.go b/protocol/app/app.go index a249eda009..6a78da46d7 100644 --- a/protocol/app/app.go +++ b/protocol/app/app.go @@ -993,6 +993,7 @@ func New( }, app.AffiliatesKeeper, *app.FeeTiersKeeper, + app.StatsKeeper, ) revShareModule := revsharemodule.NewAppModule(appCodec, app.RevShareKeeper) app.FeeTiersKeeper.SetRevShareKeeper(app.RevShareKeeper) diff --git a/protocol/app/testdata/default_genesis_state.json b/protocol/app/testdata/default_genesis_state.json index 0ffa88aba8..0e70dedd02 100644 --- a/protocol/app/testdata/default_genesis_state.json +++ b/protocol/app/testdata/default_genesis_state.json @@ -37,6 +37,11 @@ "taker_fee_share_ppm": 150000 } ] + }, + "affiliate_parameters": { + "maximum_30d_affiliate_revenue_per_referred_user_quote_quantums": "10000000000", + "maximum_30d_attributable_volume_per_referred_user_quote_quantums": "100000000000000", + "referee_minimum_fee_tier_idx": 2 } }, "auth": { diff --git a/protocol/scripts/affiliates/update_affiliate_parameters.py b/protocol/scripts/affiliates/update_affiliate_parameters.py new file mode 100644 index 0000000000..97cbb4b588 --- /dev/null +++ b/protocol/scripts/affiliates/update_affiliate_parameters.py @@ -0,0 +1,177 @@ +#!/usr/bin/env python3 +""" +Script to add an order router rev share to the protocol. +""" + +import argparse +import os +import shutil +import subprocess +import sys +import tempfile +import yaml +import json +import time +from typing import Dict, Any + +# Mainnet configuration +mainnet_node = "https://dydx-ops-rpc.kingnodes.com:443" +mainnet_chain = "dydx-mainnet-1" + +# Staging configuration +staging_node = "https://validator.v4staging.dydx.exchange:443" +staging_chain = "dydxprotocol-testnet" + +# Testnet configuration +testnet_node = "https://validator.v4testnet.dydx.exchange:443" +testnet_chain = "dydxprotocol-testnet" + +PROPOSAL_STATUS_PASSED = 3 + +def vote_for(node, chain, proposal_id, person): + print("voting as " + person) + cmd = [ + "dydxprotocold", + "tx", + "gov", + "vote", + proposal_id, + "yes", + "--from=" + person, + "--node=" + node, + "--chain-id=" + chain, + "--keyring-backend=test", + "--fees=5000000000000000adv4tnt", + "--yes" + ] + result = subprocess.run(cmd, capture_output=True, text=True) + if result.returncode != 0: + raise Exception(f"Failed to vote: {result.stderr}") + +def load_yml(file_path) -> Dict[str, Any]: + """ + Loads any yml file and returns the data as a dictionary. + + Args: + file_path: Path to the yml file + + Returns: + Dictionary containing the parsed data + """ + try: + with open(file_path, 'r', encoding='utf-8') as file: + data = yaml.safe_load(file) + return data + except FileNotFoundError: + print(f"Error: File '{file_path}' not found.") + return {} + except yaml.YAMLError as e: + print(f"Error parsing YAML file: {e}") + return {} + +def get_proposal_id(node, chain): + cmd = [ + "dydxprotocold", + "query", + "gov", + "proposals", + "--node=" + node, + "--chain-id=" + chain + ] + with tempfile.NamedTemporaryFile(mode='w', suffix='.json') as tmp_file: + subprocess.run(cmd, stdout=tmp_file) + result = load_yml(tmp_file.name) + return result['proposals'][-1]['id'] + +def main(): + parser = argparse.ArgumentParser(description='Parse market map and sync markets') + parser.add_argument('--chain-id', default=staging_chain, help='Chain ID, default is dydxprotocol-testnet') + parser.add_argument('--node', default=staging_node, help='Node URL, default is https://validator.v4staging.dydx.exchange:443') + parser.add_argument('--max-30d-commission', type=int, required=True, help='Maximum 30d commission per referred') + parser.add_argument('--referee-min-fee-tier', type=int, required=True, help='Referee minimum fee tier idx') + parser.add_argument('--max-30d-revenue', type=int, required=True, help='Maximum 30d affiliate revenue per affiliate') + args = parser.parse_args() + + counter = 0 + # 3 retries for the process. + for i in range(3): + try: + with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as tmp_file: + affiliate_parameters_msg = { + "messages": [ + { + "@type": "/dydxprotocol.affiliates.MsgUpdateAffiliateParameters", + "authority": "dydx10d07y265gmmuvt4z0w9aw880jnsr700jnmapky", + "affiliate_parameters": { + "maximum_30d_commission_per_referred_quote_quantums": int(args.max_30d_commission), + "referee_minimum_fee_tier_idx": int(args.referee_min_fee_tier), + "maximum_30d_affiliate_revenue_per_affiliate_quote_quantums": int(args.max_30d_revenue), + } + } + ], + "deposit": "10000000000000000000000adv4tnt", + "metadata": "", + "title": "Update affiliate parameters", + "summary": f"Update affiliate parameters: max_30d_commission={args.max_30d_commission}, referee_min_fee_tier={args.referee_min_fee_tier}, max_30d_revenue={args.max_30d_revenue}" + } + json.dump(affiliate_parameters_msg, tmp_file, indent=2) + print(affiliate_parameters_msg) + tmp_file_path = tmp_file.name + print("submitting proposal for affiliate parameters update") + cmd = [ + "dydxprotocold", + "tx", + "gov", + "submit-proposal", + tmp_file_path, + "--from=alice", + "--gas=auto", + "--fees=10000000000000000000000adv4tnt", + "--node=" + args.node, + "--chain-id=" + args.chain_id, + "--keyring-backend=test", + "--yes" + ] + + result = subprocess.run(cmd, capture_output=True, text=True) + if result.returncode != 0: + raise Exception(f"Failed to submit proposal: {result.stderr}") + # delete the temporary file + os.remove(tmp_file_path) + print("voting for affiliate parameters update") + time.sleep(5) + # vote for alice + voters = ["alice", "bob", "carl", "dave", "emily", "fiona", "greg", "henry", "ian", "jeff"] + proposal_id = get_proposal_id(args.node, args.chain_id) + for voter in voters: + vote_for(args.node, args.chain_id, proposal_id, voter) + + # wait for the proposal to pass + print("Waiting 2 minutes for proposal to pass") + time.sleep(120) + # check if the proposal passed + cmd = [ + "dydxprotocold", + "query", + "gov", + "proposal", + proposal_id, + "--node=" + args.node, + "--chain-id=" + args.chain_id + ] + with tempfile.NamedTemporaryFile(mode='w', suffix='.json') as tmp_file: + subprocess.run(cmd, stdout=tmp_file) + result = load_yml(tmp_file.name) + if result['proposal']['status'] == PROPOSAL_STATUS_PASSED: + print("proposal passed, affiliate parameters updated") + return True + else: + raise Exception("Failed to update affiliate parameters") + break + except Exception as e: + print(e) + print(f"got exception, retrying {i+1} time(s)") + + +if __name__ == "__main__": + main() diff --git a/protocol/scripts/genesis/sample_pregenesis.json b/protocol/scripts/genesis/sample_pregenesis.json index 6e78202357..d9bcb64415 100644 --- a/protocol/scripts/genesis/sample_pregenesis.json +++ b/protocol/scripts/genesis/sample_pregenesis.json @@ -3,6 +3,11 @@ "app_name": "dydxprotocold", "app_state": { "affiliates": { + "affiliate_parameters": { + "maximum_30d_affiliate_revenue_per_referred_user_quote_quantums": "10000000000", + "maximum_30d_attributable_volume_per_referred_user_quote_quantums": "100000000000000", + "referee_minimum_fee_tier_idx": 2 + }, "affiliate_tiers": { "tiers": [ { @@ -4091,7 +4096,11 @@ ] } }, +<<<<<<< HEAD "app_version": "7.0.0-dev0-149-g12cfb908b", +======= + "app_version": "9.0.0-64-g531bff2de", +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) "chain_id": "dydx-sample-1", "consensus": { "params": { diff --git a/protocol/testing/testnet/genesis.json b/protocol/testing/testnet/genesis.json index 3fe44a2f9b..5e00230942 100644 --- a/protocol/testing/testnet/genesis.json +++ b/protocol/testing/testnet/genesis.json @@ -36,6 +36,16 @@ } ] }, +<<<<<<< HEAD +======= + "affiliates": { + "affiliate_parameters": { + "maximum_30d_attributable_volume_per_referred_user_quote_quantums": "100000000000000", + "referee_minimum_fee_tier_idx": 2, + "maximum_30d_affiliate_revenue_per_referred_user_quote_quantums": "10000000000" + } + }, +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) "auth": { "params": { "max_memo_characters": "256", diff --git a/protocol/testutil/keeper/assets.go b/protocol/testutil/keeper/assets.go index bdf43259cc..d1c68c9068 100644 --- a/protocol/testutil/keeper/assets.go +++ b/protocol/testutil/keeper/assets.go @@ -82,7 +82,7 @@ func AssetsKeepers( affiliatesKeeper, _ := createAffiliatesKeeper(stateStore, db, cdc, statsKeeper, transientStoreKey, msgSenderEnabled) vaultKeeper, _ := createVaultKeeper(stateStore, db, cdc, transientStoreKey) feetiersKeeper, _ := createFeeTiersKeeper(stateStore, statsKeeper, vaultKeeper, affiliatesKeeper, db, cdc) - revShareKeeper, _, _ := createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper) + revShareKeeper, _, _ := createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper, statsKeeper) marketMapKeeper, _ := createMarketMapKeeper(stateStore, db, cdc) pricesKeeper, _, _, mockTimeProvider = createPricesKeeper( stateStore, diff --git a/protocol/testutil/keeper/clob.go b/protocol/testutil/keeper/clob.go index eeb19ca084..22e3722ed9 100644 --- a/protocol/testutil/keeper/clob.go +++ b/protocol/testutil/keeper/clob.go @@ -131,7 +131,14 @@ func NewClobKeepersTestContextWithUninitializedMemStore( db, cdc, ) - revShareKeeper, _, _ := createRevShareKeeper(stateStore, db, cdc, ks.AffiliatesKeeper, ks.FeeTiersKeeper) + revShareKeeper, _, _ := createRevShareKeeper( + stateStore, + db, + cdc, + ks.AffiliatesKeeper, + ks.FeeTiersKeeper, + ks.StatsKeeper, + ) ks.FeeTiersKeeper.SetRevShareKeeper(revShareKeeper) ks.AffiliatesKeeper.SetFeetiersKeeper(ks.FeeTiersKeeper) ks.MarketMapKeeper, _ = createMarketMapKeeper(stateStore, db, cdc) diff --git a/protocol/testutil/keeper/listing.go b/protocol/testutil/keeper/listing.go index 685f8082bd..5bbdf3abdd 100644 --- a/protocol/testutil/keeper/listing.go +++ b/protocol/testutil/keeper/listing.go @@ -97,7 +97,7 @@ func ListingKeepers( db, cdc, ) - revShareKeeper, _, _ := createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feeTiersKeeper) + revShareKeeper, _, _ := createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feeTiersKeeper, statsKeeper) marketMapKeeper, _ = createMarketMapKeeper(stateStore, db, cdc) pricesKeeper, _, _, mockTimeProvider = createPricesKeeper( stateStore, diff --git a/protocol/testutil/keeper/perpetuals.go b/protocol/testutil/keeper/perpetuals.go index aadc88b1f5..1472442b3c 100644 --- a/protocol/testutil/keeper/perpetuals.go +++ b/protocol/testutil/keeper/perpetuals.go @@ -91,7 +91,7 @@ func PerpetualsKeepersWithClobHelpers( affiliatesKeeper, _ := createAffiliatesKeeper(stateStore, db, cdc, statsKeeper, transientStoreKey, true) vaultKeeper, _ := createVaultKeeper(stateStore, db, cdc, transientStoreKey) feetiersKeeper, _ := createFeeTiersKeeper(stateStore, statsKeeper, vaultKeeper, affiliatesKeeper, db, cdc) - revShareKeeper, _, _ := createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper) + revShareKeeper, _, _ := createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper, statsKeeper) pc.MarketMapKeeper, _ = createMarketMapKeeper(stateStore, db, cdc) pc.PricesKeeper, _, pc.IndexPriceCache, pc.MockTimeProvider = createPricesKeeper( stateStore, diff --git a/protocol/testutil/keeper/prices.go b/protocol/testutil/keeper/prices.go index bad68af141..e5847c5819 100644 --- a/protocol/testutil/keeper/prices.go +++ b/protocol/testutil/keeper/prices.go @@ -73,7 +73,7 @@ func PricesKeepers(t testing.TB) ( affiliatesKeeper, _ := createAffiliatesKeeper(stateStore, db, cdc, statsKeeper, transientStoreKey, true) vaultKeeper, _ := createVaultKeeper(stateStore, db, cdc, transientStoreKey) feetiersKeeper, _ := createFeeTiersKeeper(stateStore, statsKeeper, vaultKeeper, affiliatesKeeper, db, cdc) - revShareKeeper, _, _ = createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper) + revShareKeeper, _, _ = createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper, statsKeeper) marketMapKeeper, _ = createMarketMapKeeper(stateStore, db, cdc) // Define necessary keepers here for unit tests keeper, storeKey, indexPriceCache, mockTimeProvider = diff --git a/protocol/testutil/keeper/revshare.go b/protocol/testutil/keeper/revshare.go index ea3b786d5d..c1f130cff4 100644 --- a/protocol/testutil/keeper/revshare.go +++ b/protocol/testutil/keeper/revshare.go @@ -10,6 +10,7 @@ import ( affiliateskeeper "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/keeper" feetierskeeper "github.com/dydxprotocol/v4-chain/protocol/x/feetiers/keeper" "github.com/dydxprotocol/v4-chain/protocol/x/revshare/types" + statskeeper "github.com/dydxprotocol/v4-chain/protocol/x/stats/keeper" storetypes "cosmossdk.io/store/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -58,7 +59,7 @@ func RevShareKeepers(t testing.TB) ( vaultKeeper, _ := createVaultKeeper(stateStore, db, cdc, transientStoreKey) feetiersKeeper, _ := createFeeTiersKeeper(stateStore, statsKeeper, vaultKeeper, affiliatesKeeper, db, cdc) keeper, storeKey, mockTimeProvider = - createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper) + createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper, statsKeeper) return []GenesisInitializer{keeper} }, @@ -73,6 +74,7 @@ func createRevShareKeeper( cdc *codec.ProtoCodec, affiliatesKeeper *affiliateskeeper.Keeper, feetiersKeeper *feetierskeeper.Keeper, + statsKeeper *statskeeper.Keeper, ) ( *keeper.Keeper, storetypes.StoreKey, @@ -88,6 +90,7 @@ func createRevShareKeeper( }, *affiliatesKeeper, *feetiersKeeper, + *statsKeeper, ) return k, storeKey, mockTimeProvider diff --git a/protocol/testutil/keeper/rewards.go b/protocol/testutil/keeper/rewards.go index d6c1aaf620..91e5f56e21 100644 --- a/protocol/testutil/keeper/rewards.go +++ b/protocol/testutil/keeper/rewards.go @@ -80,7 +80,7 @@ func RewardsKeepers( db, cdc, ) - revShareKeeper, _, _ := createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper) + revShareKeeper, _, _ := createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper, statsKeeper) marketMapKeeper, _ := createMarketMapKeeper(stateStore, db, cdc) pricesKeeper, _, _, _ = createPricesKeeper(stateStore, db, cdc, transientStoreKey, revShareKeeper, marketMapKeeper) // Mock time provider response for market creation. diff --git a/protocol/testutil/keeper/sending.go b/protocol/testutil/keeper/sending.go index 7a0dd77d58..76d8fe9b55 100644 --- a/protocol/testutil/keeper/sending.go +++ b/protocol/testutil/keeper/sending.go @@ -80,7 +80,7 @@ func SendingKeepersWithSubaccountsKeeper(t testing.TB, saKeeper types.Subaccount affiliatesKeeper, _ := createAffiliatesKeeper(stateStore, db, cdc, statsKeeper, transientStoreKey, true) vaultKeeper, _ := createVaultKeeper(stateStore, db, cdc, transientStoreKey) feetiersKeeper, _ := createFeeTiersKeeper(stateStore, statsKeeper, vaultKeeper, affiliatesKeeper, db, cdc) - revShareKeeper, _, _ := createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper) + revShareKeeper, _, _ := createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper, statsKeeper) marketMapKeeper, _ := createMarketMapKeeper(stateStore, db, cdc) ks.PricesKeeper, _, _, mockTimeProvider = createPricesKeeper( stateStore, diff --git a/protocol/testutil/keeper/subaccounts.go b/protocol/testutil/keeper/subaccounts.go index a0c14b3fc0..6ba71ec068 100644 --- a/protocol/testutil/keeper/subaccounts.go +++ b/protocol/testutil/keeper/subaccounts.go @@ -79,7 +79,7 @@ func SubaccountsKeepers(t testing.TB, msgSenderEnabled bool) ( affiliatesKeeper, _ = createAffiliatesKeeper(stateStore, db, cdc, statsKeeper, transientStoreKey, true) vaultKeeper, _ := createVaultKeeper(stateStore, db, cdc, transientStoreKey) feetiersKeeper, _ := createFeeTiersKeeper(stateStore, statsKeeper, vaultKeeper, affiliatesKeeper, db, cdc) - revShareKeeper, _, _ = createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper) + revShareKeeper, _, _ = createRevShareKeeper(stateStore, db, cdc, affiliatesKeeper, feetiersKeeper, statsKeeper) marketMapKeeper, _ := createMarketMapKeeper(stateStore, db, cdc) pricesKeeper, _, _, mockTimeProvider = createPricesKeeper( stateStore, diff --git a/protocol/x/affiliates/genesis.go b/protocol/x/affiliates/genesis.go index fd6c3d4ec7..0d27c8d586 100644 --- a/protocol/x/affiliates/genesis.go +++ b/protocol/x/affiliates/genesis.go @@ -12,6 +12,13 @@ func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) if err != nil { panic(err) } + + err = k.UpdateAffiliateParameters(ctx, &types.MsgUpdateAffiliateParameters{ + AffiliateParameters: genState.AffiliateParameters, + }) + if err != nil { + panic(err) + } } // ExportGenesis returns the module's exported genesis. @@ -21,7 +28,13 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { panic(err) } + affiliateParameters, err := k.GetAffiliateParameters(ctx) + if err != nil { + panic(err) + } + return &types.GenesisState{ - AffiliateTiers: affiliateTiers, + AffiliateTiers: affiliateTiers, + AffiliateParameters: affiliateParameters, } } diff --git a/protocol/x/affiliates/keeper/grpc_query.go b/protocol/x/affiliates/keeper/grpc_query.go index 84185382a8..b7040f7273 100644 --- a/protocol/x/affiliates/keeper/grpc_query.go +++ b/protocol/x/affiliates/keeper/grpc_query.go @@ -7,6 +7,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/dydxprotocol/v4-chain/protocol/dtypes" + "github.com/dydxprotocol/v4-chain/protocol/lib" "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/types" ) @@ -39,19 +40,16 @@ func (k Keeper) AffiliateInfo(c context.Context, } } - referredVolume, err := k.GetReferredVolume(ctx, req.GetAddress()) - if err != nil { - return nil, err - } - + userStats := k.statsKeeper.GetUserStats(ctx, addr.String()) + referredVolume := userStats.Affiliate_30DReferredVolumeQuoteQuantums stakedAmount := k.statsKeeper.GetStakedAmount(ctx, req.GetAddress()) return &types.AffiliateInfoResponse{ - IsWhitelisted: isWhitelisted, - Tier: tierLevel, - FeeSharePpm: feeSharePpm, - ReferredVolume: dtypes.NewIntFromBigInt(referredVolume), - StakedAmount: dtypes.NewIntFromBigInt(stakedAmount), + IsWhitelisted: isWhitelisted, + Tier: tierLevel, + FeeSharePpm: feeSharePpm, + StakedAmount: dtypes.NewIntFromBigInt(stakedAmount), + ReferredVolume_30DRolling: dtypes.NewIntFromBigInt(lib.BigU(referredVolume)), }, nil } diff --git a/protocol/x/affiliates/keeper/grpc_query_test.go b/protocol/x/affiliates/keeper/grpc_query_test.go index 23f0e35c93..dc01622dd1 100644 --- a/protocol/x/affiliates/keeper/grpc_query_test.go +++ b/protocol/x/affiliates/keeper/grpc_query_test.go @@ -29,11 +29,15 @@ func TestAffiliateInfo(t *testing.T) { Address: constants.AliceAccAddress.String(), }, res: &types.AffiliateInfoResponse{ - IsWhitelisted: false, - Tier: 0, - FeeSharePpm: types.DefaultAffiliateTiers.Tiers[0].TakerFeeSharePpm, - ReferredVolume: dtypes.NewIntFromUint64(types.DefaultAffiliateTiers.Tiers[0].ReqReferredVolumeQuoteQuantums), - StakedAmount: dtypes.NewIntFromUint64(uint64(types.DefaultAffiliateTiers.Tiers[0].ReqStakedWholeCoins) * 1e18), + IsWhitelisted: false, + Tier: 0, + FeeSharePpm: types.DefaultAffiliateTiers.Tiers[0].TakerFeeSharePpm, + ReferredVolume_30DRolling: dtypes.NewIntFromUint64( + types.DefaultAffiliateTiers.Tiers[0].ReqReferredVolumeQuoteQuantums, + ), + StakedAmount: dtypes.NewIntFromUint64( + uint64(types.DefaultAffiliateTiers.Tiers[0].ReqStakedWholeCoins) * 1e18, + ), }, setup: func(ctx sdk.Context, k keeper.Keeper, tApp *testapp.TestApp) { err := k.RegisterAffiliate(ctx, constants.BobAccAddress.String(), constants.AliceAccAddress.String()) @@ -59,11 +63,15 @@ func TestAffiliateInfo(t *testing.T) { Address: constants.AliceAccAddress.String(), }, res: &types.AffiliateInfoResponse{ - IsWhitelisted: false, - Tier: 0, - FeeSharePpm: types.DefaultAffiliateTiers.Tiers[0].TakerFeeSharePpm, - ReferredVolume: dtypes.NewIntFromUint64(types.DefaultAffiliateTiers.Tiers[0].ReqReferredVolumeQuoteQuantums), - StakedAmount: dtypes.NewIntFromUint64(uint64(types.DefaultAffiliateTiers.Tiers[0].ReqStakedWholeCoins) * 1e18), + IsWhitelisted: false, + Tier: 0, + FeeSharePpm: types.DefaultAffiliateTiers.Tiers[0].TakerFeeSharePpm, + ReferredVolume_30DRolling: dtypes.NewIntFromUint64( + types.DefaultAffiliateTiers.Tiers[0].ReqReferredVolumeQuoteQuantums, + ), + StakedAmount: dtypes.NewIntFromUint64( + uint64(types.DefaultAffiliateTiers.Tiers[0].ReqStakedWholeCoins) * 1e18, + ), }, setup: func(ctx sdk.Context, k keeper.Keeper, tApp *testapp.TestApp) { stakingKeeper := tApp.App.StakingKeeper @@ -96,11 +104,19 @@ func TestAffiliateInfo(t *testing.T) { Address: constants.AliceAccAddress.String(), }, res: &types.AffiliateInfoResponse{ +<<<<<<< HEAD IsWhitelisted: true, Tier: 0, FeeSharePpm: 120_000, ReferredVolume: dtypes.NewIntFromUint64(0), StakedAmount: dtypes.NewIntFromUint64(0), +======= + IsWhitelisted: true, + Tier: 4, + FeeSharePpm: 250_000, + ReferredVolume_30DRolling: dtypes.NewIntFromUint64(0), + StakedAmount: dtypes.NewIntFromUint64(0), +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) }, setup: func(ctx sdk.Context, k keeper.Keeper, tApp *testapp.TestApp) { err := k.RegisterAffiliate(ctx, constants.BobAccAddress.String(), constants.AliceAccAddress.String()) diff --git a/protocol/x/affiliates/keeper/keeper.go b/protocol/x/affiliates/keeper/keeper.go index 52cb1bfd3a..f64e09592c 100644 --- a/protocol/x/affiliates/keeper/keeper.go +++ b/protocol/x/affiliates/keeper/keeper.go @@ -10,7 +10,6 @@ import ( storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/dydxprotocol/v4-chain/protocol/dtypes" indexerevents "github.com/dydxprotocol/v4-chain/protocol/indexer/events" "github.com/dydxprotocol/v4-chain/protocol/indexer/indexer_manager" "github.com/dydxprotocol/v4-chain/protocol/lib" @@ -109,6 +108,7 @@ func (k Keeper) GetReferredBy(ctx sdk.Context, referee string) (string, bool) { return string(referredByPrefixStore.Get([]byte(referee))), true } +<<<<<<< HEAD // AddReferredVolume adds the referred volume from a block to the affiliate's referred volume. func (k Keeper) AddReferredVolume( ctx sdk.Context, @@ -157,6 +157,8 @@ func (k Keeper) GetReferredVolume(ctx sdk.Context, affiliateAddr string) (*big.I return referredVolume.BigInt(), nil } +======= +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) // GetAllAffiliateTiers returns all affiliate tiers. func (k Keeper) GetAllAffiliateTiers(ctx sdk.Context) (types.AffiliateTiers, error) { store := ctx.KVStore(k.storeKey) @@ -225,9 +227,27 @@ func (k Keeper) GetTierForAffiliate( numTiers := uint32(len(tiers)) maxTierLevel := numTiers - 1 currentTier := uint32(0) +<<<<<<< HEAD referredVolume, err := k.GetReferredVolume(ctx, affiliateAddr) if err != nil { return 0, 0, err +======= + + // Check whether the address is overridden, if it is then set the + // affiliate tier to the max + if affiliateOverrides != nil { + if _, exists := affiliateOverrides[affiliateAddr]; exists { + feeSharePpm = affiliateTiers.Tiers[maxTierLevel].TakerFeeSharePpm + return uint32(maxTierLevel), feeSharePpm, nil + } + } + + // Get the affiliate revenue generated in the last 30d + userStats := k.statsKeeper.GetUserStats(ctx, affiliateAddr) + referredVolume := big.NewInt(0) + if userStats != nil { + referredVolume = new(big.Int).SetUint64(userStats.Affiliate_30DReferredVolumeQuoteQuantums) +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) } for index, tier := range tiers { @@ -292,6 +312,7 @@ func (k Keeper) GetIndexerEventManager() indexer_manager.IndexerEventManager { return k.indexerEventManager } +// Deprecated: This is deprecated in favor of AffiliateOverride. func (k Keeper) GetAffiliateWhitelistMap(ctx sdk.Context) (map[string]uint32, error) { affiliateWhitelist, err := k.GetAffiliateWhitelist(ctx) if err != nil { @@ -306,6 +327,7 @@ func (k Keeper) GetAffiliateWhitelistMap(ctx sdk.Context) (map[string]uint32, er return affiliateWhitelistMap, nil } +// Deprecated: This is deprecated in favor of AffiliateOverride. func (k Keeper) SetAffiliateWhitelist(ctx sdk.Context, whitelist types.AffiliateWhitelist) error { store := ctx.KVStore(k.storeKey) addressSet := make(map[string]bool) @@ -334,6 +356,7 @@ func (k Keeper) SetAffiliateWhitelist(ctx sdk.Context, whitelist types.Affiliate return nil } +// DO NOT USE: This will be deprecated soon. func (k Keeper) GetAffiliateWhitelist(ctx sdk.Context) (types.AffiliateWhitelist, error) { store := ctx.KVStore(k.storeKey) affiliateWhitelistBytes := store.Get([]byte(types.AffiliateWhitelistKey)) @@ -350,6 +373,115 @@ func (k Keeper) GetAffiliateWhitelist(ctx sdk.Context) (types.AffiliateWhitelist return affiliateWhitelist, nil } +<<<<<<< HEAD +======= +func (k Keeper) UpdateAffiliateParameters( + ctx sdk.Context, + msg *types.MsgUpdateAffiliateParameters, +) error { + store := ctx.KVStore(k.storeKey) + + affiliateParametersBytes, err := k.cdc.Marshal(&msg.AffiliateParameters) + if err != nil { + return err + } + store.Set([]byte(types.AffiliateParametersKey), affiliateParametersBytes) + + return nil +} + +func (k Keeper) GetAffiliateParameters(ctx sdk.Context) (types.AffiliateParameters, error) { + store := ctx.KVStore(k.storeKey) + affiliateParametersBytes := store.Get([]byte(types.AffiliateParametersKey)) + if affiliateParametersBytes == nil { + return types.AffiliateParameters{}, nil + } + affiliateParameters := types.AffiliateParameters{} + err := k.cdc.Unmarshal(affiliateParametersBytes, &affiliateParameters) + if err != nil { + return types.AffiliateParameters{}, err + } + return affiliateParameters, nil +} + +func (k Keeper) SetAffiliateOverrides(ctx sdk.Context, overrides types.AffiliateOverrides) error { + store := ctx.KVStore(k.storeKey) + affiliateOverridesBytes, err := k.cdc.Marshal(&overrides) + if err != nil { + return err + } + store.Set([]byte(types.AffiliateOverridesKey), affiliateOverridesBytes) + return nil +} + +func (k Keeper) GetAffiliateOverrides(ctx sdk.Context) (types.AffiliateOverrides, error) { + store := ctx.KVStore(k.storeKey) + affiliateOverridesBytes := store.Get([]byte(types.AffiliateOverridesKey)) + if affiliateOverridesBytes == nil { + return types.AffiliateOverrides{}, nil + } + affiliateOverrides := types.AffiliateOverrides{} + err := k.cdc.Unmarshal(affiliateOverridesBytes, &affiliateOverrides) + if err != nil { + return types.AffiliateOverrides{}, err + } + return affiliateOverrides, nil +} + +func (k Keeper) GetAffiliateOverridesMap(ctx sdk.Context) (map[string]bool, error) { + affiliateOverrides, err := k.GetAffiliateOverrides(ctx) + if err != nil { + return nil, err + } + affiliateOverridesMap := make(map[string]bool) + for _, address := range affiliateOverrides.Addresses { + affiliateOverridesMap[address] = true + } + return affiliateOverridesMap, nil +} + +func (k Keeper) addReferredVolumeIfQualified( + ctx sdk.Context, + referee string, + referrer string, + volume uint64, + affiliateParams types.AffiliateParameters, + previouslyAttributedVolume map[string]uint64, +) error { + // Get the user stats from the referee + refereeUserStats := k.statsKeeper.GetUserStats(ctx, referee) + if refereeUserStats == nil { + return errorsmod.Wrapf(types.ErrUpdatingAffiliateReferredVolume, + "referee %s, refereeUserStats is nil", referee) + } + + previousVolume := (refereeUserStats.TakerNotional + refereeUserStats.MakerNotional + + previouslyAttributedVolume[referee]) + + // If parameter is 0 then no limit is applied + cap := affiliateParams.Maximum_30DAttributableVolumePerReferredUserQuoteQuantums + if cap != 0 { + if previousVolume >= cap { + volume = 0 + } else if previousVolume+volume > cap { + // Remainder of the volume to get them to the cap + volume = cap - previousVolume + } + } + previouslyAttributedVolume[referee] += volume + + // Add the volume to the referrer on their 30d rolling window + if volume > 0 { + affiliateUserStats := k.statsKeeper.GetUserStats(ctx, referrer) + if affiliateUserStats != nil { + affiliateUserStats.Affiliate_30DReferredVolumeQuoteQuantums += volume + } + k.statsKeeper.SetUserStats(ctx, referrer, affiliateUserStats) + } + return nil +} + +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) func (k Keeper) AggregateAffiliateReferredVolumeForFills( ctx sdk.Context, ) error { diff --git a/protocol/x/affiliates/keeper/keeper_test.go b/protocol/x/affiliates/keeper/keeper_test.go index 5a889d1f9d..e745218ce2 100644 --- a/protocol/x/affiliates/keeper/keeper_test.go +++ b/protocol/x/affiliates/keeper/keeper_test.go @@ -129,6 +129,7 @@ func TestGetReferredByEmptyAffiliate(t *testing.T) { require.Equal(t, "", affiliate) } +<<<<<<< HEAD func TestAddReferredVolume(t *testing.T) { tApp := testapp.NewTestAppBuilder(t).Build() ctx := tApp.InitChain() @@ -167,6 +168,8 @@ func TestGetReferredVolumeInvalidAffiliate(t *testing.T) { require.False(t, exists) } +======= +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) func TestGetTakerFeeShareViaReferredVolume(t *testing.T) { tApp := testapp.NewTestAppBuilder(t).Build() ctx := tApp.InitChain() @@ -177,7 +180,14 @@ func TestGetTakerFeeShareViaReferredVolume(t *testing.T) { require.NoError(t, err) stakingKeeper := tApp.App.StakingKeeper - err = stakingKeeper.SetDelegation(ctx, + require.NoError(t, k.UpdateAffiliateParameters(ctx, &types.MsgUpdateAffiliateParameters{ + Authority: constants.GovAuthority, + AffiliateParameters: types.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 100_000_000_000_000, + }, + })) + + require.NoError(t, stakingKeeper.SetDelegation(ctx, stakingtypes.NewDelegation(constants.AliceAccAddress.String(), constants.AliceValAddress.String(), math.LegacyNewDecFromBigInt( new(big.Int).Mul( @@ -186,8 +196,7 @@ func TestGetTakerFeeShareViaReferredVolume(t *testing.T) { ), ), ), - ) - require.NoError(t, err) + )) // Register affiliate and referee affiliate := constants.AliceAccAddress.String() @@ -202,11 +211,11 @@ func TestGetTakerFeeShareViaReferredVolume(t *testing.T) { require.Equal(t, affiliate, affiliateAddr) require.Equal(t, types.DefaultAffiliateTiers.Tiers[0].TakerFeeSharePpm, feeSharePpm) - // Add more referred volume to upgrade tier - err = k.AddReferredVolume(ctx, affiliate, big.NewInt( - int64(types.DefaultAffiliateTiers.Tiers[1].ReqReferredVolumeQuoteQuantums), - )) - require.NoError(t, err) + tApp.App.StatsKeeper.SetUserStats(ctx, affiliate, &statstypes.UserStats{ + TakerNotional: 100_000_000_000_000, + MakerNotional: 100_000_000_000_000, + Affiliate_30DReferredVolumeQuoteQuantums: 1_000_000_000_001, + }) // Get updated taker fee share for referee affiliateAddr, feeSharePpm, exists, err = k.GetTakerFeeShare(ctx, referee, map[string]uint32{}) @@ -299,9 +308,11 @@ func TestGetTierForAffiliate_VolumeAndStake(t *testing.T) { err = k.RegisterAffiliate(ctx, referee, affiliate) require.NoError(t, err) - reqReferredVolume := big.NewInt(int64(affiliateTiers.Tiers[2].ReqReferredVolumeQuoteQuantums)) - err = k.AddReferredVolume(ctx, affiliate, reqReferredVolume) - require.NoError(t, err) + tApp.App.StatsKeeper.SetUserStats(ctx, affiliate, &statstypes.UserStats{ + TakerNotional: 100_000_000_000_000, + MakerNotional: 100_000_000_000_000, + Affiliate_30DReferredVolumeQuoteQuantums: affiliateTiers.Tiers[2].ReqReferredVolumeQuoteQuantums, + }) stakedAmount := new(big.Int).Mul( big.NewInt(int64(affiliateTiers.Tiers[3].ReqStakedWholeCoins)), @@ -700,6 +711,7 @@ func TestAggregateAffiliateReferredVolumeForFills(t *testing.T) { referee2 := constants.DaveAccAddress.String() maker := constants.CarlAccAddress.String() testCases := []struct { +<<<<<<< HEAD name string referrals int expectedVolume *big.Int @@ -708,6 +720,22 @@ func TestAggregateAffiliateReferredVolumeForFills(t *testing.T) { { name: "0 referrals", expectedVolume: big.NewInt(0), +======= + name string + referrals int + expectedVolume *big.Int + referreeAddressesToVerify []string + expectedCommissions []*big.Int + expectedReferrer []string + expectedAttributedVolume []uint64 + setup func(t *testing.T, ctx sdk.Context, k *keeper.Keeper, statsKeeper *statskeeper.Keeper) + }{ + { + name: "0 referrals", + expectedVolume: big.NewInt(0), + expectedReferrer: []string{}, + expectedAttributedVolume: []uint64{}, +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) setup: func(t *testing.T, ctx sdk.Context, k *keeper.Keeper, statsKeeper *statskeeper.Keeper) { statsKeeper.SetBlockStats(ctx, &statstypes.BlockStats{ Fills: []*statstypes.BlockStats_Fill{ @@ -724,9 +752,27 @@ func TestAggregateAffiliateReferredVolumeForFills(t *testing.T) { name: "1 referral", referrals: 1, expectedVolume: big.NewInt(100_000_000_000), +<<<<<<< HEAD +======= + expectedReferrer: []string{ + affiliate, + }, + expectedAttributedVolume: []uint64{ + 200_000_000_000, + }, +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) setup: func(t *testing.T, ctx sdk.Context, k *keeper.Keeper, statsKeeper *statskeeper.Keeper) { err := k.RegisterAffiliate(ctx, referee1, affiliate) require.NoError(t, err) + + // They are close to the maximum of attributable volume so we should not add more than expected + statsKeeper.SetUserStats(ctx, affiliate, &statstypes.UserStats{ + TakerNotional: 0, + MakerNotional: 0, + Affiliate_30DRevenueGeneratedQuantums: 0, + Affiliate_30DReferredVolumeQuoteQuantums: 100_000_000_000, + }) + statsKeeper.SetBlockStats(ctx, &statstypes.BlockStats{ Fills: []*statstypes.BlockStats_Fill{ { @@ -739,14 +785,35 @@ func TestAggregateAffiliateReferredVolumeForFills(t *testing.T) { }, }, { +<<<<<<< HEAD name: "2 referrals", referrals: 2, expectedVolume: big.NewInt(300_000_000_000), +======= + name: "2 referrals, no limit", + referrals: 2, + expectedVolume: big.NewInt(300_000_000_000), + expectedReferrer: []string{ + affiliate, + }, + expectedAttributedVolume: []uint64{ + 300_000_000_000, + }, +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) setup: func(t *testing.T, ctx sdk.Context, k *keeper.Keeper, statsKeeper *statskeeper.Keeper) { err := k.RegisterAffiliate(ctx, referee1, affiliate) require.NoError(t, err) err = k.RegisterAffiliate(ctx, referee2, affiliate) require.NoError(t, err) + + // They are close to the maximum of attributable volume so we should not add more than expected + statsKeeper.SetUserStats(ctx, affiliate, &statstypes.UserStats{ + TakerNotional: 0, + MakerNotional: 0, + Affiliate_30DRevenueGeneratedQuantums: 0, + Affiliate_30DReferredVolumeQuoteQuantums: 0, + }) + statsKeeper.SetBlockStats(ctx, &statstypes.BlockStats{ Fills: []*statstypes.BlockStats_Fill{ { @@ -767,6 +834,15 @@ func TestAggregateAffiliateReferredVolumeForFills(t *testing.T) { name: "2 referrals, maker also referred", referrals: 2, expectedVolume: big.NewInt(600_000_000_000), +<<<<<<< HEAD +======= + expectedReferrer: []string{ + affiliate, + }, + expectedAttributedVolume: []uint64{ + 600_000_000_000, + }, +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) setup: func(t *testing.T, ctx sdk.Context, k *keeper.Keeper, statsKeeper *statskeeper.Keeper) { err := k.RegisterAffiliate(ctx, referee1, affiliate) require.NoError(t, err) @@ -788,15 +864,43 @@ func TestAggregateAffiliateReferredVolumeForFills(t *testing.T) { }, }, }) +<<<<<<< HEAD +======= + err = k.UpdateAffiliateParameters(ctx, &types.MsgUpdateAffiliateParameters{ + Authority: constants.GovAuthority, + AffiliateParameters: types.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 800_000_000_000, + }, + }) + require.NoError(t, err) +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) }, }, { name: "2 referrals, takers not referred, maker referred", referrals: 2, expectedVolume: big.NewInt(300_000_000_000), +<<<<<<< HEAD +======= + expectedReferrer: []string{ + affiliate, + }, + expectedAttributedVolume: []uint64{ + 200_000_000_000, + }, +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) setup: func(t *testing.T, ctx sdk.Context, k *keeper.Keeper, statsKeeper *statskeeper.Keeper) { err := k.RegisterAffiliate(ctx, maker, affiliate) require.NoError(t, err) + + // They are close to the maximum of attributable volume so we should not add more than expected + statsKeeper.SetUserStats(ctx, affiliate, &statstypes.UserStats{ + TakerNotional: 0, + MakerNotional: 0, + Affiliate_30DRevenueGeneratedQuantums: 0, + Affiliate_30DReferredVolumeQuoteQuantums: 0, + }) + statsKeeper.SetBlockStats(ctx, &statstypes.BlockStats{ Fills: []*statstypes.BlockStats_Fill{ { @@ -805,12 +909,220 @@ func TestAggregateAffiliateReferredVolumeForFills(t *testing.T) { Notional: 100_000_000_000, }, { +<<<<<<< HEAD Taker: referee2, Maker: maker, Notional: 200_000_000_000, }, }, }) +======= + Taker: referee2, + Maker: maker, + Notional: 100_000_000_000, + AffiliateFeeGeneratedQuantums: 2_000_000_000, + }, + }, + }) + err = k.UpdateAffiliateParameters(ctx, &types.MsgUpdateAffiliateParameters{ + Authority: constants.GovAuthority, + AffiliateParameters: types.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 300_000_000_000, + }, + }) + require.NoError(t, err) + }, + }, + { + name: "2 referrals, reached maximum attributable revenue", + referrals: 2, + expectedVolume: big.NewInt(300_000_000_000), + expectedReferrer: []string{ + affiliate, + }, + expectedAttributedVolume: []uint64{ + 80_000_000_000, + }, + setup: func(t *testing.T, ctx sdk.Context, k *keeper.Keeper, statsKeeper *statskeeper.Keeper) { + err := k.RegisterAffiliate(ctx, referee1, affiliate) + require.NoError(t, err) + err = k.RegisterAffiliate(ctx, referee2, affiliate) + require.NoError(t, err) + + // They are close to the maximum of attributable volume so we should not add more than expected + statsKeeper.SetUserStats(ctx, affiliate, &statstypes.UserStats{ + TakerNotional: 0, + MakerNotional: 0, + Affiliate_30DRevenueGeneratedQuantums: 0, + Affiliate_30DReferredVolumeQuoteQuantums: 0, + }) + + // Maximum volume was reached per affiliate, so we should not add any attributable volume + statsKeeper.SetUserStats(ctx, referee1, &statstypes.UserStats{ + TakerNotional: 150_000_000_000, + MakerNotional: 100_000_000_000, + }) + statsKeeper.SetUserStats(ctx, referee2, &statstypes.UserStats{ + TakerNotional: 150_000_000_000, + MakerNotional: 100_000_000_000, + }) + + statsKeeper.SetBlockStats(ctx, &statstypes.BlockStats{ + Fills: []*statstypes.BlockStats_Fill{ + { + Taker: referee1, + Maker: maker, + Notional: 100_000_000_000, + AffiliateFeeGeneratedQuantums: 1_000_000_000, + }, + { + Taker: referee2, + Maker: maker, + Notional: 200_000_000_000, + AffiliateFeeGeneratedQuantums: 2_000_000_000, + }, + }, + }) + err = k.UpdateAffiliateParameters(ctx, &types.MsgUpdateAffiliateParameters{ + Authority: constants.GovAuthority, + AffiliateParameters: types.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 290_000_000_000, + }, + }) + require.NoError(t, err) + }, + }, + { + name: "2 referrals, test limits of attributable revenue", + referrals: 2, + expectedVolume: big.NewInt(300_000_000_000), + expectedReferrer: []string{ + affiliate, + }, + expectedAttributedVolume: []uint64{ + 200_000_000_000, + }, + setup: func(t *testing.T, ctx sdk.Context, k *keeper.Keeper, statsKeeper *statskeeper.Keeper) { + err := k.RegisterAffiliate(ctx, referee1, affiliate) + require.NoError(t, err) + err = k.RegisterAffiliate(ctx, referee2, affiliate) + require.NoError(t, err) + + // They are close to the maximum of attributable volume so we should not add more than expected + statsKeeper.SetUserStats(ctx, affiliate, &statstypes.UserStats{ + TakerNotional: 0, + MakerNotional: 0, + Affiliate_30DRevenueGeneratedQuantums: 0, + Affiliate_30DReferredVolumeQuoteQuantums: 0, + }) + + // They are close to the maximum of attributable volume so we should not add more than expected + statsKeeper.SetUserStats(ctx, referee1, &statstypes.UserStats{ + TakerNotional: 50_000_000_000, + MakerNotional: 100_000_000_000, + Affiliate_30DRevenueGeneratedQuantums: 1_000_000_000, + }) + statsKeeper.SetUserStats(ctx, referee2, &statstypes.UserStats{ + TakerNotional: 50_000_000_000, + MakerNotional: 100_000_000_000, + Affiliate_30DRevenueGeneratedQuantums: 1_000_000_000, + }) + + statsKeeper.SetBlockStats(ctx, &statstypes.BlockStats{ + Fills: []*statstypes.BlockStats_Fill{ + { + Taker: referee1, + Maker: maker, + Notional: 100_000_000_000, + AffiliateFeeGeneratedQuantums: 1_000_000_000, + }, + { + Taker: referee2, + Maker: maker, + Notional: 200_000_000_000, + AffiliateFeeGeneratedQuantums: 2_000_000_000, + }, + }, + }) + err = k.UpdateAffiliateParameters(ctx, &types.MsgUpdateAffiliateParameters{ + Authority: constants.GovAuthority, + AffiliateParameters: types.AffiliateParameters{ + // Each affiliate can only generate 250_000_000_000 quantums of attributable revenue on a 30d window + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 250_000_000_000, + }, + }) + require.NoError(t, err) + }, + }, + { + name: "maker is also affiliate, make sure attributed volume doesn't exceed max per user", + referrals: 2, + expectedVolume: big.NewInt(600_000_000_000), + expectedReferrer: []string{ + affiliate, + }, + expectedAttributedVolume: []uint64{ + 350_000_000_000, + }, + setup: func(t *testing.T, ctx sdk.Context, k *keeper.Keeper, statsKeeper *statskeeper.Keeper) { + err := k.RegisterAffiliate(ctx, referee1, affiliate) + require.NoError(t, err) + err = k.RegisterAffiliate(ctx, referee2, affiliate) + require.NoError(t, err) + err = k.RegisterAffiliate(ctx, maker, affiliate) + require.NoError(t, err) + + // They are close to the maximum of attributable volume so we should not add more than expected + statsKeeper.SetUserStats(ctx, affiliate, &statstypes.UserStats{ + TakerNotional: 0, + MakerNotional: 0, + Affiliate_30DRevenueGeneratedQuantums: 0, + Affiliate_30DReferredVolumeQuoteQuantums: 0, + }) + // Starts with 150M volume + statsKeeper.SetUserStats(ctx, referee1, &statstypes.UserStats{ + TakerNotional: 50_000_000_000, + MakerNotional: 100_000_000_000, + Affiliate_30DRevenueGeneratedQuantums: 1_000_000_000, + }) + // starts with 150M volume + statsKeeper.SetUserStats(ctx, referee2, &statstypes.UserStats{ + TakerNotional: 50_000_000_000, + MakerNotional: 100_000_000_000, + Affiliate_30DRevenueGeneratedQuantums: 1_000_000_000, + }) + // Starts with 100M volume + statsKeeper.SetUserStats(ctx, maker, &statstypes.UserStats{ + TakerNotional: 50_000_000_000, + MakerNotional: 50_000_000_000, + Affiliate_30DRevenueGeneratedQuantums: 1_000_000_000, + }) + + statsKeeper.SetBlockStats(ctx, &statstypes.BlockStats{ + Fills: []*statstypes.BlockStats_Fill{ + { + Taker: referee1, + Maker: maker, + Notional: 100_000_000_000, + AffiliateFeeGeneratedQuantums: 1_000_000_000, + }, + { + Taker: referee2, + Maker: maker, + Notional: 200_000_000_000, + AffiliateFeeGeneratedQuantums: 2_000_000_000, + }, + }, + }) + err = k.UpdateAffiliateParameters(ctx, &types.MsgUpdateAffiliateParameters{ + Authority: constants.GovAuthority, + AffiliateParameters: types.AffiliateParameters{ + // Each affiliate can only generate 250_000_000_000 quantums of attributable revenue on a 30d window + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 250_000_000_000, + }, + }) + require.NoError(t, err) +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) }, }, } @@ -830,9 +1142,18 @@ func TestAggregateAffiliateReferredVolumeForFills(t *testing.T) { err = k.AggregateAffiliateReferredVolumeForFills(ctx) require.NoError(t, err) +<<<<<<< HEAD referredVolume, err := k.GetReferredVolume(ctx, affiliate) require.NoError(t, err) require.Equal(t, tc.expectedVolume, referredVolume) +======= + for idx := range tc.expectedReferrer { + referrer := tc.expectedReferrer[idx] + referrerUser := statsKeeper.GetUserStats(ctx, referrer) + require.NoError(t, err) + require.Equal(t, tc.expectedAttributedVolume[idx], referrerUser.Affiliate_30DReferredVolumeQuoteQuantums) + } +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) }) } } @@ -847,3 +1168,53 @@ func TestGetTierForAffiliateEmptyTiers(t *testing.T) { require.Equal(t, uint32(0), tierLevel) require.Equal(t, uint32(0), feeSharePpm) } +<<<<<<< HEAD +======= + +func TestUpdateAffiliateParameters(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + ctx := tApp.InitChain() + k := tApp.App.AffiliatesKeeper + + err := k.UpdateAffiliateParameters(ctx, &types.MsgUpdateAffiliateParameters{ + Authority: constants.GovAuthority, + AffiliateParameters: types.DefaultAffiliateParameters, + }) + require.NoError(t, err) + + affiliateParameters, err := k.GetAffiliateParameters(ctx) + require.NoError(t, err) + require.Equal( + t, + uint64(100_000_000_000_000), + affiliateParameters.GetMaximum_30DAttributableVolumePerReferredUserQuoteQuantums(), + ) + require.Equal(t, uint32(2), affiliateParameters.GetRefereeMinimumFeeTierIdx()) + require.Equal( + t, + uint64(10_000_000_000), + affiliateParameters.GetMaximum_30DAffiliateRevenuePerReferredUserQuoteQuantums(), + ) +} + +func TestGetTierForAffiliateOverrides(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + ctx := tApp.InitChain() + k := tApp.App.AffiliatesKeeper + + err := k.UpdateAffiliateTiers(ctx, types.DefaultAffiliateTiers) + require.NoError(t, err) + + tierLevel, feeSharePpm, err := k.GetTierForAffiliate(ctx, constants.AliceAccAddress.String(), map[string]bool{}) + require.NoError(t, err) + require.Equal(t, uint32(3), tierLevel) + require.Equal(t, uint32(150_000), feeSharePpm) + + tierLevel, feeSharePpm, err = k.GetTierForAffiliate(ctx, constants.AliceAccAddress.String(), map[string]bool{ + constants.AliceAccAddress.String(): true, + }) + require.NoError(t, err) + require.Equal(t, uint32(4), tierLevel) + require.Equal(t, uint32(250_000), feeSharePpm) +} +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) diff --git a/protocol/x/affiliates/types/affiliates.pb.go b/protocol/x/affiliates/types/affiliates.pb.go index 6601119888..86b81184bc 100644 --- a/protocol/x/affiliates/types/affiliates.pb.go +++ b/protocol/x/affiliates/types/affiliates.pb.go @@ -236,6 +236,122 @@ func (m *AffiliateWhitelist_Tier) GetTakerFeeSharePpm() uint32 { return 0 } +<<<<<<< HEAD +======= +// AffiliateParameters defines the parameters for the affiliate program. +type AffiliateParameters struct { + // Maximum attributable volume for a referred user in a 30d rolling window in + // notional + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums uint64 `protobuf:"varint,1,opt,name=maximum_30d_attributable_volume_per_referred_user_quote_quantums,json=maximum30dAttributableVolumePerReferredUserQuoteQuantums,proto3" json:"maximum_30d_attributable_volume_per_referred_user_quote_quantums,omitempty"` + // Referred user automatically gets set to this fee tier + RefereeMinimumFeeTierIdx uint32 `protobuf:"varint,2,opt,name=referee_minimum_fee_tier_idx,json=refereeMinimumFeeTierIdx,proto3" json:"referee_minimum_fee_tier_idx,omitempty"` + // Maximum affiliate revenue for a referred user in a 30d rolling window in + // quote quantums + Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums uint64 `protobuf:"varint,3,opt,name=maximum_30d_affiliate_revenue_per_referred_user_quote_quantums,json=maximum30dAffiliateRevenuePerReferredUserQuoteQuantums,proto3" json:"maximum_30d_affiliate_revenue_per_referred_user_quote_quantums,omitempty"` +} + +func (m *AffiliateParameters) Reset() { *m = AffiliateParameters{} } +func (m *AffiliateParameters) String() string { return proto.CompactTextString(m) } +func (*AffiliateParameters) ProtoMessage() {} +func (*AffiliateParameters) Descriptor() ([]byte, []int) { + return fileDescriptor_7de5ba9c426e9350, []int{2} +} +func (m *AffiliateParameters) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AffiliateParameters) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AffiliateParameters.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AffiliateParameters) XXX_Merge(src proto.Message) { + xxx_messageInfo_AffiliateParameters.Merge(m, src) +} +func (m *AffiliateParameters) XXX_Size() int { + return m.Size() +} +func (m *AffiliateParameters) XXX_DiscardUnknown() { + xxx_messageInfo_AffiliateParameters.DiscardUnknown(m) +} + +var xxx_messageInfo_AffiliateParameters proto.InternalMessageInfo + +func (m *AffiliateParameters) GetMaximum_30DAttributableVolumePerReferredUserQuoteQuantums() uint64 { + if m != nil { + return m.Maximum_30DAttributableVolumePerReferredUserQuoteQuantums + } + return 0 +} + +func (m *AffiliateParameters) GetRefereeMinimumFeeTierIdx() uint32 { + if m != nil { + return m.RefereeMinimumFeeTierIdx + } + return 0 +} + +func (m *AffiliateParameters) GetMaximum_30DAffiliateRevenuePerReferredUserQuoteQuantums() uint64 { + if m != nil { + return m.Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums + } + return 0 +} + +// AffiliateOverrides defines the affiliate whitelist. +type AffiliateOverrides struct { + // List of unique whitelisted addresses. + // These are automatically put at the maximum affiliate tier + Addresses []string `protobuf:"bytes,1,rep,name=addresses,proto3" json:"addresses,omitempty"` +} + +func (m *AffiliateOverrides) Reset() { *m = AffiliateOverrides{} } +func (m *AffiliateOverrides) String() string { return proto.CompactTextString(m) } +func (*AffiliateOverrides) ProtoMessage() {} +func (*AffiliateOverrides) Descriptor() ([]byte, []int) { + return fileDescriptor_7de5ba9c426e9350, []int{3} +} +func (m *AffiliateOverrides) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AffiliateOverrides) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AffiliateOverrides.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *AffiliateOverrides) XXX_Merge(src proto.Message) { + xxx_messageInfo_AffiliateOverrides.Merge(m, src) +} +func (m *AffiliateOverrides) XXX_Size() int { + return m.Size() +} +func (m *AffiliateOverrides) XXX_DiscardUnknown() { + xxx_messageInfo_AffiliateOverrides.DiscardUnknown(m) +} + +var xxx_messageInfo_AffiliateOverrides proto.InternalMessageInfo + +func (m *AffiliateOverrides) GetAddresses() []string { + if m != nil { + return m.Addresses + } + return nil +} + +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) func init() { proto.RegisterType((*AffiliateTiers)(nil), "dydxprotocol.affiliates.AffiliateTiers") proto.RegisterType((*AffiliateTiers_Tier)(nil), "dydxprotocol.affiliates.AffiliateTiers.Tier") @@ -248,6 +364,7 @@ func init() { } var fileDescriptor_7de5ba9c426e9350 = []byte{ +<<<<<<< HEAD // 384 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x92, 0x4f, 0x6b, 0xe2, 0x40, 0x18, 0xc6, 0x33, 0xea, 0x2e, 0x38, 0xcb, 0x2e, 0x4b, 0x5c, 0x76, 0x45, 0x96, 0xac, 0x78, 0xca, @@ -273,6 +390,45 @@ var fileDescriptor_7de5ba9c426e9350 = []byte{ 0xa1, 0x5d, 0xfd, 0x1d, 0x52, 0x19, 0x66, 0x03, 0xcb, 0x63, 0xb1, 0xbd, 0xf7, 0x9a, 0xc6, 0x7f, 0x9a, 0x5e, 0xe8, 0xd2, 0x91, 0xbd, 0x51, 0x26, 0xbb, 0x2f, 0x4c, 0x4e, 0x13, 0x10, 0x83, 0xf7, 0x39, 0x6c, 0xbd, 0x04, 0x00, 0x00, 0xff, 0xff, 0xc7, 0x32, 0xb9, 0x71, 0x89, 0x02, 0x00, 0x00, +======= + // 561 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x41, 0x6b, 0x13, 0x41, + 0x14, 0xce, 0xa6, 0x51, 0xe8, 0x88, 0x22, 0x9b, 0xa2, 0x31, 0x94, 0xb5, 0xe4, 0x94, 0x83, 0xd9, + 0x04, 0x23, 0xc5, 0x83, 0x14, 0x13, 0xa1, 0xa8, 0x54, 0x4c, 0x37, 0xda, 0x82, 0x97, 0x61, 0x92, + 0x7d, 0x49, 0x06, 0x77, 0x76, 0x36, 0x33, 0xb3, 0x31, 0xbd, 0x78, 0xf0, 0x17, 0x08, 0xfe, 0x15, + 0xd1, 0xbf, 0xd0, 0x63, 0xf1, 0x24, 0x1e, 0x44, 0x92, 0x3f, 0x22, 0x33, 0xbb, 0x49, 0x36, 0xd8, + 0xa8, 0x97, 0x61, 0xf6, 0x7d, 0xef, 0x7d, 0xf3, 0xbe, 0xf7, 0xcd, 0x0e, 0xaa, 0xfa, 0x67, 0xfe, + 0x34, 0x12, 0x5c, 0xf1, 0x3e, 0x0f, 0xea, 0x64, 0x30, 0xa0, 0x01, 0x25, 0x0a, 0x64, 0x66, 0xeb, + 0x1a, 0xd8, 0xbe, 0x9d, 0xcd, 0x74, 0x57, 0x70, 0xf9, 0x4e, 0x9f, 0x4b, 0xc6, 0x25, 0x36, 0x58, + 0x3d, 0xf9, 0x48, 0x6a, 0xca, 0x3b, 0x43, 0x3e, 0xe4, 0x49, 0x5c, 0xef, 0x92, 0x68, 0xe5, 0x53, + 0x1e, 0xdd, 0x68, 0x2d, 0xea, 0x5f, 0x51, 0x10, 0xd2, 0x7e, 0x8a, 0xae, 0x28, 0xbd, 0x29, 0x59, + 0x7b, 0x5b, 0xd5, 0x6b, 0xf7, 0xef, 0xb9, 0x1b, 0x0e, 0x73, 0xd7, 0xeb, 0x5c, 0xbd, 0xb6, 0x0b, + 0xe7, 0x3f, 0xef, 0xe6, 0xbc, 0x84, 0xa0, 0xfc, 0xc5, 0x42, 0x05, 0x1d, 0xb5, 0x9f, 0xa3, 0x8a, + 0x80, 0x31, 0x16, 0x30, 0x00, 0x21, 0xc0, 0xc7, 0x13, 0x1e, 0xc4, 0x0c, 0xf0, 0x38, 0xe6, 0x4a, + 0xaf, 0x24, 0x54, 0x31, 0xd3, 0xe7, 0x59, 0xd5, 0x82, 0xe7, 0x08, 0x18, 0x7b, 0x69, 0xe2, 0x89, + 0xc9, 0x3b, 0xd6, 0x69, 0xc7, 0x69, 0x96, 0xdd, 0x44, 0xb7, 0x34, 0x97, 0x54, 0xe4, 0x2d, 0xf8, + 0xf8, 0xdd, 0x88, 0x07, 0x80, 0xfb, 0x9c, 0x86, 0xb2, 0x94, 0xdf, 0xb3, 0xaa, 0xd7, 0xbd, 0xa2, + 0x80, 0x71, 0xd7, 0x80, 0xa7, 0x1a, 0x7b, 0xa2, 0x21, 0xbb, 0x86, 0x8a, 0x3a, 0x24, 0xf0, 0x00, + 0x00, 0xcb, 0x11, 0x11, 0x80, 0xa3, 0x88, 0x95, 0xb6, 0x4c, 0xc5, 0x4d, 0x03, 0x1d, 0x02, 0x74, + 0x35, 0xd0, 0x89, 0x58, 0xe5, 0xab, 0x85, 0xec, 0xa5, 0xba, 0xd3, 0x11, 0x55, 0x10, 0x50, 0xa9, + 0xec, 0xa3, 0xf5, 0xc9, 0x34, 0xfe, 0x3d, 0x99, 0x65, 0xed, 0x25, 0xd3, 0xe9, 0xa6, 0xc3, 0xd9, + 0x45, 0xdb, 0xc4, 0xf7, 0x05, 0x48, 0x09, 0x09, 0xf3, 0xb6, 0xb7, 0x0a, 0x6c, 0xea, 0x3c, 0xbf, + 0xa1, 0xf3, 0x1f, 0x79, 0x54, 0x5c, 0x9e, 0xde, 0x21, 0x82, 0x30, 0x50, 0xda, 0xd4, 0x0f, 0x16, + 0x7a, 0xcc, 0xc8, 0x94, 0xb2, 0x98, 0xe1, 0x66, 0xc3, 0xc7, 0x44, 0x29, 0x41, 0x7b, 0xb1, 0x22, + 0xbd, 0x00, 0x16, 0x76, 0x44, 0x20, 0x56, 0x16, 0xc5, 0x12, 0xc4, 0xe5, 0x06, 0x3d, 0x4c, 0x79, + 0x9a, 0x0d, 0xbf, 0x95, 0x61, 0x49, 0xbc, 0xea, 0x80, 0x58, 0x98, 0xf7, 0x5a, 0x82, 0x58, 0xb7, + 0xee, 0x00, 0xed, 0x1a, 0x7e, 0x00, 0xcc, 0x68, 0x68, 0x7a, 0xd1, 0xaa, 0xf4, 0x38, 0x30, 0xf5, + 0xa7, 0xa9, 0xa8, 0x52, 0x9a, 0xf3, 0x22, 0x49, 0x39, 0x04, 0x73, 0xbf, 0x9e, 0xf9, 0x53, 0xfb, + 0x3d, 0x3a, 0x58, 0xd3, 0xb0, 0xd0, 0x89, 0x05, 0x4c, 0x20, 0x8c, 0xff, 0x43, 0xc1, 0x96, 0x51, + 0xb0, 0x9f, 0x51, 0xb0, 0xe0, 0xf0, 0x12, 0x8a, 0xbf, 0xf5, 0x5f, 0x39, 0xca, 0xdc, 0x8a, 0x97, + 0x13, 0x10, 0x82, 0xfa, 0x20, 0xed, 0xfd, 0x3f, 0xfc, 0x6b, 0x97, 0xbe, 0x7d, 0xae, 0xed, 0xa4, + 0x7f, 0x5f, 0x2b, 0xc1, 0xba, 0x4a, 0xd0, 0x70, 0x98, 0x71, 0xb6, 0x7d, 0x72, 0x3e, 0x73, 0xac, + 0x8b, 0x99, 0x63, 0xfd, 0x9a, 0x39, 0xd6, 0xc7, 0xb9, 0x93, 0xbb, 0x98, 0x3b, 0xb9, 0xef, 0x73, + 0x27, 0xf7, 0xe6, 0xd1, 0x90, 0xaa, 0x51, 0xdc, 0x73, 0xfb, 0x9c, 0xd5, 0xd7, 0xde, 0x84, 0xc9, + 0x83, 0x5a, 0x7f, 0x44, 0x68, 0x58, 0x5f, 0x46, 0xa6, 0xd9, 0x77, 0x42, 0x9d, 0x45, 0x20, 0x7b, + 0x57, 0x0d, 0xd8, 0xfc, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x10, 0x28, 0x41, 0x19, 0x4f, 0x04, 0x00, + 0x00, +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) } func (m *AffiliateTiers) Marshal() (dAtA []byte, err error) { @@ -424,6 +580,79 @@ func (m *AffiliateWhitelist_Tier) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +<<<<<<< HEAD +======= +func (m *AffiliateParameters) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AffiliateParameters) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AffiliateParameters) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums != 0 { + i = encodeVarintAffiliates(dAtA, i, uint64(m.Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums)) + i-- + dAtA[i] = 0x18 + } + if m.RefereeMinimumFeeTierIdx != 0 { + i = encodeVarintAffiliates(dAtA, i, uint64(m.RefereeMinimumFeeTierIdx)) + i-- + dAtA[i] = 0x10 + } + if m.Maximum_30DAttributableVolumePerReferredUserQuoteQuantums != 0 { + i = encodeVarintAffiliates(dAtA, i, uint64(m.Maximum_30DAttributableVolumePerReferredUserQuoteQuantums)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *AffiliateOverrides) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *AffiliateOverrides) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AffiliateOverrides) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Addresses) > 0 { + for iNdEx := len(m.Addresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.Addresses[iNdEx]) + copy(dAtA[i:], m.Addresses[iNdEx]) + i = encodeVarintAffiliates(dAtA, i, uint64(len(m.Addresses[iNdEx]))) + i-- + dAtA[i] = 0xa + } + } + return len(dAtA) - i, nil +} + +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) func encodeVarintAffiliates(dAtA []byte, offset int, v uint64) int { offset -= sovAffiliates(v) base := offset @@ -501,6 +730,42 @@ func (m *AffiliateWhitelist_Tier) Size() (n int) { return n } +<<<<<<< HEAD +======= +func (m *AffiliateParameters) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Maximum_30DAttributableVolumePerReferredUserQuoteQuantums != 0 { + n += 1 + sovAffiliates(uint64(m.Maximum_30DAttributableVolumePerReferredUserQuoteQuantums)) + } + if m.RefereeMinimumFeeTierIdx != 0 { + n += 1 + sovAffiliates(uint64(m.RefereeMinimumFeeTierIdx)) + } + if m.Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums != 0 { + n += 1 + sovAffiliates(uint64(m.Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums)) + } + return n +} + +func (m *AffiliateOverrides) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if len(m.Addresses) > 0 { + for _, s := range m.Addresses { + l = len(s) + n += 1 + l + sovAffiliates(uint64(l)) + } + } + return n +} + +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) func sovAffiliates(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -883,6 +1148,198 @@ func (m *AffiliateWhitelist_Tier) Unmarshal(dAtA []byte) error { } return nil } +<<<<<<< HEAD +======= +func (m *AffiliateParameters) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAffiliates + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AffiliateParameters: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AffiliateParameters: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Maximum_30DAttributableVolumePerReferredUserQuoteQuantums", wireType) + } + m.Maximum_30DAttributableVolumePerReferredUserQuoteQuantums = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAffiliates + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Maximum_30DAttributableVolumePerReferredUserQuoteQuantums |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field RefereeMinimumFeeTierIdx", wireType) + } + m.RefereeMinimumFeeTierIdx = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAffiliates + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.RefereeMinimumFeeTierIdx |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums", wireType) + } + m.Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAffiliates + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipAffiliates(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAffiliates + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *AffiliateOverrides) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAffiliates + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: AffiliateOverrides: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AffiliateOverrides: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Addresses", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowAffiliates + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthAffiliates + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthAffiliates + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Addresses = append(m.Addresses, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipAffiliates(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthAffiliates + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) func skipAffiliates(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/protocol/x/affiliates/types/constants.go b/protocol/x/affiliates/types/constants.go index 788eb5c9e0..8d685c87fe 100644 --- a/protocol/x/affiliates/types/constants.go +++ b/protocol/x/affiliates/types/constants.go @@ -26,5 +26,14 @@ var ( }, } +<<<<<<< HEAD +======= + DefaultAffiliateParameters = AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 100_000_000_000_000, // 100 million USDC + RefereeMinimumFeeTierIdx: 2, + Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums: 10_000_000_000, // 10k commission + } + +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) AffiliatesRevSharePpmCap = uint32(500_000) // 50% ) diff --git a/protocol/x/affiliates/types/expected_keepers.go b/protocol/x/affiliates/types/expected_keepers.go index b92c4d304a..ab9734d220 100644 --- a/protocol/x/affiliates/types/expected_keepers.go +++ b/protocol/x/affiliates/types/expected_keepers.go @@ -10,6 +10,11 @@ import ( type StatsKeeper interface { GetStakedAmount(ctx sdk.Context, delegatorAddr string) *big.Int GetBlockStats(ctx sdk.Context) *stattypes.BlockStats +<<<<<<< HEAD +======= + GetUserStats(ctx sdk.Context, address string) *stattypes.UserStats + SetUserStats(ctx sdk.Context, address string, userStats *stattypes.UserStats) +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) } type FeetiersKeeper interface { diff --git a/protocol/x/affiliates/types/genesis.go b/protocol/x/affiliates/types/genesis.go index 0fac6ad561..5510b780ea 100644 --- a/protocol/x/affiliates/types/genesis.go +++ b/protocol/x/affiliates/types/genesis.go @@ -3,7 +3,8 @@ package types // DefaultGenesis returns the default stats genesis state. func DefaultGenesis() *GenesisState { return &GenesisState{ - AffiliateTiers: DefaultAffiliateTiers, + AffiliateTiers: DefaultAffiliateTiers, + AffiliateParameters: DefaultAffiliateParameters, } } diff --git a/protocol/x/affiliates/types/genesis.pb.go b/protocol/x/affiliates/types/genesis.pb.go index 890eab489a..2f54c3e334 100644 --- a/protocol/x/affiliates/types/genesis.pb.go +++ b/protocol/x/affiliates/types/genesis.pb.go @@ -27,6 +27,8 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package type GenesisState struct { // The list of affiliate tiers AffiliateTiers AffiliateTiers `protobuf:"bytes,1,opt,name=affiliate_tiers,json=affiliateTiers,proto3" json:"affiliate_tiers"` + // The affiliate parameters + AffiliateParameters AffiliateParameters `protobuf:"bytes,2,opt,name=affiliate_parameters,json=affiliateParameters,proto3" json:"affiliate_parameters"` } func (m *GenesisState) Reset() { *m = GenesisState{} } @@ -69,6 +71,13 @@ func (m *GenesisState) GetAffiliateTiers() AffiliateTiers { return AffiliateTiers{} } +func (m *GenesisState) GetAffiliateParameters() AffiliateParameters { + if m != nil { + return m.AffiliateParameters + } + return AffiliateParameters{} +} + func init() { proto.RegisterType((*GenesisState)(nil), "dydxprotocol.affiliates.GenesisState") } @@ -78,20 +87,22 @@ func init() { } var fileDescriptor_7d3ffc50e877971b = []byte{ - // 207 bytes of a gzipped FileDescriptorProto + // 240 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x4d, 0xa9, 0x4c, 0xa9, 0x28, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0xce, 0xcf, 0xd1, 0x4f, 0x4c, 0x4b, 0xcb, 0xcc, 0xc9, 0x4c, 0x2c, 0x49, 0x2d, 0xd6, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, 0xd6, 0x03, 0xcb, 0x09, 0x89, 0x23, 0x2b, 0xd3, 0x43, 0x28, 0x93, 0x12, 0x49, 0xcf, 0x4f, 0xcf, 0x07, 0x4b, 0xe8, 0x83, 0x58, - 0x10, 0xe5, 0x52, 0x1a, 0xb8, 0x4c, 0x45, 0x30, 0x21, 0x2a, 0x95, 0xd2, 0xb8, 0x78, 0xdc, 0x21, - 0x36, 0x05, 0x97, 0x24, 0x96, 0xa4, 0x0a, 0x85, 0x71, 0xf1, 0xc3, 0xd5, 0xc4, 0x97, 0x64, 0xa6, - 0x16, 0x15, 0x4b, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x1b, 0xa9, 0xeb, 0xe1, 0x70, 0x82, 0x9e, 0x23, - 0x8c, 0x19, 0x02, 0x52, 0xee, 0xc4, 0x72, 0xe2, 0x9e, 0x3c, 0x43, 0x10, 0x5f, 0x22, 0xaa, 0x68, - 0xd8, 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0x38, 0xe1, 0xb1, - 0x1c, 0xc3, 0x85, 0xc7, 0x72, 0x0c, 0x37, 0x1e, 0xcb, 0x31, 0x44, 0xd9, 0xa4, 0x67, 0x96, 0x64, - 0x94, 0x26, 0xe9, 0x25, 0xe7, 0xe7, 0xea, 0xa3, 0x38, 0xbb, 0xcc, 0x44, 0x37, 0x39, 0x23, 0x31, - 0x33, 0x4f, 0x1f, 0x2e, 0x52, 0x81, 0xec, 0x95, 0x92, 0xca, 0x82, 0xd4, 0xe2, 0x24, 0x36, 0xb0, - 0xa4, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x3f, 0xb3, 0x6a, 0x5a, 0x48, 0x01, 0x00, 0x00, + 0x10, 0xe5, 0x52, 0x1a, 0xb8, 0x4c, 0x45, 0x30, 0x21, 0x2a, 0x95, 0xce, 0x32, 0x72, 0xf1, 0xb8, + 0x43, 0xac, 0x0a, 0x2e, 0x49, 0x2c, 0x49, 0x15, 0x0a, 0xe3, 0xe2, 0x87, 0x2b, 0x8a, 0x2f, 0xc9, + 0x4c, 0x2d, 0x2a, 0x96, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x52, 0xd7, 0xc3, 0xe1, 0x06, 0x3d, + 0x47, 0x18, 0x33, 0x04, 0xa4, 0xdc, 0x89, 0xe5, 0xc4, 0x3d, 0x79, 0x86, 0x20, 0xbe, 0x44, 0x14, + 0x51, 0xa1, 0x54, 0x2e, 0x11, 0x84, 0xb9, 0x05, 0x89, 0x45, 0x89, 0xb9, 0xa9, 0x25, 0x20, 0xc3, + 0x99, 0xc0, 0x86, 0xeb, 0x10, 0x36, 0x3c, 0x00, 0xae, 0x07, 0x6a, 0x83, 0x70, 0x22, 0x16, 0xa9, + 0xb0, 0x13, 0x8f, 0xe4, 0x18, 0x2f, 0x3c, 0x92, 0x63, 0x7c, 0xf0, 0x48, 0x8e, 0x71, 0xc2, 0x63, + 0x39, 0x86, 0x0b, 0x8f, 0xe5, 0x18, 0x6e, 0x3c, 0x96, 0x63, 0x88, 0xb2, 0x49, 0xcf, 0x2c, 0xc9, + 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x47, 0x09, 0x9e, 0x32, 0x13, 0xdd, 0xe4, 0x8c, 0xc4, + 0xcc, 0x3c, 0x7d, 0xb8, 0x48, 0x05, 0x72, 0x90, 0x95, 0x54, 0x16, 0xa4, 0x16, 0x27, 0xb1, 0x81, + 0x25, 0x8d, 0x01, 0x01, 0x00, 0x00, 0xff, 0xff, 0x29, 0xbf, 0x39, 0xbc, 0xb0, 0x01, 0x00, 0x00, } func (m *GenesisState) Marshal() (dAtA []byte, err error) { @@ -114,6 +125,16 @@ func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size, err := m.AffiliateParameters.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 { size, err := m.AffiliateTiers.MarshalToSizedBuffer(dAtA[:i]) if err != nil { @@ -146,6 +167,8 @@ func (m *GenesisState) Size() (n int) { _ = l l = m.AffiliateTiers.Size() n += 1 + l + sovGenesis(uint64(l)) + l = m.AffiliateParameters.Size() + n += 1 + l + sovGenesis(uint64(l)) return n } @@ -217,6 +240,39 @@ func (m *GenesisState) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AffiliateParameters", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.AffiliateParameters.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipGenesis(dAtA[iNdEx:]) diff --git a/protocol/x/affiliates/types/keys.go b/protocol/x/affiliates/types/keys.go index 0965b44e1d..780d250b87 100644 --- a/protocol/x/affiliates/types/keys.go +++ b/protocol/x/affiliates/types/keys.go @@ -13,7 +13,11 @@ const ( const ( ReferredByKeyPrefix = "RB:" +<<<<<<< HEAD ReferredVolumeKeyPrefix = "RV:" +======= + ReferredVolumeInWindowKeyPrefix = "RVW:" +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) AffiliateTiersKey = "AT" diff --git a/protocol/x/affiliates/types/query.pb.go b/protocol/x/affiliates/types/query.pb.go index 5ba1fd76db..8bf4367d74 100644 --- a/protocol/x/affiliates/types/query.pb.go +++ b/protocol/x/affiliates/types/query.pb.go @@ -89,9 +89,11 @@ type AffiliateInfoResponse struct { // regular affiliate). FeeSharePpm uint32 `protobuf:"varint,3,opt,name=fee_share_ppm,json=feeSharePpm,proto3" json:"fee_share_ppm,omitempty"` // The affiliate's all-time referred volume in quote quantums. - ReferredVolume github_com_dydxprotocol_v4_chain_protocol_dtypes.SerializableInt `protobuf:"bytes,4,opt,name=referred_volume,json=referredVolume,proto3,customtype=github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt" json:"referred_volume"` + ReferredVolume github_com_dydxprotocol_v4_chain_protocol_dtypes.SerializableInt `protobuf:"bytes,4,opt,name=referred_volume,json=referredVolume,proto3,customtype=github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt" json:"referred_volume"` // Deprecated: Do not use. // The affiliate's currently staked native tokens (in whole coins). StakedAmount github_com_dydxprotocol_v4_chain_protocol_dtypes.SerializableInt `protobuf:"bytes,5,opt,name=staked_amount,json=stakedAmount,proto3,customtype=github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt" json:"staked_amount"` + // The affiliate's 30d referred volume in quote quantums. + ReferredVolume_30DRolling github_com_dydxprotocol_v4_chain_protocol_dtypes.SerializableInt `protobuf:"bytes,6,opt,name=referred_volume_30d_rolling,json=referredVolume30dRolling,proto3,customtype=github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt" json:"referred_volume_30d_rolling"` } func (m *AffiliateInfoResponse) Reset() { *m = AffiliateInfoResponse{} } @@ -425,6 +427,7 @@ func init() { } var fileDescriptor_2edc1b3ea39b05a9 = []byte{ +<<<<<<< HEAD // 672 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x54, 0xcb, 0x6e, 0xd3, 0x40, 0x14, 0x8d, 0x4b, 0x03, 0x74, 0xda, 0x14, 0x3a, 0x2a, 0xc2, 0x35, 0x28, 0xad, 0x8c, 0x10, 0x11, @@ -468,6 +471,61 @@ var fileDescriptor_2edc1b3ea39b05a9 = []byte{ 0x6c, 0xef, 0x15, 0x95, 0x5f, 0x7b, 0x45, 0xe5, 0xfd, 0x7e, 0x31, 0xb7, 0xbd, 0x5f, 0xcc, 0xed, 0xec, 0x17, 0x73, 0xcf, 0x6e, 0x0d, 0xfe, 0xb0, 0x6d, 0xf6, 0xb3, 0x88, 0x47, 0xae, 0x76, 0x52, 0x24, 0x97, 0xfe, 0x04, 0x00, 0x00, 0xff, 0xff, 0x9e, 0x0e, 0x60, 0x8f, 0x2d, 0x07, 0x00, 0x00, +======= + // 822 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x95, 0xcf, 0x6f, 0xd3, 0x48, + 0x14, 0xc7, 0xe3, 0xfe, 0xda, 0x76, 0xda, 0x74, 0xb7, 0xb3, 0x5d, 0xad, 0x9b, 0x56, 0x69, 0xe5, + 0xd5, 0x6a, 0xa3, 0x6d, 0x63, 0xb7, 0x49, 0x77, 0x25, 0x24, 0x0e, 0x24, 0x08, 0x41, 0x7b, 0x69, + 0x71, 0x51, 0x91, 0xe0, 0x60, 0x9c, 0x78, 0x92, 0x8c, 0xb0, 0x3d, 0xae, 0xc7, 0x29, 0x0d, 0x88, + 0x0b, 0x17, 0xae, 0x48, 0x9c, 0xf9, 0x03, 0x90, 0x38, 0x20, 0x81, 0xc4, 0xbf, 0xd0, 0x63, 0x05, + 0x17, 0xc4, 0xa1, 0x42, 0x6d, 0xff, 0x10, 0x14, 0x7b, 0x3c, 0x76, 0x9a, 0x44, 0x71, 0x44, 0x6f, + 0xce, 0x7b, 0xf3, 0xde, 0xf7, 0xe3, 0x97, 0xf9, 0x3e, 0x83, 0xbf, 0x8c, 0x96, 0x71, 0xe4, 0xb8, + 0xc4, 0x23, 0x55, 0x62, 0x2a, 0x7a, 0xad, 0x86, 0x4d, 0xac, 0x7b, 0x88, 0x2a, 0x07, 0x4d, 0xe4, + 0xb6, 0x64, 0x3f, 0x03, 0xff, 0x8c, 0x1f, 0x92, 0xa3, 0x43, 0x99, 0x85, 0x2a, 0xa1, 0x16, 0xa1, + 0x9a, 0x9f, 0x53, 0x82, 0x1f, 0x41, 0x4d, 0x66, 0xbe, 0x4e, 0xea, 0x24, 0x88, 0xb7, 0x9f, 0x58, + 0x34, 0xd7, 0x4f, 0x2e, 0x7a, 0x64, 0x27, 0x97, 0xea, 0x84, 0xd4, 0x4d, 0xa4, 0xe8, 0x0e, 0x56, + 0x74, 0xdb, 0x26, 0x9e, 0xee, 0x61, 0x62, 0xb3, 0xac, 0xb4, 0x0d, 0xe6, 0x4b, 0x61, 0xc5, 0x96, + 0x5d, 0x23, 0x2a, 0x3a, 0x68, 0x22, 0xea, 0xc1, 0x02, 0xf8, 0x45, 0x37, 0x0c, 0x17, 0x51, 0x2a, + 0x0a, 0x2b, 0x42, 0x6e, 0xaa, 0x2c, 0x7e, 0xfe, 0x98, 0x9f, 0x67, 0x60, 0xa5, 0x20, 0xb3, 0xe7, + 0xb9, 0xd8, 0xae, 0xab, 0xe1, 0x41, 0xe9, 0x62, 0x14, 0xfc, 0x71, 0xa9, 0x19, 0x75, 0x88, 0x4d, + 0x11, 0xfc, 0x1b, 0xcc, 0x62, 0xaa, 0x3d, 0x69, 0x60, 0x0f, 0x99, 0x98, 0x7a, 0xc8, 0xf0, 0x9b, + 0x4e, 0xaa, 0x69, 0x4c, 0xef, 0x47, 0x41, 0x08, 0xc1, 0x98, 0x87, 0x91, 0x2b, 0x8e, 0xac, 0x08, + 0xb9, 0xb4, 0xea, 0x3f, 0x43, 0x09, 0xa4, 0x6b, 0x08, 0x69, 0xb4, 0xa1, 0xbb, 0x48, 0x73, 0x1c, + 0x4b, 0x1c, 0xf5, 0x93, 0xd3, 0x35, 0x84, 0xf6, 0xda, 0xb1, 0x5d, 0xc7, 0x82, 0x14, 0xfc, 0xea, + 0xa2, 0x1a, 0x72, 0x5d, 0x64, 0x68, 0x87, 0xc4, 0x6c, 0x5a, 0x48, 0x1c, 0x5b, 0x11, 0x72, 0x33, + 0xe5, 0xed, 0xe3, 0xd3, 0xe5, 0xd4, 0xb7, 0xd3, 0xe5, 0x1b, 0x75, 0xec, 0x35, 0x9a, 0x15, 0xb9, + 0x4a, 0x2c, 0xa5, 0x63, 0x70, 0x87, 0x9b, 0xf9, 0x6a, 0x43, 0xc7, 0xb6, 0xc2, 0x23, 0x86, 0xd7, + 0x72, 0x10, 0x95, 0xf7, 0x90, 0x8b, 0x75, 0x13, 0x3f, 0xd5, 0x2b, 0x26, 0xda, 0xb2, 0x3d, 0x51, + 0x50, 0x67, 0x43, 0x89, 0x7d, 0x5f, 0x01, 0x5a, 0x20, 0x4d, 0x3d, 0xfd, 0x31, 0x32, 0x34, 0xdd, + 0x22, 0x4d, 0xdb, 0x13, 0xc7, 0x7d, 0xc9, 0x3b, 0x57, 0x25, 0xa9, 0xce, 0x04, 0xed, 0x4b, 0x7e, + 0x77, 0xf8, 0x52, 0x00, 0x8b, 0x97, 0x5e, 0x52, 0x2b, 0xae, 0x1b, 0x9a, 0x4b, 0x4c, 0x13, 0xdb, + 0x75, 0x71, 0xe2, 0x8a, 0xd5, 0xc5, 0xce, 0xd7, 0x2d, 0xae, 0x1b, 0x6a, 0xa0, 0x24, 0xdd, 0x06, + 0x73, 0x2a, 0xcb, 0x95, 0x5b, 0x3f, 0x73, 0x5f, 0x1e, 0x02, 0x18, 0x6f, 0xc4, 0xee, 0xca, 0x2d, + 0x30, 0xc7, 0xef, 0xb0, 0x96, 0xb4, 0xe7, 0x6f, 0xbc, 0x84, 0xc5, 0xa5, 0x0c, 0x10, 0x4b, 0xa6, + 0xc9, 0xaf, 0xe3, 0x3d, 0x8c, 0x5c, 0xca, 0x60, 0xa5, 0x47, 0x60, 0xa1, 0x47, 0x8e, 0xe9, 0xdf, + 0x04, 0xe3, 0xed, 0x8b, 0x17, 0x68, 0x4e, 0x17, 0xfe, 0x91, 0xfb, 0x78, 0x56, 0xee, 0xac, 0x2f, + 0x8f, 0xb5, 0x47, 0xaf, 0x06, 0xb5, 0xd2, 0x22, 0x58, 0xe0, 0x69, 0x7e, 0xc3, 0x43, 0x79, 0x0b, + 0x64, 0x7a, 0x25, 0x99, 0xfe, 0x0e, 0x98, 0xe2, 0x46, 0x61, 0x0c, 0xab, 0x83, 0x19, 0x78, 0x1f, + 0xc6, 0x11, 0xf5, 0xe8, 0x60, 0xd9, 0x39, 0x44, 0xae, 0x8b, 0x0d, 0x44, 0x7b, 0xb1, 0xc4, 0x92, + 0x11, 0x0b, 0x09, 0x83, 0xc9, 0x59, 0x78, 0x9f, 0x90, 0x85, 0xf7, 0x90, 0x96, 0x62, 0x72, 0xbb, + 0xba, 0xab, 0x5b, 0xc8, 0x8b, 0xfd, 0x2f, 0x07, 0x60, 0xb1, 0x67, 0x96, 0xd1, 0xa8, 0x00, 0x38, + 0x3c, 0xca, 0x70, 0xd6, 0x06, 0xe3, 0x44, 0x9d, 0x18, 0x4f, 0xac, 0x4b, 0xe1, 0xdd, 0x24, 0x18, + 0xbf, 0xdb, 0xde, 0xd0, 0xf0, 0xad, 0x00, 0xd2, 0x1d, 0xdb, 0x0b, 0xe6, 0x07, 0xf7, 0x8e, 0xad, + 0xcc, 0x8c, 0x9c, 0xf4, 0x78, 0xf0, 0x3a, 0xd2, 0xb5, 0x17, 0x5f, 0x2e, 0x5e, 0x8f, 0x14, 0xe1, + 0x86, 0x32, 0x70, 0x97, 0x6b, 0xd8, 0xae, 0x11, 0xe5, 0x19, 0x73, 0xc3, 0x73, 0xf8, 0x46, 0x00, + 0x20, 0xb2, 0x0e, 0xfc, 0xb7, 0xaf, 0x72, 0x97, 0x51, 0x33, 0xab, 0x89, 0xce, 0x32, 0xc4, 0xff, + 0x7d, 0xc4, 0x75, 0x28, 0xf7, 0x45, 0xe4, 0x2b, 0xa9, 0xd2, 0x8a, 0xf1, 0xbd, 0x17, 0xc0, 0x5c, + 0x97, 0xc3, 0xe0, 0x46, 0xff, 0x01, 0xf5, 0x71, 0x6a, 0xa6, 0x30, 0x4c, 0x09, 0x83, 0xde, 0xf4, + 0xa1, 0x65, 0xb8, 0xd6, 0x7f, 0xae, 0xa6, 0xa9, 0x45, 0xb3, 0xf5, 0x1d, 0x0b, 0x3f, 0x08, 0x00, + 0x76, 0xbb, 0x09, 0x16, 0x86, 0xb0, 0x5e, 0x08, 0x5d, 0x1c, 0xaa, 0x26, 0x39, 0x35, 0x27, 0xe6, + 0xde, 0x86, 0x9f, 0x04, 0xf0, 0x7b, 0x8f, 0x8b, 0x0e, 0x8b, 0xc3, 0xd8, 0x22, 0xe4, 0xde, 0x1c, + 0xae, 0x88, 0x81, 0xff, 0xe7, 0x83, 0x2b, 0x30, 0x9f, 0x00, 0x3c, 0x32, 0x5e, 0xe7, 0xbc, 0xf9, + 0xc6, 0x48, 0x32, 0xef, 0xcb, 0x3b, 0x2c, 0xc9, 0xbc, 0xbb, 0x56, 0xdb, 0x50, 0xf3, 0xe6, 0xfb, + 0xab, 0xbc, 0x7f, 0x7c, 0x96, 0x15, 0x4e, 0xce, 0xb2, 0xc2, 0xf7, 0xb3, 0xac, 0xf0, 0xea, 0x3c, + 0x9b, 0x3a, 0x39, 0xcf, 0xa6, 0xbe, 0x9e, 0x67, 0x53, 0x0f, 0xae, 0x27, 0xff, 0xe2, 0x1e, 0xc5, + 0x55, 0xfc, 0xaf, 0x6f, 0x65, 0xc2, 0x4f, 0x16, 0x7f, 0x04, 0x00, 0x00, 0xff, 0xff, 0xd4, 0x38, + 0xfa, 0x6c, 0x46, 0x0a, 0x00, 0x00, +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) } // Reference imports to suppress errors if they are not otherwise used. @@ -716,6 +774,16 @@ func (m *AffiliateInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size := m.ReferredVolume_30DRolling.Size() + i -= size + if _, err := m.ReferredVolume_30DRolling.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 { size := m.StakedAmount.Size() i -= size @@ -974,6 +1042,8 @@ func (m *AffiliateInfoResponse) Size() (n int) { n += 1 + l + sovQuery(uint64(l)) l = m.StakedAmount.Size() n += 1 + l + sovQuery(uint64(l)) + l = m.ReferredVolume_30DRolling.Size() + n += 1 + l + sovQuery(uint64(l)) return n } @@ -1284,6 +1354,39 @@ func (m *AffiliateInfoResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReferredVolume_30DRolling", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.ReferredVolume_30DRolling.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipQuery(dAtA[iNdEx:]) diff --git a/protocol/x/clob/keeper/mev.go b/protocol/x/clob/keeper/mev.go index 8bb7e8ceb5..48c231f2ac 100644 --- a/protocol/x/clob/keeper/mev.go +++ b/protocol/x/clob/keeper/mev.go @@ -455,6 +455,10 @@ func (k Keeper) GetMEVDataFromOperations( // Populate `mevMatches` and `mevLiquidationMatches` from the local validator's match operations. mevMatches := make([]types.MEVMatch, 0) mevLiquidationMatches := make([]types.MEVLiquidationMatch, 0) + affiliateParameters, err := k.affiliatesKeeper.GetAffiliateParameters(ctx) + if err != nil { + return nil, err + } for _, operation := range operations { switch typedOperation := operation.Operation.(type) { case *types.OperationRaw_ShortTermOrderPlacement: @@ -484,6 +488,7 @@ func (k Keeper) GetMEVDataFromOperations( ctx, takerOrder.GetSubaccountId().Owner, true, + affiliateParameters.RefereeMinimumFeeTierIdx, ), MakerOrderSubaccountId: &makerOrder.OrderId.SubaccountId, @@ -493,6 +498,7 @@ func (k Keeper) GetMEVDataFromOperations( ctx, makerOrder.GetSubaccountId().Owner, false, + affiliateParameters.RefereeMinimumFeeTierIdx, ), ClobPairId: takerOrder.OrderId.ClobPairId, @@ -540,6 +546,7 @@ func (k Keeper) GetMEVDataFromOperations( ctx, makerOrder.GetSubaccountId().Owner, false, + affiliateParameters.RefereeMinimumFeeTierIdx, ), ClobPairId: matchLiquidation.ClobPairId, diff --git a/protocol/x/clob/keeper/orders.go b/protocol/x/clob/keeper/orders.go index 5a97d91c5c..c94f3f710a 100644 --- a/protocol/x/clob/keeper/orders.go +++ b/protocol/x/clob/keeper/orders.go @@ -1112,7 +1112,16 @@ func (k Keeper) AddOrderToOrderbookSubaccountUpdatesCheck( panic(types.ErrInvalidClob) } perpetualId := clobPair.MustGetPerpetualId() - makerFeePpm := k.feeTiersKeeper.GetPerpetualFeePpm(ctx, subaccountId.Owner, false) + affiliateParameters, err := k.affiliatesKeeper.GetAffiliateParameters(ctx) + if err != nil { + panic(err) + } + makerFeePpm := k.feeTiersKeeper.GetPerpetualFeePpm( + ctx, + subaccountId.Owner, + false, + affiliateParameters.RefereeMinimumFeeTierIdx, + ) bigFillQuoteQuantums := types.FillAmountToQuoteQuantums( order.Subticks, order.RemainingQuantums, diff --git a/protocol/x/clob/keeper/process_single_match.go b/protocol/x/clob/keeper/process_single_match.go index 58a75f789a..f46e63434d 100644 --- a/protocol/x/clob/keeper/process_single_match.go +++ b/protocol/x/clob/keeper/process_single_match.go @@ -136,11 +136,14 @@ func (k Keeper) ProcessSingleMatch( return false, takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, err } + // Fee tier for affiliates + referreeIndexOverride := affiliateParameters.RefereeMinimumFeeTierIdx + // Calculate taker and maker fee ppms. takerFeePpm := k.feeTiersKeeper.GetPerpetualFeePpm( - ctx, matchWithOrders.TakerOrder.GetSubaccountId().Owner, true) + ctx, matchWithOrders.TakerOrder.GetSubaccountId().Owner, true, referreeIndexOverride) makerFeePpm := k.feeTiersKeeper.GetPerpetualFeePpm( - ctx, matchWithOrders.MakerOrder.GetSubaccountId().Owner, false) + ctx, matchWithOrders.MakerOrder.GetSubaccountId().Owner, false, referreeIndexOverride) takerInsuranceFundDelta := new(big.Int) if takerMatchableOrder.IsLiquidation() { diff --git a/protocol/x/clob/types/expected_keepers.go b/protocol/x/clob/types/expected_keepers.go index 5b0b0f13b5..8a7303996d 100644 --- a/protocol/x/clob/types/expected_keepers.go +++ b/protocol/x/clob/types/expected_keepers.go @@ -115,7 +115,7 @@ type BlockTimeKeeper interface { } type FeeTiersKeeper interface { - GetPerpetualFeePpm(ctx sdk.Context, address string, isTaker bool) int32 + GetPerpetualFeePpm(ctx sdk.Context, address string, isTaker bool, feeTierOverrideIdx uint32) int32 } type PerpetualsKeeper interface { diff --git a/protocol/x/feetiers/keeper/grpc_query.go b/protocol/x/feetiers/keeper/grpc_query.go index 7d785c2f17..70e10cf484 100644 --- a/protocol/x/feetiers/keeper/grpc_query.go +++ b/protocol/x/feetiers/keeper/grpc_query.go @@ -48,7 +48,11 @@ func (k Keeper) UserFeeTier( if _, err := sdk.AccAddressFromBech32(req.User); err != nil { return nil, status.Error(codes.InvalidArgument, "invalid bech32 address") } - index, tier := k.getUserFeeTier(ctx, req.User) + affiliateParameters, err := k.affiliatesKeeper.GetAffiliateParameters(ctx) + if err != nil { + return nil, err + } + index, tier := k.getUserFeeTier(ctx, req.User, affiliateParameters.RefereeMinimumFeeTierIdx) return &types.QueryUserFeeTierResponse{ Index: index, Tier: tier, diff --git a/protocol/x/feetiers/keeper/keeper.go b/protocol/x/feetiers/keeper/keeper.go index 98ce7d3086..26791e756d 100644 --- a/protocol/x/feetiers/keeper/keeper.go +++ b/protocol/x/feetiers/keeper/keeper.go @@ -60,7 +60,11 @@ func (k *Keeper) SetVaultKeeper(vk types.VaultKeeper) { k.vaultKeeper = vk } -func (k Keeper) getUserFeeTier(ctx sdk.Context, address string) (uint32, *types.PerpetualFeeTier) { +func (k Keeper) getUserFeeTier( + ctx sdk.Context, + address string, + feeTierOverrideIdx uint32, +) (uint32, *types.PerpetualFeeTier) { tiers := k.GetPerpetualFeeParams(ctx).Tiers // A vault is always in the highest tier. @@ -102,18 +106,28 @@ func (k Keeper) getUserFeeTier(ctx sdk.Context, address string) (uint32, *types. idx = uint32(i) } - if idx < types.RefereeStartingFeeTier { + maxTierIdx := uint32(len(tiers) - 1) + if feeTierOverrideIdx > maxTierIdx { + feeTierOverrideIdx = maxTierIdx + } + + if idx < feeTierOverrideIdx { _, hasReferree := k.affiliatesKeeper.GetReferredBy(ctx, address) if hasReferree { - idx = types.RefereeStartingFeeTier + idx = feeTierOverrideIdx } } return idx, tiers[idx] } -func (k Keeper) GetPerpetualFeePpm(ctx sdk.Context, address string, isTaker bool) int32 { - _, userTier := k.getUserFeeTier(ctx, address) +func (k Keeper) GetPerpetualFeePpm( + ctx sdk.Context, + address string, + isTaker bool, + feeTierOverrideIdx uint32, +) int32 { + _, userTier := k.getUserFeeTier(ctx, address, feeTierOverrideIdx) if isTaker { return userTier.TakerFeePpm } diff --git a/protocol/x/feetiers/keeper/keeper_test.go b/protocol/x/feetiers/keeper/keeper_test.go index 56c94f7237..d47965c25b 100644 --- a/protocol/x/feetiers/keeper/keeper_test.go +++ b/protocol/x/feetiers/keeper/keeper_test.go @@ -99,7 +99,13 @@ func TestGetPerpetualFeePpm(t *testing.T) { ctx := tApp.InitChain() tApp.App.VaultKeeper.AddVaultToAddressStore(ctx, constants.Vault_Clob0) k := tApp.App.FeeTiersKeeper - err := k.SetPerpetualFeeParams( + err := tApp.App.AffiliatesKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{ + AffiliateParameters: affiliatetypes.AffiliateParameters{ + RefereeMinimumFeeTierIdx: 2, + }, + }) + require.NoError(t, err) + err = k.SetPerpetualFeeParams( ctx, types.PerpetualFeeParams{ Tiers: []*types.PerpetualFeeTier{ @@ -130,8 +136,8 @@ func TestGetPerpetualFeePpm(t *testing.T) { statsKeeper.SetUserStats(ctx, tc.user, tc.UserStats) statsKeeper.SetGlobalStats(ctx, tc.GlobalStats) - require.Equal(t, tc.expectedTakerFeePpm, k.GetPerpetualFeePpm(ctx, tc.user, true)) - require.Equal(t, tc.expectedMakerFeePpm, k.GetPerpetualFeePpm(ctx, tc.user, false)) + require.Equal(t, tc.expectedTakerFeePpm, k.GetPerpetualFeePpm(ctx, tc.user, true, 2)) + require.Equal(t, tc.expectedMakerFeePpm, k.GetPerpetualFeePpm(ctx, tc.user, false, 2)) }) } } @@ -250,6 +256,13 @@ func TestGetPerpetualFeePpm_Referral(t *testing.T) { statsKeeper := tApp.App.StatsKeeper affiliatesKeeper := tApp.App.AffiliatesKeeper + err = affiliatesKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{ + AffiliateParameters: affiliatetypes.AffiliateParameters{ + RefereeMinimumFeeTierIdx: 2, + }, + }) + require.NoError(t, err) + // common setup err = affiliatesKeeper.UpdateAffiliateTiers(ctx, affiliatetypes.DefaultAffiliateTiers) require.NoError(t, err) @@ -259,7 +272,7 @@ func TestGetPerpetualFeePpm_Referral(t *testing.T) { } require.Equal(t, tc.expectedTakerFeePpm, - k.GetPerpetualFeePpm(ctx, constants.AliceAccAddress.String(), true)) + k.GetPerpetualFeePpm(ctx, constants.AliceAccAddress.String(), true, 2)) }) } } diff --git a/protocol/x/feetiers/types/expected_keepers.go b/protocol/x/feetiers/types/expected_keepers.go index f9a17782b3..0c3c04d041 100644 --- a/protocol/x/feetiers/types/expected_keepers.go +++ b/protocol/x/feetiers/types/expected_keepers.go @@ -22,6 +22,7 @@ type VaultKeeper interface { type AffiliatesKeeper interface { GetReferredBy(ctx sdk.Context, referee string) (string, bool) GetAllAffiliateTiers(ctx sdk.Context) (affiliatetypes.AffiliateTiers, error) + GetAffiliateParameters(ctx sdk.Context) (affiliatetypes.AffiliateParameters, error) } // RevShareKeeper defines the expected revshare keeper. diff --git a/protocol/x/feetiers/types/types.go b/protocol/x/feetiers/types/types.go index 348d5f478d..82cbd1c2e0 100644 --- a/protocol/x/feetiers/types/types.go +++ b/protocol/x/feetiers/types/types.go @@ -7,7 +7,7 @@ import ( type FeeTiersKeeper interface { GetLowestMakerFee(ctx sdk.Context) int32 GetAffiliateRefereeLowestTakerFee(ctx sdk.Context) int32 - GetPerpetualFeePpm(ctx sdk.Context, address string, isTaker bool) int32 + GetPerpetualFeePpm(ctx sdk.Context, address string, isTaker bool, feeTierOverrideIdx uint32) int32 GetPerpetualFeeParams( ctx sdk.Context, ) PerpetualFeeParams diff --git a/protocol/x/revshare/keeper/keeper.go b/protocol/x/revshare/keeper/keeper.go index 11dac305c2..eb30eef827 100644 --- a/protocol/x/revshare/keeper/keeper.go +++ b/protocol/x/revshare/keeper/keeper.go @@ -11,6 +11,7 @@ import ( affiliateskeeper "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/keeper" feetierskeeper "github.com/dydxprotocol/v4-chain/protocol/x/feetiers/keeper" "github.com/dydxprotocol/v4-chain/protocol/x/revshare/types" + statsKeeper "github.com/dydxprotocol/v4-chain/protocol/x/stats/keeper" ) type ( @@ -20,6 +21,7 @@ type ( authorities map[string]struct{} affiliatesKeeper affiliateskeeper.Keeper feetiersKeeper feetierskeeper.Keeper + statsKeeper statsKeeper.Keeper } ) @@ -29,6 +31,7 @@ func NewKeeper( authorities []string, affiliatesKeeper affiliateskeeper.Keeper, feetiersKeeper feetierskeeper.Keeper, + statsKeeper statsKeeper.Keeper, ) *Keeper { return &Keeper{ cdc: cdc, @@ -36,6 +39,7 @@ func NewKeeper( authorities: lib.UniqueSliceToSet(authorities), affiliatesKeeper: affiliatesKeeper, feetiersKeeper: feetiersKeeper, + statsKeeper: statsKeeper, } } diff --git a/protocol/x/revshare/keeper/revshare.go b/protocol/x/revshare/keeper/revshare.go index 483935a5f4..f65241ae9c 100644 --- a/protocol/x/revshare/keeper/revshare.go +++ b/protocol/x/revshare/keeper/revshare.go @@ -291,7 +291,12 @@ func (k Keeper) GetAllRevShares( func (k Keeper) getAffiliateRevShares( ctx sdk.Context, fill clobtypes.FillForProcess, +<<<<<<< HEAD affiliatesWhitelistMap map[string]uint32, +======= + affiliateOverrides map[string]bool, + affiliateParams affiliatetypes.AffiliateParameters, +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) ) ([]types.RevShare, *big.Int, error) { takerAddr := fill.TakerAddr takerFee := fill.TakerFeeQuoteQuantums @@ -300,8 +305,28 @@ func (k Keeper) getAffiliateRevShares( return nil, big.NewInt(0), nil } + userStats := k.statsKeeper.GetUserStats(ctx, takerAddr) + if userStats != nil { + // If the affiliate revenue generated is greater than the maximum 30d attributable volume + // per referred user notional, then no affiliate rev share is generated + // Disable this check if it is 0 + cap := affiliateParams.Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums + if cap != 0 && + userStats.Affiliate_30DRevenueGeneratedQuantums >= cap { + // Exceeded revenue cap, no rev share is attributed + return []types.RevShare{}, big.NewInt(0), nil + } + } + takerAffiliateAddr, feeSharePpm, exists, err := k.affiliatesKeeper.GetTakerFeeShare( +<<<<<<< HEAD ctx, takerAddr, affiliatesWhitelistMap) +======= + ctx, + takerAddr, + affiliateOverrides, + ) +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) if err != nil { return nil, big.NewInt(0), err } diff --git a/protocol/x/revshare/keeper/revshare_test.go b/protocol/x/revshare/keeper/revshare_test.go index 0ee8ff85d3..a0b2ed61db 100644 --- a/protocol/x/revshare/keeper/revshare_test.go +++ b/protocol/x/revshare/keeper/revshare_test.go @@ -15,6 +15,8 @@ import ( clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" "github.com/dydxprotocol/v4-chain/protocol/x/revshare/keeper" "github.com/dydxprotocol/v4-chain/protocol/x/revshare/types" + statsKeeper "github.com/dydxprotocol/v4-chain/protocol/x/stats/keeper" + statstypes "github.com/dydxprotocol/v4-chain/protocol/x/stats/types" "github.com/stretchr/testify/require" ) @@ -263,7 +265,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { fill clobtypes.FillForProcess expectedRevSharesForFill types.RevSharesForFill setup func(tApp *testapp.TestApp, ctx sdk.Context, - keeper *keeper.Keeper, affiliatesKeeper *affiliateskeeper.Keeper) + keeper *keeper.Keeper, affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) }{ { name: "Valid revenue share from affiliates, unconditional and market mapper", @@ -331,7 +333,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MarketId: marketId, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { err := keeper.SetMarketMapperRevenueShareParams(ctx, types.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), RevenueSharePpm: 100_000, // 10% @@ -359,6 +361,66 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { require.NoError(t, err) }, }, + { + name: "Affiliates has over 30d attributable volume, no rev share", + expectedRevSharesForFill: types.RevSharesForFill{ + AllRevShares: []types.RevShare{ + { + Recipient: constants.AliceAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE, + RevShareType: types.REV_SHARE_TYPE_MARKET_MAPPER, + QuoteQuantums: big.NewInt(1_200_000), // (10 + 2) * 10% + RevSharePpm: 100_000, + }, + }, + FeeSourceToQuoteQuantums: map[types.RevShareFeeSource]*big.Int{ + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: big.NewInt(0), // affiliate rev share fees + // unconditional + market mapper rev shares fees + types.REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE: big.NewInt(1_200_000), + types.REV_SHARE_FEE_SOURCE_MAKER_FEE: big.NewInt(0), + }, + FeeSourceToRevSharePpm: map[types.RevShareFeeSource]uint32{ + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: 0, // affiliate rev share fee ppm + types.REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE: 100_000, // unconditional + market mapper rev share fee ppm + types.REV_SHARE_FEE_SOURCE_MAKER_FEE: 0, + }, + }, + fill: clobtypes.FillForProcess{ + TakerAddr: constants.AliceAccAddress.String(), + TakerFeeQuoteQuantums: big.NewInt(10_000_000), + MakerAddr: constants.BobAccAddress.String(), + MakerFeeQuoteQuantums: big.NewInt(2_000_000), + FillQuoteQuantums: big.NewInt(100_000_000_000), + ProductId: perpetualId, + MonthlyRollingTakerVolumeQuantums: 1_000_000_000_000, + MarketId: marketId, + }, + setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { + err := keeper.SetMarketMapperRevenueShareParams(ctx, types.MarketMapperRevenueShareParams{ + Address: constants.AliceAccAddress.String(), + RevenueSharePpm: 100_000, // 10% + ValidDays: 1, + }) + require.NoError(t, err) + + require.NoError(t, affiliatesKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{ + AffiliateParameters: affiliatetypes.AffiliateParameters{ + Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums: 1_000_000_000_000, + }, + })) + + statsKeeper.SetUserStats(ctx, constants.AliceAccAddress.String(), &statstypes.UserStats{ + Affiliate_30DRevenueGeneratedQuantums: 1_000_000_000_000, + }) + + err = affiliatesKeeper.UpdateAffiliateTiers(ctx, affiliatetypes.DefaultAffiliateTiers) + require.NoError(t, err) + err = affiliatesKeeper.RegisterAffiliate(ctx, constants.AliceAccAddress.String(), + constants.BobAccAddress.String()) + require.NoError(t, err) + }, + }, { name: "Valid rev-share from affiliates, negative maker fee and unconditional and market mapper", expectedRevSharesForFill: types.RevSharesForFill{ @@ -421,7 +483,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MonthlyRollingTakerVolumeQuantums: 1_000_000_000_000, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { err := keeper.SetMarketMapperRevenueShareParams(ctx, types.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), RevenueSharePpm: 100_000, // 10% @@ -498,7 +560,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { }, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { err := keeper.SetMarketMapperRevenueShareParams(ctx, types.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), RevenueSharePpm: 100_000, // 10% @@ -573,7 +635,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MonthlyRollingTakerVolumeQuantums: 1_000_000_000_000, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { err := keeper.SetMarketMapperRevenueShareParams(ctx, types.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), RevenueSharePpm: 100_000, // 10% @@ -636,7 +698,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MonthlyRollingTakerVolumeQuantums: 1_000_000_000_000, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { keeper.SetUnconditionalRevShareConfigParams(ctx, types.UnconditionalRevShareConfig{ Configs: []types.UnconditionalRevShareConfig_RecipientConfig{ { @@ -702,7 +764,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { TakerOrderRouterAddr: constants.DaveAccAddress.String(), }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { keeper.SetUnconditionalRevShareConfigParams(ctx, types.UnconditionalRevShareConfig{ Configs: []types.UnconditionalRevShareConfig_RecipientConfig{ { @@ -771,7 +833,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { TakerOrderRouterAddr: constants.DaveAccAddress.String(), }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { keeper.SetUnconditionalRevShareConfigParams(ctx, types.UnconditionalRevShareConfig{ Configs: []types.UnconditionalRevShareConfig_RecipientConfig{ { @@ -850,7 +912,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MakerOrderRouterAddr: constants.AliceAccAddress.String(), }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { keeper.SetUnconditionalRevShareConfigParams(ctx, types.UnconditionalRevShareConfig{ Configs: []types.UnconditionalRevShareConfig_RecipientConfig{ { @@ -919,7 +981,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MakerOrderRouterAddr: constants.DaveAccAddress.String(), }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { err := keeper.SetMarketMapperRevenueShareParams(ctx, types.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), RevenueSharePpm: 100_000, // 10% @@ -999,7 +1061,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MakerOrderRouterAddr: constants.DaveAccAddress.String(), }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { err := keeper.SetMarketMapperRevenueShareParams(ctx, types.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), RevenueSharePpm: 100_000, // 10% @@ -1025,6 +1087,57 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { TakerFeeSharePpm: 250_000, // 25% }, }, +<<<<<<< HEAD +======= + { + Recipient: constants.AliceAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE, + RevShareType: types.REV_SHARE_TYPE_MARKET_MAPPER, + QuoteQuantums: big.NewInt(1_160_000), + RevSharePpm: 100_000, // 10% + }, + }, + AffiliateRevShare: nil, + FeeSourceToQuoteQuantums: map[types.RevShareFeeSource]*big.Int{ + types.REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE: big.NewInt(1_160_000), + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: big.NewInt(0), + types.REV_SHARE_FEE_SOURCE_MAKER_FEE: big.NewInt(400_000), + }, + FeeSourceToRevSharePpm: map[types.RevShareFeeSource]uint32{ + types.REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE: 100_000, // 10% + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: 0, // 10% + types.REV_SHARE_FEE_SOURCE_MAKER_FEE: 200_000, // 20% + }, + }, + fill: clobtypes.FillForProcess{ + TakerAddr: constants.AliceAccAddress.String(), + TakerFeeQuoteQuantums: big.NewInt(10_000_000), + MakerAddr: constants.BobAccAddress.String(), + MakerFeeQuoteQuantums: big.NewInt(2_000_000), + FillQuoteQuantums: big.NewInt(100_000_000_000), + ProductId: marketId, + MonthlyRollingTakerVolumeQuantums: 1_000_000_000_000, + TakerOrderRouterAddr: constants.CarlAccAddress.String(), + MakerOrderRouterAddr: constants.DaveAccAddress.String(), + }, + setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { + err := keeper.SetMarketMapperRevenueShareParams(ctx, types.MarketMapperRevenueShareParams{ + Address: constants.AliceAccAddress.String(), + RevenueSharePpm: 100_000, // 10% + ValidDays: 1, + }) + require.NoError(t, err) + + err = affiliatesKeeper.UpdateAffiliateTiers(ctx, affiliatetypes.DefaultAffiliateTiers) + require.NoError(t, err) + + err = keeper.SetOrderRouterRevShare(ctx, constants.DaveAccAddress.String(), 200_000) // 20% + require.NoError(t, err) + + err = affiliatesKeeper.SetAffiliateOverrides(ctx, affiliatetypes.AffiliateOverrides{ + Addresses: []string{constants.BobAccAddress.String()}, +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) }) require.NoError(t, err) }, @@ -1079,7 +1192,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MakerOrderRouterAddr: constants.DaveAccAddress.String(), }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { err := keeper.SetMarketMapperRevenueShareParams(ctx, types.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), RevenueSharePpm: 100_000, // 10% @@ -1133,7 +1246,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MonthlyRollingTakerVolumeQuantums: 1_000_000_000_000, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { }, }, { @@ -1150,7 +1263,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MarketId: marketId, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { err := keeper.SetMarketMapperRevenueShareParams(ctx, types.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), RevenueSharePpm: 100_000, // 10% @@ -1228,7 +1341,7 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { MarketId: marketId, }, setup: func(tApp *testapp.TestApp, ctx sdk.Context, keeper *keeper.Keeper, - affiliatesKeeper *affiliateskeeper.Keeper) { + affiliatesKeeper *affiliateskeeper.Keeper, statsKeeper *statsKeeper.Keeper) { err := keeper.SetMarketMapperRevenueShareParams(ctx, types.MarketMapperRevenueShareParams{ Address: constants.AliceAccAddress.String(), RevenueSharePpm: 100_000, // 10% @@ -1265,10 +1378,17 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { ctx := tApp.InitChain() keeper := tApp.App.RevShareKeeper affiliatesKeeper := tApp.App.AffiliatesKeeper + statsKeeper := tApp.App.StatsKeeper if tc.setup != nil { - tc.setup(tApp, ctx, &keeper, &affiliatesKeeper) + tc.setup(tApp, ctx, &keeper, &affiliatesKeeper, &statsKeeper) } + require.NoError(t, affiliatesKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{ + AffiliateParameters: affiliatetypes.AffiliateParameters{ + Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums: 1_000_000_000_000, + }, + })) + keeper.CreateNewMarketRevShare(ctx, marketId) affiliateWhitelistMap, err := affiliatesKeeper.GetAffiliateWhitelistMap(ctx) require.NoError(t, err) diff --git a/protocol/x/stats/keeper/keeper.go b/protocol/x/stats/keeper/keeper.go index 4dc2bc4033..93e62384ee 100644 --- a/protocol/x/stats/keeper/keeper.go +++ b/protocol/x/stats/keeper/keeper.go @@ -202,6 +202,11 @@ func (k Keeper) ProcessBlockStats(ctx sdk.Context) { for _, fill := range blockStats.Fills { userStats := k.GetUserStats(ctx, fill.Taker) userStats.TakerNotional += fill.Notional +<<<<<<< HEAD +======= + // Add affiliate revenue generated on taker for this fill (if any) + userStats.Affiliate_30DRevenueGeneratedQuantums += fill.AffiliateFeeGeneratedQuantums +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) k.SetUserStats(ctx, fill.Taker, userStats) userStats = k.GetUserStats(ctx, fill.Maker) @@ -222,6 +227,11 @@ func (k Keeper) ProcessBlockStats(ctx sdk.Context) { } userStatsMap[fill.Taker].Stats.TakerNotional += fill.Notional userStatsMap[fill.Maker].Stats.MakerNotional += fill.Notional +<<<<<<< HEAD +======= + // Track affiliate revenue generated on the taker in this epoch snapshot + userStatsMap[fill.Taker].Stats.Affiliate_30DRevenueGeneratedQuantums += fill.AffiliateFeeGeneratedQuantums +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) globalStats := k.GetGlobalStats(ctx) globalStats.NotionalTraded += fill.Notional @@ -271,6 +281,10 @@ func (k Keeper) ExpireOldStats(ctx sdk.Context) { stats := k.GetUserStats(ctx, removedStats.User) stats.TakerNotional -= removedStats.Stats.TakerNotional stats.MakerNotional -= removedStats.Stats.MakerNotional +<<<<<<< HEAD +======= + stats.Affiliate_30DRevenueGeneratedQuantums -= removedStats.Stats.Affiliate_30DRevenueGeneratedQuantums +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) k.SetUserStats(ctx, removedStats.User, stats) // Just remove TakerNotional to avoid double counting diff --git a/protocol/x/stats/types/stats.pb.go b/protocol/x/stats/types/stats.pb.go index 7c0a6f59a9..f16ce6f174 100644 --- a/protocol/x/stats/types/stats.pb.go +++ b/protocol/x/stats/types/stats.pb.go @@ -345,6 +345,13 @@ type UserStats struct { TakerNotional uint64 `protobuf:"varint,1,opt,name=taker_notional,json=takerNotional,proto3" json:"taker_notional,omitempty"` // Maker USDC in quantums MakerNotional uint64 `protobuf:"varint,2,opt,name=maker_notional,json=makerNotional,proto3" json:"maker_notional,omitempty"` +<<<<<<< HEAD +======= + // Affiliate revenue generated in quantums with this user being a referee + Affiliate_30DRevenueGeneratedQuantums uint64 `protobuf:"varint,3,opt,name=affiliate_30d_revenue_generated_quantums,json=affiliate30dRevenueGeneratedQuantums,proto3" json:"affiliate_30d_revenue_generated_quantums,omitempty"` + // Referred volume in quote quantums with this user being an affiliate + Affiliate_30DReferredVolumeQuoteQuantums uint64 `protobuf:"varint,4,opt,name=affiliate_30d_referred_volume_quote_quantums,json=affiliate30dReferredVolumeQuoteQuantums,proto3" json:"affiliate_30d_referred_volume_quote_quantums,omitempty"` +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) } func (m *UserStats) Reset() { *m = UserStats{} } @@ -394,6 +401,23 @@ func (m *UserStats) GetMakerNotional() uint64 { return 0 } +<<<<<<< HEAD +======= +func (m *UserStats) GetAffiliate_30DRevenueGeneratedQuantums() uint64 { + if m != nil { + return m.Affiliate_30DRevenueGeneratedQuantums + } + return 0 +} + +func (m *UserStats) GetAffiliate_30DReferredVolumeQuoteQuantums() uint64 { + if m != nil { + return m.Affiliate_30DReferredVolumeQuoteQuantums + } + return 0 +} + +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) // CachedStakeAmount stores the last calculated total staked amount for address type CachedStakeAmount struct { // Last calculated total staked amount by the delegator (in coin amount). @@ -457,6 +481,7 @@ func init() { func init() { proto.RegisterFile("dydxprotocol/stats/stats.proto", fileDescriptor_07475747e6dcccdc) } var fileDescriptor_07475747e6dcccdc = []byte{ +<<<<<<< HEAD // 542 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0xcb, 0x6e, 0xd3, 0x4e, 0x14, 0xc6, 0xe3, 0x5c, 0xfe, 0x4a, 0x26, 0x71, 0xfe, 0x62, 0xd4, 0x45, 0x64, 0x84, 0x13, 0x19, @@ -492,6 +517,50 @@ var fileDescriptor_07475747e6dcccdc = []byte{ 0x6a, 0xe6, 0x5a, 0xbf, 0x66, 0xae, 0x75, 0x31, 0x77, 0x4b, 0x57, 0x73, 0xb7, 0xf4, 0x7d, 0xee, 0x96, 0x3e, 0x3d, 0xfe, 0xf7, 0x35, 0xce, 0x16, 0xff, 0x28, 0x6c, 0x33, 0xfa, 0x0f, 0xf8, 0xde, 0x9f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x83, 0x7a, 0xe4, 0x41, 0xc6, 0x03, 0x00, 0x00, +======= + // 651 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0xc1, 0x4f, 0x13, 0x4f, + 0x14, 0xee, 0x96, 0xf2, 0x0b, 0x0c, 0xb4, 0xbf, 0xb8, 0xe1, 0xd0, 0xd4, 0xd0, 0x36, 0x55, 0x42, + 0x0f, 0xb8, 0x25, 0xd4, 0x60, 0xbc, 0x49, 0x0d, 0xa0, 0x26, 0x9a, 0xb0, 0x20, 0x1a, 0x13, 0xb3, + 0x99, 0xee, 0xbc, 0x6e, 0x27, 0xcc, 0xee, 0x94, 0xdd, 0x59, 0x02, 0xfe, 0x15, 0xdc, 0x3c, 0xfa, + 0xef, 0x70, 0xe4, 0x68, 0x3c, 0xa0, 0xc2, 0xff, 0xe0, 0xd9, 0xcc, 0x1b, 0x76, 0x4b, 0x81, 0x83, + 0x97, 0x66, 0xde, 0x37, 0xdf, 0xf7, 0xbd, 0xd7, 0x6f, 0x5e, 0x96, 0xd4, 0xd9, 0x09, 0x3b, 0x1e, + 0xc5, 0x52, 0x49, 0x5f, 0x8a, 0x4e, 0xa2, 0xa8, 0x4a, 0xcc, 0xaf, 0x83, 0xa0, 0x6d, 0xdf, 0xbc, + 0x77, 0xf0, 0xa6, 0xb6, 0x10, 0xc8, 0x40, 0x22, 0xd6, 0xd1, 0x27, 0xc3, 0xac, 0x35, 0x02, 0x29, + 0x03, 0x01, 0x1d, 0xac, 0xfa, 0xe9, 0xa0, 0xa3, 0x78, 0x08, 0x89, 0xa2, 0xe1, 0xc8, 0x10, 0x5a, + 0xbf, 0x2d, 0x42, 0x7a, 0x42, 0xfa, 0x07, 0xbb, 0xda, 0xc5, 0x7e, 0x4e, 0xa6, 0x07, 0x5c, 0x88, + 0xa4, 0x6a, 0x35, 0xa7, 0xda, 0x73, 0x6b, 0x8f, 0x9c, 0xbb, 0x9d, 0x9c, 0x31, 0xdd, 0xd9, 0xe2, + 0x42, 0xb8, 0x46, 0x51, 0xfb, 0x6a, 0x91, 0x92, 0xae, 0xed, 0x05, 0x32, 0xad, 0xe8, 0x01, 0xc4, + 0x55, 0xab, 0x69, 0xb5, 0x67, 0x5d, 0x53, 0x68, 0x34, 0x44, 0xb4, 0x68, 0x50, 0x2c, 0xec, 0x1a, + 0x99, 0x89, 0xa4, 0xe2, 0x32, 0xa2, 0xa2, 0x3a, 0xd5, 0xb4, 0xda, 0x25, 0x37, 0xaf, 0xed, 0x6d, + 0xd2, 0xa4, 0x83, 0x01, 0x17, 0x9c, 0x2a, 0xf0, 0x06, 0x00, 0x5e, 0x00, 0x11, 0xc4, 0x54, 0x01, + 0xf3, 0x0e, 0x53, 0x1a, 0xa9, 0x34, 0x4c, 0xaa, 0x25, 0xd4, 0x2c, 0xe6, 0xbc, 0x2d, 0x80, 0xed, + 0x8c, 0xb5, 0x73, 0x4d, 0x6a, 0xad, 0x93, 0x32, 0x8e, 0xfb, 0x16, 0x14, 0x65, 0x54, 0x51, 0x7b, + 0x89, 0x54, 0x54, 0x4c, 0xb9, 0xe0, 0x51, 0xe0, 0xc1, 0x48, 0xfa, 0x43, 0x1c, 0xb5, 0xec, 0x96, + 0x33, 0x74, 0x53, 0x83, 0xad, 0x3f, 0x16, 0x21, 0x78, 0x32, 0xd9, 0xbc, 0x21, 0x15, 0x24, 0x7b, + 0x10, 0x31, 0x4f, 0xe7, 0x88, 0xaa, 0xb9, 0xb5, 0x9a, 0x63, 0x42, 0x76, 0xb2, 0x90, 0x9d, 0xbd, + 0x2c, 0xe4, 0xde, 0xcc, 0xd9, 0x45, 0xa3, 0x70, 0xfa, 0xb3, 0x61, 0xb9, 0xf3, 0xa8, 0xdd, 0x8c, + 0x98, 0xbe, 0xb4, 0x7b, 0x64, 0x1a, 0xc3, 0xac, 0x16, 0x31, 0xe7, 0x95, 0xfb, 0x72, 0x1e, 0xb7, + 0x76, 0xde, 0x27, 0x10, 0x7f, 0xe0, 0xca, 0x54, 0xae, 0x91, 0xd6, 0x3e, 0x92, 0xf2, 0x04, 0x6e, + 0xdb, 0xa4, 0x94, 0x26, 0x79, 0xee, 0x78, 0xb6, 0xbb, 0xe3, 0x46, 0x7a, 0xd6, 0xc5, 0xfb, 0x1a, + 0x69, 0x97, 0x9b, 0xce, 0xad, 0x75, 0x32, 0xb7, 0x2d, 0x64, 0x9f, 0x0a, 0xe3, 0xbb, 0x4c, 0xfe, + 0xcf, 0x1e, 0xc5, 0x53, 0x31, 0x65, 0xc0, 0xb0, 0x45, 0xc9, 0xad, 0x64, 0xf0, 0x1e, 0xa2, 0xad, + 0xd3, 0x22, 0x99, 0xcd, 0xcd, 0x30, 0x65, 0xfd, 0xc8, 0x5e, 0xfe, 0xc2, 0x46, 0x55, 0x46, 0xf4, + 0x5d, 0xf6, 0xcc, 0x4b, 0xa4, 0x12, 0x4e, 0xd2, 0x8a, 0x86, 0x16, 0x4e, 0xd0, 0xf6, 0x49, 0x7b, + 0xbc, 0x0d, 0xdd, 0x55, 0xe6, 0xc5, 0x70, 0x04, 0x51, 0x7a, 0xef, 0x56, 0x98, 0x4d, 0x7a, 0x9c, + 0xf3, 0xbb, 0xab, 0xcc, 0x35, 0xec, 0x3b, 0xcb, 0x61, 0x7f, 0x26, 0x2b, 0xb7, 0x7d, 0x07, 0x10, + 0xc7, 0xc0, 0xbc, 0x23, 0x29, 0xd2, 0x10, 0xbc, 0xc3, 0x54, 0x2a, 0xb8, 0xbd, 0x71, 0xcb, 0x93, + 0xde, 0x46, 0xb1, 0x8f, 0x82, 0x1d, 0xcd, 0xcf, 0x77, 0xef, 0x9b, 0x45, 0x1e, 0xbc, 0xa4, 0xfe, + 0x10, 0xd8, 0xae, 0xfe, 0xdb, 0x1b, 0xa1, 0x4c, 0x23, 0x65, 0x87, 0xa4, 0x9c, 0xe8, 0x92, 0x79, + 0x14, 0x01, 0x4c, 0x66, 0xbe, 0xf7, 0x4a, 0x6f, 0xcb, 0x8f, 0x8b, 0xc6, 0x8b, 0x80, 0xab, 0x61, + 0xda, 0x77, 0x7c, 0x19, 0x76, 0x26, 0x3e, 0x05, 0x47, 0x4f, 0x9f, 0xf8, 0x43, 0xca, 0xa3, 0x4e, + 0x8e, 0x30, 0x75, 0x32, 0x82, 0xc4, 0xd9, 0x85, 0x98, 0x53, 0xc1, 0xbf, 0xd0, 0xbe, 0x80, 0xd7, + 0x91, 0x72, 0xe7, 0x8d, 0xfd, 0x75, 0xbb, 0x87, 0x64, 0xd6, 0xc7, 0x19, 0x3c, 0xaa, 0x30, 0xdd, + 0x29, 0x77, 0xc6, 0x00, 0x1b, 0xaa, 0xb7, 0x73, 0x76, 0x59, 0xb7, 0xce, 0x2f, 0xeb, 0xd6, 0xaf, + 0xcb, 0xba, 0x75, 0x7a, 0x55, 0x2f, 0x9c, 0x5f, 0xd5, 0x0b, 0xdf, 0xaf, 0xea, 0x85, 0x4f, 0xcf, + 0xfe, 0x7d, 0x8c, 0xe3, 0xeb, 0xaf, 0x14, 0x4e, 0xd3, 0xff, 0x0f, 0xf1, 0xee, 0xdf, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x5e, 0x82, 0xca, 0x98, 0xc8, 0x04, 0x00, 0x00, +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) } func (m *BlockStats) Marshal() (dAtA []byte, err error) { @@ -736,6 +805,19 @@ func (m *UserStats) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l +<<<<<<< HEAD +======= + if m.Affiliate_30DReferredVolumeQuoteQuantums != 0 { + i = encodeVarintStats(dAtA, i, uint64(m.Affiliate_30DReferredVolumeQuoteQuantums)) + i-- + dAtA[i] = 0x20 + } + if m.Affiliate_30DRevenueGeneratedQuantums != 0 { + i = encodeVarintStats(dAtA, i, uint64(m.Affiliate_30DRevenueGeneratedQuantums)) + i-- + dAtA[i] = 0x18 + } +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) if m.MakerNotional != 0 { i = encodeVarintStats(dAtA, i, uint64(m.MakerNotional)) i-- @@ -903,6 +985,15 @@ func (m *UserStats) Size() (n int) { if m.MakerNotional != 0 { n += 1 + sovStats(uint64(m.MakerNotional)) } +<<<<<<< HEAD +======= + if m.Affiliate_30DRevenueGeneratedQuantums != 0 { + n += 1 + sovStats(uint64(m.Affiliate_30DRevenueGeneratedQuantums)) + } + if m.Affiliate_30DReferredVolumeQuoteQuantums != 0 { + n += 1 + sovStats(uint64(m.Affiliate_30DReferredVolumeQuoteQuantums)) + } +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) return n } @@ -1583,6 +1674,47 @@ func (m *UserStats) Unmarshal(dAtA []byte) error { break } } +<<<<<<< HEAD +======= + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Affiliate_30DRevenueGeneratedQuantums", wireType) + } + m.Affiliate_30DRevenueGeneratedQuantums = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStats + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Affiliate_30DRevenueGeneratedQuantums |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 4: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Affiliate_30DReferredVolumeQuoteQuantums", wireType) + } + m.Affiliate_30DReferredVolumeQuoteQuantums = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStats + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Affiliate_30DReferredVolumeQuoteQuantums |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } +>>>>>>> 1b536022 (Integrate commission and overrides to fee tier calculation (#3117)) default: iNdEx = preIndex skippy, err := skipStats(dAtA[iNdEx:]) diff --git a/protocol/x/vault/keeper/vault_test.go b/protocol/x/vault/keeper/vault_test.go index aa47b8dbee..60148241dc 100644 --- a/protocol/x/vault/keeper/vault_test.go +++ b/protocol/x/vault/keeper/vault_test.go @@ -9,6 +9,7 @@ import ( testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" testutil "github.com/dydxprotocol/v4-chain/protocol/testutil/util" + affiliatetypes "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/types" assettypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/types" feetierstypes "github.com/dydxprotocol/v4-chain/protocol/x/feetiers/types" satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" @@ -329,16 +330,25 @@ func TestVaultIsBestFeeTier(t *testing.T) { vaultClob1Address := constants.Vault_Clob1.ToModuleAccountAddress() aliceAddress := constants.AliceAccAddress.String() + require.NoError(t, tApp.App.AffiliatesKeeper.UpdateAffiliateParameters( + ctx, + &affiliatetypes.MsgUpdateAffiliateParameters{ + AffiliateParameters: affiliatetypes.AffiliateParameters{ + RefereeMinimumFeeTierIdx: 2, + }, + }, + )) + // Vault in genesis state should be in best fee tier. - takerFee := tApp.App.FeeTiersKeeper.GetPerpetualFeePpm(ctx, vaultClob0Address, true) + takerFee := tApp.App.FeeTiersKeeper.GetPerpetualFeePpm(ctx, vaultClob0Address, true, 2) require.Equal(t, int32(11), takerFee) - makerFee := tApp.App.FeeTiersKeeper.GetPerpetualFeePpm(ctx, vaultClob0Address, false) + makerFee := tApp.App.FeeTiersKeeper.GetPerpetualFeePpm(ctx, vaultClob0Address, false, 2) require.Equal(t, int32(1), makerFee) // A regular user Alice should be in worst fee tier. - takerFee = tApp.App.FeeTiersKeeper.GetPerpetualFeePpm(ctx, aliceAddress, true) + takerFee = tApp.App.FeeTiersKeeper.GetPerpetualFeePpm(ctx, aliceAddress, true, 2) require.Equal(t, int32(33), takerFee) - makerFee = tApp.App.FeeTiersKeeper.GetPerpetualFeePpm(ctx, aliceAddress, false) + makerFee = tApp.App.FeeTiersKeeper.GetPerpetualFeePpm(ctx, aliceAddress, false, 2) require.Equal(t, int32(3), makerFee) // A newly allocated-to vault should be in best fee tier. @@ -363,9 +373,9 @@ func TestVaultIsBestFeeTier(t *testing.T) { uint32(ctx.BlockHeight())+1, testapp.AdvanceToBlockOptions{}, ) - takerFee = tApp.App.FeeTiersKeeper.GetPerpetualFeePpm(ctx, vaultClob1Address, true) + takerFee = tApp.App.FeeTiersKeeper.GetPerpetualFeePpm(ctx, vaultClob1Address, true, 2) require.Equal(t, int32(11), takerFee) - makerFee = tApp.App.FeeTiersKeeper.GetPerpetualFeePpm(ctx, vaultClob1Address, false) + makerFee = tApp.App.FeeTiersKeeper.GetPerpetualFeePpm(ctx, vaultClob1Address, false, 2) require.Equal(t, int32(1), makerFee) }