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 f6ded433c8a..ace13fa1bcc 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 17ec53fdf64..285720a4054 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 1cdebd2f257..4ccbb18adab 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 1f41aed7f6b..f6ae2bb9606 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 aba3335b9d4..c577bf0554f 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 4dd0dd22918..a653747ed29 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 9e2c612e70c..8d2d4fd6e77 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 4d1558fa1b7..53b15219a77 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 a249eda009a..6a78da46d74 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 0ffa88aba81..0e70dedd029 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 00000000000..97cbb4b5887 --- /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 6e78202357d..d9bcb64415b 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 3fe44a2f9b8..5e00230942d 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 bdf43259ccc..d1c68c90684 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 eeb19ca0844..22e3722ed91 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 685f8082bda..5bbdf3abdd2 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 aadc88b1f56..1472442b3c7 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 bad68af1416..e5847c5819d 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 ea3b786d5d0..c1f130cff4a 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 d6c1aaf6206..91e5f56e216 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 7a0dd77d58f..76d8fe9b55b 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 a0c14b3fc0f..6ba71ec0683 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 fd6c3d4ec78..0d27c8d5866 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 84185382a8e..b7040f72733 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 23f0e35c936..dc01622dd1f 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 52cb1bfd3a6..f64e09592c8 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 5a889d1f9de..e745218ce24 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 66011198889..86b81184bc8 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 788eb5c9e00..8d685c87fe3 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 b92c4d304a8..ab9734d220c 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 0fac6ad5611..5510b780ea4 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 890eab489a8..2f54c3e334b 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 0965b44e1dd..780d250b87a 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 5ba1fd76dbb..8bf4367d745 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 8bb7e8ceb5c..48c231f2acb 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 5a97d91c5c5..c94f3f710ab 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 3c43a440472..11e65ffdc2f 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 5b0b0f13b5d..8a7303996d3 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 7d785c2f174..70e10cf4845 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 98ce7d3086a..26791e756d9 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 56c94f72378..d47965c25b0 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 f9a17782b39..0c3c04d0415 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 348d5f478d6..82cbd1c2e03 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 11dac305c2d..eb30eef8271 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 483935a5f4d..f65241ae9c7 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 0ee8ff85d31..a0b2ed61dbe 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 4dc2bc40338..93e62384ee3 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 7c0a6f59a9a..f16ce6f1748 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 aa47b8dbee4..60148241dc0 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) }