From 589578fa05771aefa1bb22f514cbc7dca28575d4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 15:09:26 -0500 Subject: [PATCH 01/14] [ENG-1305] Create new governance message for account level transfer (backport #3244) (#3257) Co-authored-by: David Li --- .../codegen/dydxprotocol/sending/transfer.ts | 111 +++++ .../dydxprotocol/sending/tx.rpc.msg.ts | 17 +- .../src/codegen/dydxprotocol/sending/tx.ts | 46 ++ proto/dydxprotocol/sending/transfer.proto | 18 + proto/dydxprotocol/sending/tx.proto | 8 + protocol/app/msgs/all_msgs.go | 18 +- protocol/app/msgs/internal_msgs.go | 6 +- protocol/app/msgs/internal_msgs_test.go | 2 + protocol/lib/ante/internal_msg.go | 1 + protocol/lib/metrics/constants.go | 1 + protocol/mocks/SendingKeeper.go | 18 + .../keeper/msg_server_create_transfer.go | 39 ++ .../keeper/msg_server_create_transfer_test.go | 72 ++++ protocol/x/sending/keeper/transfer.go | 16 + protocol/x/sending/keeper/transfer_test.go | 144 ++++++- protocol/x/sending/module_test.go | 5 +- protocol/x/sending/types/expected_keepers.go | 6 + protocol/x/sending/types/transfer.pb.go | 400 ++++++++++++++++-- protocol/x/sending/types/tx.pb.go | 209 ++++++++- protocol/x/sending/types/types.go | 4 + 20 files changed, 1069 insertions(+), 72 deletions(-) diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/sending/transfer.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/sending/transfer.ts index c3eb7b4219..262605d0df 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/sending/transfer.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/sending/transfer.ts @@ -148,6 +148,42 @@ export interface MsgSendFromModuleToAccountSDKType { coin?: CoinSDKType; } +/** + * MsgSendFromAccountToAccount represents a single transfer from one + * `x/bank` account to another `x/bank` account. + * Should only be executed by governance. + */ + +export interface MsgSendFromAccountToAccount { + authority: string; + /** The sender account address. */ + + sender: string; + /** The recipient account address. */ + + recipient: string; + /** The coin to transfer, which specifies both denom and amount. */ + + coin?: Coin; +} +/** + * MsgSendFromAccountToAccount represents a single transfer from one + * `x/bank` account to another `x/bank` account. + * Should only be executed by governance. + */ + +export interface MsgSendFromAccountToAccountSDKType { + authority: string; + /** The sender account address. */ + + sender: string; + /** The recipient account address. */ + + recipient: string; + /** The coin to transfer, which specifies both denom and amount. */ + + coin?: CoinSDKType; +} function createBaseTransfer(): Transfer { return { @@ -447,4 +483,79 @@ export const MsgSendFromModuleToAccount = { return message; } +}; + +function createBaseMsgSendFromAccountToAccount(): MsgSendFromAccountToAccount { + return { + authority: "", + sender: "", + recipient: "", + coin: undefined + }; +} + +export const MsgSendFromAccountToAccount = { + encode(message: MsgSendFromAccountToAccount, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.authority !== "") { + writer.uint32(10).string(message.authority); + } + + if (message.sender !== "") { + writer.uint32(18).string(message.sender); + } + + if (message.recipient !== "") { + writer.uint32(26).string(message.recipient); + } + + if (message.coin !== undefined) { + Coin.encode(message.coin, writer.uint32(34).fork()).ldelim(); + } + + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MsgSendFromAccountToAccount { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgSendFromAccountToAccount(); + + while (reader.pos < end) { + const tag = reader.uint32(); + + switch (tag >>> 3) { + case 1: + message.authority = reader.string(); + break; + + case 2: + message.sender = reader.string(); + break; + + case 3: + message.recipient = reader.string(); + break; + + case 4: + message.coin = Coin.decode(reader, reader.uint32()); + break; + + default: + reader.skipType(tag & 7); + break; + } + } + + return message; + }, + + fromPartial(object: DeepPartial): MsgSendFromAccountToAccount { + const message = createBaseMsgSendFromAccountToAccount(); + message.authority = object.authority ?? ""; + message.sender = object.sender ?? ""; + message.recipient = object.recipient ?? ""; + message.coin = object.coin !== undefined && object.coin !== null ? Coin.fromPartial(object.coin) : undefined; + return message; + } + }; \ No newline at end of file diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/sending/tx.rpc.msg.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/sending/tx.rpc.msg.ts index 857d693706..b5f73c084b 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/sending/tx.rpc.msg.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/sending/tx.rpc.msg.ts @@ -1,7 +1,7 @@ -import { MsgDepositToSubaccount, MsgWithdrawFromSubaccount, MsgSendFromModuleToAccount } from "./transfer"; +import { MsgDepositToSubaccount, MsgWithdrawFromSubaccount, MsgSendFromModuleToAccount, MsgSendFromAccountToAccount } from "./transfer"; import { Rpc } from "../../helpers"; import * as _m0 from "protobufjs/minimal"; -import { MsgCreateTransfer, MsgCreateTransferResponse, MsgDepositToSubaccountResponse, MsgWithdrawFromSubaccountResponse, MsgSendFromModuleToAccountResponse } from "./tx"; +import { MsgCreateTransfer, MsgCreateTransferResponse, MsgDepositToSubaccountResponse, MsgWithdrawFromSubaccountResponse, MsgSendFromModuleToAccountResponse, MsgSendFromAccountToAccountResponse } from "./tx"; /** Msg defines the Msg service. */ export interface Msg { @@ -25,6 +25,12 @@ export interface Msg { */ sendFromModuleToAccount(request: MsgSendFromModuleToAccount): Promise; + /** + * SendFromAccountToAccount initiates a new transfer from an `x/bank` account + * to another `x/bank` account (should only be executed by governance). + */ + + sendFromAccountToAccount(request: MsgSendFromAccountToAccount): Promise; } export class MsgClientImpl implements Msg { private readonly rpc: Rpc; @@ -35,6 +41,7 @@ export class MsgClientImpl implements Msg { this.depositToSubaccount = this.depositToSubaccount.bind(this); this.withdrawFromSubaccount = this.withdrawFromSubaccount.bind(this); this.sendFromModuleToAccount = this.sendFromModuleToAccount.bind(this); + this.sendFromAccountToAccount = this.sendFromAccountToAccount.bind(this); } createTransfer(request: MsgCreateTransfer): Promise { @@ -61,4 +68,10 @@ export class MsgClientImpl implements Msg { return promise.then(data => MsgSendFromModuleToAccountResponse.decode(new _m0.Reader(data))); } + sendFromAccountToAccount(request: MsgSendFromAccountToAccount): Promise { + const data = MsgSendFromAccountToAccount.encode(request).finish(); + const promise = this.rpc.request("dydxprotocol.sending.Msg", "SendFromAccountToAccount", data); + return promise.then(data => MsgSendFromAccountToAccountResponse.decode(new _m0.Reader(data))); + } + } \ No newline at end of file diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/sending/tx.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/sending/tx.ts index 8ca72ce016..c1a4a80fca 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/sending/tx.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/sending/tx.ts @@ -55,6 +55,18 @@ export interface MsgSendFromModuleToAccountResponse {} */ export interface MsgSendFromModuleToAccountResponseSDKType {} +/** + * MsgSendFromAccountToAccountResponse is a response type used for new + * account-to-account transfers. + */ + +export interface MsgSendFromAccountToAccountResponse {} +/** + * MsgSendFromAccountToAccountResponse is a response type used for new + * account-to-account transfers. + */ + +export interface MsgSendFromAccountToAccountResponseSDKType {} function createBaseMsgCreateTransfer(): MsgCreateTransfer { return { @@ -235,4 +247,38 @@ export const MsgSendFromModuleToAccountResponse = { return message; } +}; + +function createBaseMsgSendFromAccountToAccountResponse(): MsgSendFromAccountToAccountResponse { + return {}; +} + +export const MsgSendFromAccountToAccountResponse = { + encode(_: MsgSendFromAccountToAccountResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MsgSendFromAccountToAccountResponse { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgSendFromAccountToAccountResponse(); + + while (reader.pos < end) { + const tag = reader.uint32(); + + switch (tag >>> 3) { + default: + reader.skipType(tag & 7); + break; + } + } + + return message; + }, + + fromPartial(_: DeepPartial): MsgSendFromAccountToAccountResponse { + const message = createBaseMsgSendFromAccountToAccountResponse(); + return message; + } + }; \ No newline at end of file diff --git a/proto/dydxprotocol/sending/transfer.proto b/proto/dydxprotocol/sending/transfer.proto index 5feea351b1..768da8369c 100644 --- a/proto/dydxprotocol/sending/transfer.proto +++ b/proto/dydxprotocol/sending/transfer.proto @@ -83,3 +83,21 @@ message MsgSendFromModuleToAccount { // The coin to transfer, which specifies both denom and amount. cosmos.base.v1beta1.Coin coin = 4 [ (gogoproto.nullable) = false ]; } + +// MsgSendFromAccountToAccount represents a single transfer from one +// `x/bank` account to another `x/bank` account. +// Should only be executed by governance. +message MsgSendFromAccountToAccount { + // Authority is the address that controls the module. + option (cosmos.msg.v1.signer) = "authority"; + string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // The sender account address. + string sender = 2 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // The recipient account address. + string recipient = 3 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; + + // The coin to transfer, which specifies both denom and amount. + cosmos.base.v1beta1.Coin coin = 4 [ (gogoproto.nullable) = false ]; +} diff --git a/proto/dydxprotocol/sending/tx.proto b/proto/dydxprotocol/sending/tx.proto index 028845afe4..0b65cfd897 100644 --- a/proto/dydxprotocol/sending/tx.proto +++ b/proto/dydxprotocol/sending/tx.proto @@ -21,6 +21,10 @@ service Msg { // `x/bank` account (should only be executed by governance). rpc SendFromModuleToAccount(MsgSendFromModuleToAccount) returns (MsgSendFromModuleToAccountResponse); + // SendFromAccountToAccount initiates a new transfer from an `x/bank` account + // to another `x/bank` account (should only be executed by governance). + rpc SendFromAccountToAccount(MsgSendFromAccountToAccount) + returns (MsgSendFromAccountToAccountResponse); } // MsgCreateTransfer is a request type used for initiating new transfers. @@ -40,3 +44,7 @@ message MsgWithdrawFromSubaccountResponse {} // MsgSendFromModuleToAccountResponse is a response type used for new // module-to-account transfers. message MsgSendFromModuleToAccountResponse {} + +// MsgSendFromAccountToAccountResponse is a response type used for new +// account-to-account transfers. +message MsgSendFromAccountToAccountResponse {} \ No newline at end of file diff --git a/protocol/app/msgs/all_msgs.go b/protocol/app/msgs/all_msgs.go index 06a6dba8e6..ec090ca1dd 100644 --- a/protocol/app/msgs/all_msgs.go +++ b/protocol/app/msgs/all_msgs.go @@ -262,14 +262,16 @@ var ( "/dydxprotocol.ratelimit.MsgSetLimitParamsResponse": {}, // sending - "/dydxprotocol.sending.MsgCreateTransfer": {}, - "/dydxprotocol.sending.MsgCreateTransferResponse": {}, - "/dydxprotocol.sending.MsgDepositToSubaccount": {}, - "/dydxprotocol.sending.MsgDepositToSubaccountResponse": {}, - "/dydxprotocol.sending.MsgWithdrawFromSubaccount": {}, - "/dydxprotocol.sending.MsgWithdrawFromSubaccountResponse": {}, - "/dydxprotocol.sending.MsgSendFromModuleToAccount": {}, - "/dydxprotocol.sending.MsgSendFromModuleToAccountResponse": {}, + "/dydxprotocol.sending.MsgCreateTransfer": {}, + "/dydxprotocol.sending.MsgCreateTransferResponse": {}, + "/dydxprotocol.sending.MsgDepositToSubaccount": {}, + "/dydxprotocol.sending.MsgDepositToSubaccountResponse": {}, + "/dydxprotocol.sending.MsgWithdrawFromSubaccount": {}, + "/dydxprotocol.sending.MsgWithdrawFromSubaccountResponse": {}, + "/dydxprotocol.sending.MsgSendFromModuleToAccount": {}, + "/dydxprotocol.sending.MsgSendFromModuleToAccountResponse": {}, + "/dydxprotocol.sending.MsgSendFromAccountToAccount": {}, + "/dydxprotocol.sending.MsgSendFromAccountToAccountResponse": {}, // stats "/dydxprotocol.stats.MsgUpdateParams": {}, diff --git a/protocol/app/msgs/internal_msgs.go b/protocol/app/msgs/internal_msgs.go index 94ec2ac61a..158a26c54e 100644 --- a/protocol/app/msgs/internal_msgs.go +++ b/protocol/app/msgs/internal_msgs.go @@ -210,8 +210,10 @@ var ( "/dydxprotocol.rewards.MsgUpdateParamsResponse": nil, // sending - "/dydxprotocol.sending.MsgSendFromModuleToAccount": &sending.MsgSendFromModuleToAccount{}, - "/dydxprotocol.sending.MsgSendFromModuleToAccountResponse": nil, + "/dydxprotocol.sending.MsgSendFromModuleToAccount": &sending.MsgSendFromModuleToAccount{}, + "/dydxprotocol.sending.MsgSendFromModuleToAccountResponse": nil, + "/dydxprotocol.sending.MsgSendFromAccountToAccount": &sending.MsgSendFromAccountToAccount{}, + "/dydxprotocol.sending.MsgSendFromAccountToAccountResponse": nil, // stats "/dydxprotocol.stats.MsgUpdateParams": &stats.MsgUpdateParams{}, diff --git a/protocol/app/msgs/internal_msgs_test.go b/protocol/app/msgs/internal_msgs_test.go index 3fb3a56e8f..9f74e7832d 100644 --- a/protocol/app/msgs/internal_msgs_test.go +++ b/protocol/app/msgs/internal_msgs_test.go @@ -166,6 +166,8 @@ func TestInternalMsgSamples_Gov_Key(t *testing.T) { "/dydxprotocol.rewards.MsgUpdateParamsResponse", // sending + "/dydxprotocol.sending.MsgSendFromAccountToAccount", + "/dydxprotocol.sending.MsgSendFromAccountToAccountResponse", "/dydxprotocol.sending.MsgSendFromModuleToAccount", "/dydxprotocol.sending.MsgSendFromModuleToAccountResponse", diff --git a/protocol/lib/ante/internal_msg.go b/protocol/lib/ante/internal_msg.go index dc6ef13006..58f9e53d55 100644 --- a/protocol/lib/ante/internal_msg.go +++ b/protocol/lib/ante/internal_msg.go @@ -133,6 +133,7 @@ func IsInternalMsg(msg sdk.Msg) bool { // sending *sending.MsgSendFromModuleToAccount, + *sending.MsgSendFromAccountToAccount, // stats *stats.MsgUpdateParams, diff --git a/protocol/lib/metrics/constants.go b/protocol/lib/metrics/constants.go index 406d278dc9..f343f43e9f 100644 --- a/protocol/lib/metrics/constants.go +++ b/protocol/lib/metrics/constants.go @@ -292,6 +292,7 @@ const ( ProcessDepositToSubaccount = "process_deposit_to_subaccount" ProcessWithdrawFromSubaccount = "process_withdraw_from_subaccount" SendFromModuleToAccount = "send_from_module_to_account" + SendFromAccountToAccount = "send_from_account_to_account" AssetId = "asset_id" SenderAddress = "sender_address" SenderModuleName = "sender_module_name" diff --git a/protocol/mocks/SendingKeeper.go b/protocol/mocks/SendingKeeper.go index 53a1d36e00..747275955b 100644 --- a/protocol/mocks/SendingKeeper.go +++ b/protocol/mocks/SendingKeeper.go @@ -86,6 +86,24 @@ func (_m *SendingKeeper) ProcessWithdrawFromSubaccount(ctx cosmos_sdktypes.Conte return r0 } +// SendFromAccountToAccount provides a mock function with given fields: ctx, msg +func (_m *SendingKeeper) SendFromAccountToAccount(ctx cosmos_sdktypes.Context, msg *types.MsgSendFromAccountToAccount) error { + ret := _m.Called(ctx, msg) + + if len(ret) == 0 { + panic("no return value specified for SendFromAccountToAccount") + } + + var r0 error + if rf, ok := ret.Get(0).(func(cosmos_sdktypes.Context, *types.MsgSendFromAccountToAccount) error); ok { + r0 = rf(ctx, msg) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // SendFromModuleToAccount provides a mock function with given fields: ctx, msg func (_m *SendingKeeper) SendFromModuleToAccount(ctx cosmos_sdktypes.Context, msg *types.MsgSendFromModuleToAccount) error { ret := _m.Called(ctx, msg) diff --git a/protocol/x/sending/keeper/msg_server_create_transfer.go b/protocol/x/sending/keeper/msg_server_create_transfer.go index cec8c127ca..1b5883aa50 100644 --- a/protocol/x/sending/keeper/msg_server_create_transfer.go +++ b/protocol/x/sending/keeper/msg_server_create_transfer.go @@ -136,3 +136,42 @@ func (k msgServer) SendFromModuleToAccount( return &types.MsgSendFromModuleToAccountResponse{}, nil } + +// SendFromAccountToAccount sends coins from one account to another account. +func (k msgServer) SendFromAccountToAccount( + goCtx context.Context, + msg *types.MsgSendFromAccountToAccount, +) (*types.MsgSendFromAccountToAccountResponse, error) { + if !k.Keeper.HasAuthority(msg.Authority) { + return nil, errors.Wrapf( + govtypes.ErrInvalidSigner, + "invalid authority %s", + msg.Authority, + ) + } + + // Validate sender address. + if _, err := sdk.AccAddressFromBech32(msg.Sender); err != nil { + return nil, types.ErrInvalidAccountAddress + } + + // Validate recipient address. + if _, err := sdk.AccAddressFromBech32(msg.Recipient); err != nil { + return nil, types.ErrInvalidAccountAddress + } + + // Validate coin. + if err := msg.Coin.Validate(); err != nil { + return nil, err + } + + ctx := lib.UnwrapSDKContext(goCtx, types.ModuleName) + + if err := k.Keeper.SendFromAccountToAccount(ctx, msg); err != nil { + telemetry.IncrCounter(1, types.ModuleName, metrics.SendFromAccountToAccount, metrics.Error) + return nil, err + } + telemetry.IncrCounter(1, types.ModuleName, metrics.SendFromAccountToAccount, metrics.Success) + + return &types.MsgSendFromAccountToAccountResponse{}, nil +} diff --git a/protocol/x/sending/keeper/msg_server_create_transfer_test.go b/protocol/x/sending/keeper/msg_server_create_transfer_test.go index d60bd86a1a..3ee7b965cd 100644 --- a/protocol/x/sending/keeper/msg_server_create_transfer_test.go +++ b/protocol/x/sending/keeper/msg_server_create_transfer_test.go @@ -325,3 +325,75 @@ func TestMsgServerSendFromModuleToAccount(t *testing.T) { }) } } + +func TestMsgServerSendFromAccountToAccount_Validation(t *testing.T) { + tests := map[string]struct { + msg *types.MsgSendFromAccountToAccount + expectedErrContains string + }{ + "invalid authority": { + msg: &types.MsgSendFromAccountToAccount{ + Authority: "invalid", + Sender: constants.AliceAccAddress.String(), + Recipient: constants.BobAccAddress.String(), + Coin: sdk.NewCoin("adv4tnt", sdkmath.NewInt(100)), + }, + expectedErrContains: "invalid authority", + }, + "empty sender": { + msg: &types.MsgSendFromAccountToAccount{ + Authority: lib.GovModuleAddress.String(), + Sender: "", + Recipient: constants.BobAccAddress.String(), + Coin: sdk.NewCoin("adv4tnt", sdkmath.NewInt(100)), + }, + expectedErrContains: "Account address is invalid", + }, + "invalid sender": { + msg: &types.MsgSendFromAccountToAccount{ + Authority: lib.GovModuleAddress.String(), + Sender: "invalid", + Recipient: constants.BobAccAddress.String(), + Coin: sdk.NewCoin("adv4tnt", sdkmath.NewInt(100)), + }, + expectedErrContains: "Account address is invalid", + }, + "empty recipient": { + msg: &types.MsgSendFromAccountToAccount{ + Authority: lib.GovModuleAddress.String(), + Sender: constants.AliceAccAddress.String(), + Recipient: "", + Coin: sdk.NewCoin("adv4tnt", sdkmath.NewInt(100)), + }, + expectedErrContains: "Account address is invalid", + }, + "invalid recipient": { + msg: &types.MsgSendFromAccountToAccount{ + Authority: lib.GovModuleAddress.String(), + Sender: constants.AliceAccAddress.String(), + Recipient: "invalid", + Coin: sdk.NewCoin("adv4tnt", sdkmath.NewInt(100)), + }, + expectedErrContains: "Account address is invalid", + }, + "invalid coin": { + msg: &types.MsgSendFromAccountToAccount{ + Authority: lib.GovModuleAddress.String(), + Sender: constants.AliceAccAddress.String(), + Recipient: constants.BobAccAddress.String(), + Coin: sdk.Coin{Denom: "", Amount: sdkmath.NewInt(100)}, + }, + expectedErrContains: "invalid denom", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + ks := keepertest.SendingKeepers(t) + msgServer := keeper.NewMsgServerImpl(ks.SendingKeeper) + + _, err := msgServer.SendFromAccountToAccount(ks.Ctx, tc.msg) + require.ErrorContains(t, err, tc.expectedErrContains) + }) + } +} diff --git a/protocol/x/sending/keeper/transfer.go b/protocol/x/sending/keeper/transfer.go index d1ad32f452..809b8c7004 100644 --- a/protocol/x/sending/keeper/transfer.go +++ b/protocol/x/sending/keeper/transfer.go @@ -217,3 +217,19 @@ func (k Keeper) SendFromModuleToAccount( sdk.NewCoins(msg.Coin), ) } + +// SendFromAccountToAccount sends coins from one `x/bank` account to another `x/bank` account. +func (k Keeper) SendFromAccountToAccount( + ctx sdk.Context, + msg *types.MsgSendFromAccountToAccount, +) (err error) { + senderAddr := sdk.MustAccAddressFromBech32(msg.GetSender()) + recipientAddr := sdk.MustAccAddressFromBech32(msg.GetRecipient()) + + return k.bankKeeper.SendCoins( + ctx, + senderAddr, + recipientAddr, + sdk.NewCoins(msg.Coin), + ) +} diff --git a/protocol/x/sending/keeper/transfer_test.go b/protocol/x/sending/keeper/transfer_test.go index 10c4f0d3c4..2bbce2266d 100644 --- a/protocol/x/sending/keeper/transfer_test.go +++ b/protocol/x/sending/keeper/transfer_test.go @@ -501,7 +501,7 @@ func TestSendFromModuleToAccount(t *testing.T) { initialModuleBalance: 100, balanceToSend: 101, recipientAddress: constants.AliceAccAddress.String(), - expectedErrContains: "insufficient fund", + expectedErrContains: "insufficient funds", }, } for name, tc := range tests { @@ -633,3 +633,145 @@ func TestSendFromModuleToAccount_InvalidRecipient(t *testing.T) { ) require.ErrorContains(t, err, "Account address is invalid") } + +func TestSendFromAccountToAccount(t *testing.T) { + testDenom := "TestSendFromAccountToAccount/Coin" + testModuleName := "bridge" + tests := map[string]struct { + // Setup. + initialSenderBalance int64 + balanceToSend int64 + senderAddress string + recipientAddress string + // Expectations + expectedErrContains string + }{ + "Success - send between user accounts": { + initialSenderBalance: 1000, + balanceToSend: 100, + senderAddress: constants.AliceAccAddress.String(), + recipientAddress: constants.BobAccAddress.String(), + }, + "Success - send to module account": { + initialSenderBalance: 1000, + balanceToSend: 100, + senderAddress: constants.AliceAccAddress.String(), + recipientAddress: authtypes.NewModuleAddress("community_treasury").String(), + }, + "Success - send to self": { + initialSenderBalance: 1000, + balanceToSend: 100, + senderAddress: constants.AliceAccAddress.String(), + recipientAddress: constants.AliceAccAddress.String(), + }, + "Success - send 0 amount": { + initialSenderBalance: 700, + balanceToSend: 0, + senderAddress: constants.AliceAccAddress.String(), + recipientAddress: constants.BobAccAddress.String(), + }, + "Error - insufficient funds": { + initialSenderBalance: 100, + balanceToSend: 101, + senderAddress: constants.AliceAccAddress.String(), + recipientAddress: constants.BobAccAddress.String(), + expectedErrContains: "insufficient funds", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // Initiate keepers and fund sender with initial balance. + ks := keepertest.SendingKeepers(t) + ctx, sendingKeeper, bankKeeper := ks.Ctx, ks.SendingKeeper, ks.BankKeeper + + senderAddr := sdk.MustAccAddressFromBech32(tc.senderAddress) + + // Mint coins to bridging module and transfer to sender account + err := bankKeeper.MintCoins( + ctx, + testModuleName, + sdk.NewCoins(sdk.NewCoin(testDenom, sdkmath.NewInt(tc.initialSenderBalance))), + ) + require.NoError(t, err) + err = bankKeeper.SendCoinsFromModuleToAccount( + ctx, + testModuleName, + senderAddr, + sdk.NewCoins(sdk.NewCoin(testDenom, sdkmath.NewInt(tc.initialSenderBalance))), + ) + require.NoError(t, err) + + startingSenderBalance := bankKeeper.GetBalance( + ctx, + senderAddr, + testDenom, + ) + startingRecipientBalance := bankKeeper.GetBalance( + ctx, + sdk.MustAccAddressFromBech32(tc.recipientAddress), + testDenom, + ) + + // Send coins from account to account. + err = sendingKeeper.SendFromAccountToAccount( + ctx, + &types.MsgSendFromAccountToAccount{ + Authority: lib.GovModuleAddress.String(), + Sender: tc.senderAddress, + Recipient: tc.recipientAddress, + Coin: sdk.NewCoin(testDenom, sdkmath.NewInt(tc.balanceToSend)), + }, + ) + + // Verify ending balances and error. + endingSenderBalance := bankKeeper.GetBalance( + ctx, + senderAddr, + testDenom, + ) + endingRecipientBalance := bankKeeper.GetBalance( + ctx, + sdk.MustAccAddressFromBech32(tc.recipientAddress), + testDenom, + ) + + if tc.expectedErrContains != "" { // if error should occur. + // Verify that error is as expected. + require.ErrorContains(t, err, tc.expectedErrContains) + // Verify that sender balance is unchanged. + require.Equal( + t, + startingSenderBalance.Amount.Int64(), + endingSenderBalance.Amount.Int64(), + ) + // Verify that recipient balance is unchanged. + require.Equal( + t, + startingRecipientBalance.Amount.Int64(), + endingRecipientBalance.Amount.Int64(), + ) + } else { // if send should succeed. + // Verify that no error occurred. + require.NoError(t, err) + if tc.senderAddress == tc.recipientAddress { + // If account sent to itself, verify that balance is unchanged. + require.Equal(t, startingSenderBalance, endingSenderBalance) + } else { + // Otherwise, verify that sender balance is reduced by amount sent. + require.Equal( + t, + startingSenderBalance.Amount.Int64()-tc.balanceToSend, + endingSenderBalance.Amount.Int64(), + ) + // Verify that recipient balance is increased by amount sent. + require.Equal( + t, + startingRecipientBalance.Amount.Int64()+tc.balanceToSend, + endingRecipientBalance.Amount.Int64(), + ) + } + } + }) + } +} diff --git a/protocol/x/sending/module_test.go b/protocol/x/sending/module_test.go index 47b13c2ad2..bc3f495c87 100644 --- a/protocol/x/sending/module_test.go +++ b/protocol/x/sending/module_test.go @@ -3,12 +3,13 @@ package sending_test import ( "bytes" "encoding/json" - "github.com/dydxprotocol/v4-chain/protocol/app/module" "net/http" "net/http/httptest" "reflect" "testing" + "github.com/dydxprotocol/v4-chain/protocol/app/module" + "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" "github.com/cosmos/cosmos-sdk/codec/types" @@ -79,7 +80,7 @@ func TestAppModuleBasic_RegisterInterfaces(t *testing.T) { // due to it using an unexported method on the interface thus we use reflection to access the field // directly that contains the registrations. fv := reflect.ValueOf(registry).Elem().FieldByName("implInterfaces") - require.Len(t, fv.MapKeys(), 8) + require.Len(t, fv.MapKeys(), 10) } func TestAppModuleBasic_DefaultGenesis(t *testing.T) { diff --git a/protocol/x/sending/types/expected_keepers.go b/protocol/x/sending/types/expected_keepers.go index bbd7141af6..9c78225272 100644 --- a/protocol/x/sending/types/expected_keepers.go +++ b/protocol/x/sending/types/expected_keepers.go @@ -83,4 +83,10 @@ type BankKeeper interface { recipientAddr sdk.AccAddress, amt sdk.Coins, ) error + SendCoins( + ctx context.Context, + fromAddr sdk.AccAddress, + toAddr sdk.AccAddress, + amt sdk.Coins, + ) error } diff --git a/protocol/x/sending/types/transfer.pb.go b/protocol/x/sending/types/transfer.pb.go index e5aaee211d..ff28f2d06f 100644 --- a/protocol/x/sending/types/transfer.pb.go +++ b/protocol/x/sending/types/transfer.pb.go @@ -324,11 +324,86 @@ func (m *MsgSendFromModuleToAccount) GetCoin() types1.Coin { return types1.Coin{} } +// MsgSendFromAccountToAccount represents a single transfer from one +// `x/bank` account to another `x/bank` account. +// Should only be executed by governance. +type MsgSendFromAccountToAccount struct { + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // The sender account address. + Sender string `protobuf:"bytes,2,opt,name=sender,proto3" json:"sender,omitempty"` + // The recipient account address. + Recipient string `protobuf:"bytes,3,opt,name=recipient,proto3" json:"recipient,omitempty"` + // The coin to transfer, which specifies both denom and amount. + Coin types1.Coin `protobuf:"bytes,4,opt,name=coin,proto3" json:"coin"` +} + +func (m *MsgSendFromAccountToAccount) Reset() { *m = MsgSendFromAccountToAccount{} } +func (m *MsgSendFromAccountToAccount) String() string { return proto.CompactTextString(m) } +func (*MsgSendFromAccountToAccount) ProtoMessage() {} +func (*MsgSendFromAccountToAccount) Descriptor() ([]byte, []int) { + return fileDescriptor_6ef1d018df19de71, []int{4} +} +func (m *MsgSendFromAccountToAccount) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSendFromAccountToAccount) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSendFromAccountToAccount.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 *MsgSendFromAccountToAccount) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSendFromAccountToAccount.Merge(m, src) +} +func (m *MsgSendFromAccountToAccount) XXX_Size() int { + return m.Size() +} +func (m *MsgSendFromAccountToAccount) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSendFromAccountToAccount.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSendFromAccountToAccount proto.InternalMessageInfo + +func (m *MsgSendFromAccountToAccount) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgSendFromAccountToAccount) GetSender() string { + if m != nil { + return m.Sender + } + return "" +} + +func (m *MsgSendFromAccountToAccount) GetRecipient() string { + if m != nil { + return m.Recipient + } + return "" +} + +func (m *MsgSendFromAccountToAccount) GetCoin() types1.Coin { + if m != nil { + return m.Coin + } + return types1.Coin{} +} + func init() { proto.RegisterType((*Transfer)(nil), "dydxprotocol.sending.Transfer") proto.RegisterType((*MsgDepositToSubaccount)(nil), "dydxprotocol.sending.MsgDepositToSubaccount") proto.RegisterType((*MsgWithdrawFromSubaccount)(nil), "dydxprotocol.sending.MsgWithdrawFromSubaccount") proto.RegisterType((*MsgSendFromModuleToAccount)(nil), "dydxprotocol.sending.MsgSendFromModuleToAccount") + proto.RegisterType((*MsgSendFromAccountToAccount)(nil), "dydxprotocol.sending.MsgSendFromAccountToAccount") } func init() { @@ -336,40 +411,41 @@ func init() { } var fileDescriptor_6ef1d018df19de71 = []byte{ - // 519 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x54, 0x41, 0x8b, 0x13, 0x31, - 0x14, 0x6e, 0x76, 0x4b, 0x6d, 0x53, 0x14, 0x19, 0xca, 0x3a, 0xed, 0x61, 0x2c, 0x15, 0xa4, 0x8a, - 0x3b, 0x63, 0x77, 0x65, 0xc1, 0xbd, 0x6d, 0x5d, 0x84, 0x15, 0xea, 0xa1, 0x2d, 0x08, 0x5e, 0x4a, - 0x66, 0x12, 0xd3, 0xc0, 0x26, 0xa9, 0x49, 0xa6, 0x6e, 0xaf, 0xfe, 0x02, 0x7f, 0x8a, 0x07, 0x7f, - 0xc4, 0xde, 0x5c, 0x3c, 0x89, 0xa0, 0x48, 0x7b, 0xf0, 0x67, 0x28, 0x33, 0x13, 0x3a, 0x9d, 0x53, - 0x45, 0xc5, 0xd3, 0xe4, 0xe5, 0xfb, 0xde, 0xcb, 0xf7, 0xbd, 0xc7, 0x1b, 0x78, 0x07, 0x2f, 0xf0, - 0xc5, 0x4c, 0x49, 0x23, 0x23, 0x79, 0x1e, 0x68, 0x22, 0x30, 0x13, 0x34, 0x30, 0x0a, 0x09, 0xfd, - 0x8a, 0x28, 0x3f, 0x45, 0x9c, 0xc6, 0x26, 0xc9, 0xb7, 0xa4, 0x56, 0x33, 0x92, 0x9a, 0x4b, 0x3d, - 0x49, 0x81, 0x20, 0x0b, 0xb2, 0x84, 0x96, 0x97, 0x45, 0x41, 0x88, 0x34, 0x09, 0xe6, 0xbd, 0x90, - 0x18, 0xd4, 0x0b, 0x22, 0xc9, 0x84, 0xc5, 0x6f, 0x59, 0x9c, 0x6b, 0x1a, 0xcc, 0x7b, 0xc9, 0xc7, - 0x02, 0x0d, 0x2a, 0xa9, 0xcc, 0x0a, 0x26, 0x27, 0x7b, 0x7b, 0xaf, 0x28, 0x32, 0x0e, 0x51, 0x14, - 0xc9, 0x58, 0x18, 0xbd, 0x71, 0xce, 0xa8, 0x9d, 0x8f, 0x00, 0x56, 0xc7, 0x56, 0xbd, 0x73, 0x0a, - 0x2b, 0x89, 0x58, 0xa2, 0x5c, 0xd0, 0x06, 0xdd, 0xfa, 0xc1, 0x5d, 0xbf, 0x68, 0x24, 0x2f, 0xe4, - 0x8f, 0xd6, 0xe7, 0x33, 0xdc, 0x2f, 0x5f, 0x7e, 0xbb, 0x5d, 0x1a, 0xda, 0x5c, 0xe7, 0x19, 0xac, - 0x29, 0x12, 0xb1, 0x19, 0x23, 0xc2, 0xb8, 0x3b, 0x7f, 0x50, 0x28, 0x4f, 0x77, 0x9a, 0xb0, 0x8a, - 0xb4, 0x26, 0x66, 0xc2, 0xb0, 0xbb, 0xdb, 0x06, 0xdd, 0xeb, 0xc3, 0x6b, 0x69, 0x7c, 0x86, 0x9d, - 0x3d, 0x58, 0x41, 0x3c, 0xc9, 0x73, 0xcb, 0x6d, 0xd0, 0x2d, 0x0f, 0x6d, 0xd4, 0xf9, 0x02, 0xe0, - 0xde, 0x40, 0xd3, 0x53, 0x32, 0x93, 0x9a, 0x99, 0xb1, 0xcc, 0x1f, 0x70, 0x1e, 0x16, 0xfc, 0xd5, - 0xfa, 0xee, 0xa7, 0x0f, 0xfb, 0x0d, 0x3b, 0x88, 0x13, 0x8c, 0x15, 0xd1, 0x7a, 0x64, 0x14, 0x13, - 0xf4, 0x7f, 0x7b, 0x69, 0xc1, 0xea, 0xeb, 0x18, 0x09, 0x13, 0x73, 0x6d, 0xdd, 0xac, 0xe3, 0xe3, - 0xfa, 0xdb, 0x1f, 0xef, 0xef, 0x5b, 0x3d, 0x9d, 0xaf, 0x00, 0x36, 0x07, 0x9a, 0xbe, 0x60, 0x66, - 0x8a, 0x15, 0x7a, 0xf3, 0x54, 0x49, 0xbe, 0xe1, 0x2f, 0x9f, 0xdf, 0xce, 0x5f, 0xcc, 0xef, 0x68, - 0xd3, 0xf3, 0xb6, 0x46, 0xfd, 0x63, 0x7f, 0x3f, 0x01, 0x6c, 0x0d, 0x34, 0x1d, 0x11, 0x81, 0x13, - 0x6f, 0x03, 0x89, 0xe3, 0x73, 0x32, 0x96, 0x27, 0xd6, 0xe0, 0x11, 0xac, 0xa1, 0xd8, 0x4c, 0xa5, - 0x62, 0x66, 0xb1, 0x5d, 0xda, 0x9a, 0xea, 0x3c, 0x80, 0x4e, 0xf6, 0xc0, 0x84, 0xa7, 0x15, 0x27, - 0x02, 0x71, 0x92, 0x36, 0xa9, 0x36, 0xbc, 0x99, 0x21, 0xd9, 0x53, 0xcf, 0x11, 0x27, 0xc5, 0x06, - 0xec, 0xfe, 0x7e, 0x03, 0x0e, 0x61, 0x39, 0xd9, 0xd9, 0xd4, 0x61, 0xfd, 0xa0, 0xe9, 0x5b, 0x7e, - 0xb2, 0xd4, 0xbe, 0x5d, 0x6a, 0xff, 0x89, 0x64, 0xc2, 0xf6, 0x3b, 0x25, 0x1f, 0xdf, 0x48, 0xec, - 0xe7, 0x52, 0xfb, 0xa3, 0xcb, 0xa5, 0x07, 0xae, 0x96, 0x1e, 0xf8, 0xbe, 0xf4, 0xc0, 0xbb, 0x95, - 0x57, 0xba, 0x5a, 0x79, 0xa5, 0xcf, 0x2b, 0xaf, 0xf4, 0xf2, 0x31, 0x65, 0x66, 0x1a, 0x87, 0x7e, - 0x24, 0x79, 0x50, 0x58, 0xf0, 0xf9, 0xa3, 0xfd, 0x68, 0x8a, 0x98, 0x08, 0xd6, 0x37, 0x17, 0xf9, - 0x9f, 0x69, 0x31, 0x23, 0x3a, 0xac, 0xa4, 0xc8, 0xe1, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc2, - 0x52, 0xf4, 0xd4, 0xbe, 0x04, 0x00, 0x00, + // 542 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x54, 0x41, 0x6b, 0x13, 0x41, + 0x14, 0xce, 0xb4, 0x21, 0x26, 0x13, 0x14, 0x59, 0x42, 0xdd, 0x44, 0x58, 0x43, 0x04, 0x89, 0x62, + 0x77, 0x9b, 0x56, 0x0a, 0xf6, 0xd6, 0x58, 0x84, 0x0a, 0xf1, 0x90, 0x04, 0x04, 0x2f, 0x61, 0x76, + 0x67, 0xdc, 0x0c, 0x74, 0x66, 0xe2, 0xcc, 0x6c, 0x6c, 0xae, 0xe2, 0x0f, 0xf0, 0xa7, 0x78, 0xf0, + 0x47, 0xf4, 0x66, 0xf1, 0x24, 0x82, 0x22, 0xc9, 0xc1, 0x9f, 0xa1, 0xec, 0xee, 0x90, 0xdd, 0x3d, + 0xb5, 0x58, 0xd1, 0xd3, 0xce, 0xdb, 0xef, 0xbd, 0x37, 0xdf, 0xf7, 0xcd, 0xbc, 0x81, 0x77, 0xf1, + 0x02, 0x9f, 0xce, 0xa4, 0xd0, 0x22, 0x10, 0x27, 0x9e, 0x22, 0x1c, 0x53, 0x1e, 0x7a, 0x5a, 0x22, + 0xae, 0x5e, 0x11, 0xe9, 0x26, 0x88, 0xd5, 0xc8, 0x27, 0xb9, 0x26, 0xa9, 0xd5, 0x0c, 0x84, 0x62, + 0x42, 0x4d, 0x12, 0xc0, 0x4b, 0x83, 0xb4, 0xa0, 0xe5, 0xa4, 0x91, 0xe7, 0x23, 0x45, 0xbc, 0x79, + 0xcf, 0x27, 0x1a, 0xf5, 0xbc, 0x40, 0x50, 0x6e, 0xf0, 0x5b, 0x06, 0x67, 0x2a, 0xf4, 0xe6, 0xbd, + 0xf8, 0x63, 0x80, 0x46, 0x28, 0x42, 0x91, 0x36, 0x8c, 0x57, 0xe6, 0xef, 0xfd, 0x22, 0xc9, 0xc8, + 0x47, 0x41, 0x20, 0x22, 0xae, 0x55, 0x6e, 0x9d, 0xa6, 0x76, 0x3e, 0x01, 0x58, 0x1d, 0x1b, 0xf6, + 0xd6, 0x11, 0xac, 0xc4, 0x64, 0x89, 0xb4, 0x41, 0x1b, 0x74, 0xeb, 0xbb, 0xf7, 0xdc, 0xa2, 0x90, + 0xac, 0x91, 0x3b, 0x5a, 0xaf, 0x8f, 0x71, 0xbf, 0x7c, 0xf6, 0xfd, 0x4e, 0x69, 0x68, 0x6a, 0xad, + 0x67, 0xb0, 0x26, 0x49, 0x40, 0x67, 0x94, 0x70, 0x6d, 0x6f, 0xfc, 0x41, 0xa3, 0xac, 0xdc, 0x6a, + 0xc2, 0x2a, 0x52, 0x8a, 0xe8, 0x09, 0xc5, 0xf6, 0x66, 0x1b, 0x74, 0xaf, 0x0f, 0xaf, 0x25, 0xf1, + 0x31, 0xb6, 0xb6, 0x60, 0x05, 0xb1, 0xb8, 0xce, 0x2e, 0xb7, 0x41, 0xb7, 0x3c, 0x34, 0x51, 0xe7, + 0x2b, 0x80, 0x5b, 0x03, 0x15, 0x1e, 0x91, 0x99, 0x50, 0x54, 0x8f, 0x45, 0xb6, 0x81, 0xb5, 0x53, + 0xd0, 0x57, 0xeb, 0xdb, 0x9f, 0x3f, 0x6e, 0x37, 0xcc, 0x41, 0x1c, 0x62, 0x2c, 0x89, 0x52, 0x23, + 0x2d, 0x29, 0x0f, 0xff, 0xb5, 0x96, 0x16, 0xac, 0xbe, 0x8e, 0x10, 0xd7, 0x11, 0x53, 0x46, 0xcd, + 0x3a, 0x3e, 0xa8, 0xbf, 0xfd, 0xf9, 0xe1, 0x81, 0xe1, 0xd3, 0xf9, 0x06, 0x60, 0x73, 0xa0, 0xc2, + 0x17, 0x54, 0x4f, 0xb1, 0x44, 0x6f, 0x9e, 0x4a, 0xc1, 0x72, 0xfa, 0xb2, 0xf3, 0xdb, 0xb8, 0xc2, + 0xf9, 0xed, 0xe7, 0x35, 0x5f, 0x64, 0xd4, 0x5f, 0xd6, 0xf7, 0x0b, 0xc0, 0xd6, 0x40, 0x85, 0x23, + 0xc2, 0x71, 0xac, 0x6d, 0x20, 0x70, 0x74, 0x42, 0xc6, 0xe2, 0xd0, 0x08, 0xdc, 0x87, 0x35, 0x14, + 0xe9, 0xa9, 0x90, 0x54, 0x2f, 0x2e, 0xa6, 0xb6, 0x4e, 0xb5, 0x1e, 0x42, 0x2b, 0xdd, 0x60, 0xc2, + 0x92, 0x8e, 0x13, 0x8e, 0x18, 0x49, 0x4c, 0xaa, 0x0d, 0x6f, 0xa6, 0x48, 0xba, 0xd5, 0x73, 0xc4, + 0x48, 0xd1, 0x80, 0xcd, 0xcb, 0x1b, 0xb0, 0x07, 0xcb, 0xf1, 0xcc, 0x26, 0x0a, 0xeb, 0xbb, 0x4d, + 0xd7, 0xe4, 0xc7, 0x43, 0xed, 0x9a, 0xa1, 0x76, 0x9f, 0x08, 0xca, 0x8d, 0xdf, 0x49, 0xf2, 0xc1, + 0x8d, 0x58, 0x7e, 0x46, 0xb5, 0xf3, 0x6e, 0x03, 0xde, 0xce, 0x39, 0x60, 0x94, 0x5f, 0xdd, 0x82, + 0x9d, 0xc2, 0xdd, 0xb8, 0xcc, 0xdd, 0xff, 0x9f, 0x36, 0xf4, 0x47, 0x67, 0x4b, 0x07, 0x9c, 0x2f, + 0x1d, 0xf0, 0x63, 0xe9, 0x80, 0xf7, 0x2b, 0xa7, 0x74, 0xbe, 0x72, 0x4a, 0x5f, 0x56, 0x4e, 0xe9, + 0xe5, 0xe3, 0x90, 0xea, 0x69, 0xe4, 0xbb, 0x81, 0x60, 0x5e, 0xe1, 0x9d, 0x9b, 0x3f, 0xda, 0x0e, + 0xa6, 0x88, 0x72, 0x6f, 0xfd, 0xe7, 0x34, 0x7b, 0xa0, 0x17, 0x33, 0xa2, 0xfc, 0x4a, 0x82, 0xec, + 0xfd, 0x0e, 0x00, 0x00, 0xff, 0xff, 0x36, 0xa0, 0xfb, 0x82, 0xc5, 0x05, 0x00, 0x00, } func (m *Transfer) Marshal() (dAtA []byte, err error) { @@ -579,6 +655,60 @@ func (m *MsgSendFromModuleToAccount) MarshalToSizedBuffer(dAtA []byte) (int, err return len(dAtA) - i, nil } +func (m *MsgSendFromAccountToAccount) 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 *MsgSendFromAccountToAccount) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSendFromAccountToAccount) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Coin.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTransfer(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + if len(m.Recipient) > 0 { + i -= len(m.Recipient) + copy(dAtA[i:], m.Recipient) + i = encodeVarintTransfer(dAtA, i, uint64(len(m.Recipient))) + i-- + dAtA[i] = 0x1a + } + if len(m.Sender) > 0 { + i -= len(m.Sender) + copy(dAtA[i:], m.Sender) + i = encodeVarintTransfer(dAtA, i, uint64(len(m.Sender))) + i-- + dAtA[i] = 0x12 + } + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTransfer(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintTransfer(dAtA []byte, offset int, v uint64) int { offset -= sovTransfer(v) base := offset @@ -674,6 +804,29 @@ func (m *MsgSendFromModuleToAccount) Size() (n int) { return n } +func (m *MsgSendFromAccountToAccount) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTransfer(uint64(l)) + } + l = len(m.Sender) + if l > 0 { + n += 1 + l + sovTransfer(uint64(l)) + } + l = len(m.Recipient) + if l > 0 { + n += 1 + l + sovTransfer(uint64(l)) + } + l = m.Coin.Size() + n += 1 + l + sovTransfer(uint64(l)) + return n +} + func sovTransfer(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1319,6 +1472,185 @@ func (m *MsgSendFromModuleToAccount) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgSendFromAccountToAccount) 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 ErrIntOverflowTransfer + } + 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: MsgSendFromAccountToAccount: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSendFromAccountToAccount: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTransfer + } + 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 ErrInvalidLengthTransfer + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTransfer + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Sender", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTransfer + } + 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 ErrInvalidLengthTransfer + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTransfer + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Sender = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Recipient", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTransfer + } + 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 ErrInvalidLengthTransfer + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTransfer + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Recipient = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Coin", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTransfer + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTransfer + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTransfer + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Coin.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTransfer(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTransfer + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTransfer(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/protocol/x/sending/types/tx.pb.go b/protocol/x/sending/types/tx.pb.go index e6f398a742..7b4d2efda6 100644 --- a/protocol/x/sending/types/tx.pb.go +++ b/protocol/x/sending/types/tx.pb.go @@ -223,40 +223,81 @@ func (m *MsgSendFromModuleToAccountResponse) XXX_DiscardUnknown() { var xxx_messageInfo_MsgSendFromModuleToAccountResponse proto.InternalMessageInfo +// MsgSendFromAccountToAccountResponse is a response type used for new +// account-to-account transfers. +type MsgSendFromAccountToAccountResponse struct { +} + +func (m *MsgSendFromAccountToAccountResponse) Reset() { *m = MsgSendFromAccountToAccountResponse{} } +func (m *MsgSendFromAccountToAccountResponse) String() string { return proto.CompactTextString(m) } +func (*MsgSendFromAccountToAccountResponse) ProtoMessage() {} +func (*MsgSendFromAccountToAccountResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_056a3cb0feba7dbf, []int{5} +} +func (m *MsgSendFromAccountToAccountResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgSendFromAccountToAccountResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgSendFromAccountToAccountResponse.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 *MsgSendFromAccountToAccountResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgSendFromAccountToAccountResponse.Merge(m, src) +} +func (m *MsgSendFromAccountToAccountResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgSendFromAccountToAccountResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgSendFromAccountToAccountResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgSendFromAccountToAccountResponse proto.InternalMessageInfo + func init() { proto.RegisterType((*MsgCreateTransfer)(nil), "dydxprotocol.sending.MsgCreateTransfer") proto.RegisterType((*MsgCreateTransferResponse)(nil), "dydxprotocol.sending.MsgCreateTransferResponse") proto.RegisterType((*MsgDepositToSubaccountResponse)(nil), "dydxprotocol.sending.MsgDepositToSubaccountResponse") proto.RegisterType((*MsgWithdrawFromSubaccountResponse)(nil), "dydxprotocol.sending.MsgWithdrawFromSubaccountResponse") proto.RegisterType((*MsgSendFromModuleToAccountResponse)(nil), "dydxprotocol.sending.MsgSendFromModuleToAccountResponse") + proto.RegisterType((*MsgSendFromAccountToAccountResponse)(nil), "dydxprotocol.sending.MsgSendFromAccountToAccountResponse") } func init() { proto.RegisterFile("dydxprotocol/sending/tx.proto", fileDescriptor_056a3cb0feba7dbf) } var fileDescriptor_056a3cb0feba7dbf = []byte{ - // 349 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4d, 0xa9, 0x4c, 0xa9, - 0x28, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0xce, 0xcf, 0xd1, 0x2f, 0x4e, 0xcd, 0x4b, 0xc9, 0xcc, 0x4b, - 0xd7, 0x2f, 0xa9, 0xd0, 0x03, 0x8b, 0x09, 0x89, 0x20, 0x4b, 0xeb, 0x41, 0xa5, 0xa5, 0x94, 0xb1, - 0x6b, 0x2a, 0x4a, 0xcc, 0x2b, 0x4e, 0x4b, 0x2d, 0x82, 0x68, 0x55, 0xf2, 0xe7, 0x12, 0xf4, 0x2d, - 0x4e, 0x77, 0x2e, 0x4a, 0x4d, 0x2c, 0x49, 0x0d, 0x81, 0x4a, 0x09, 0x59, 0x71, 0x71, 0xc0, 0x94, - 0x49, 0x30, 0x2a, 0x30, 0x6a, 0x70, 0x1b, 0xc9, 0xe9, 0x61, 0xb3, 0x42, 0x0f, 0xa6, 0x23, 0x08, - 0xae, 0x5e, 0x49, 0x9a, 0x4b, 0x12, 0xc3, 0xc0, 0xa0, 0xd4, 0xe2, 0x82, 0xfc, 0xbc, 0xe2, 0x54, - 0x25, 0x05, 0x2e, 0x39, 0xdf, 0xe2, 0x74, 0x97, 0xd4, 0x82, 0xfc, 0xe2, 0xcc, 0x92, 0x90, 0xfc, - 0xe0, 0xd2, 0xa4, 0xc4, 0xe4, 0xe4, 0xfc, 0xd2, 0xbc, 0x12, 0xb8, 0x0a, 0x65, 0x2e, 0x45, 0xdf, - 0xe2, 0xf4, 0xf0, 0xcc, 0x92, 0x8c, 0x94, 0xa2, 0xc4, 0x72, 0xb7, 0xa2, 0xfc, 0x5c, 0x2c, 0x8a, - 0x54, 0xb8, 0x94, 0x7c, 0x8b, 0xd3, 0x83, 0x53, 0xf3, 0x52, 0x40, 0x0a, 0x7c, 0xf3, 0x53, 0x4a, - 0x73, 0x52, 0x43, 0xf2, 0x1d, 0x51, 0x55, 0x19, 0xfd, 0x64, 0xe6, 0x62, 0xf6, 0x2d, 0x4e, 0x17, - 0xca, 0xe2, 0xe2, 0x43, 0xf3, 0x9f, 0x3a, 0x76, 0xdf, 0x60, 0xb8, 0x5b, 0x4a, 0x9f, 0x48, 0x85, - 0x30, 0x3b, 0x85, 0x2a, 0xb9, 0x84, 0xb1, 0xf8, 0x4e, 0x48, 0x07, 0xa7, 0x39, 0x58, 0x54, 0x4b, - 0x99, 0x90, 0xa2, 0x1a, 0x6e, 0x75, 0x13, 0x23, 0x97, 0x18, 0xf6, 0x70, 0x13, 0xc2, 0xed, 0x0d, - 0xec, 0x1a, 0xa4, 0xcc, 0x49, 0xd4, 0x00, 0x77, 0x44, 0x2b, 0x23, 0x97, 0x38, 0x8e, 0x78, 0x11, - 0x32, 0xc0, 0x69, 0x28, 0x0e, 0x1d, 0x52, 0x16, 0xa4, 0xea, 0x80, 0xb9, 0xc3, 0x29, 0xf8, 0xc4, - 0x23, 0x39, 0xc6, 0x0b, 0x8f, 0xe4, 0x18, 0x1f, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, - 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x2c, 0xd3, 0x33, 0x4b, 0x32, 0x4a, 0x93, - 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x51, 0x32, 0x48, 0x99, 0x89, 0x6e, 0x72, 0x46, 0x62, 0x66, 0x9e, - 0x3e, 0x5c, 0xa4, 0x02, 0x91, 0x69, 0x2a, 0x0b, 0x52, 0x8b, 0x93, 0xd8, 0xc0, 0x32, 0xc6, 0x80, - 0x00, 0x00, 0x00, 0xff, 0xff, 0x4b, 0x8a, 0x93, 0x09, 0x8e, 0x03, 0x00, 0x00, + // 377 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x93, 0xcf, 0x4a, 0xeb, 0x40, + 0x14, 0x87, 0x3b, 0x5c, 0xee, 0xe5, 0x72, 0x04, 0xc1, 0x51, 0xb4, 0x46, 0x1c, 0x6a, 0xaa, 0xe8, + 0x42, 0x13, 0xad, 0x05, 0xad, 0x3b, 0xff, 0xe0, 0x2e, 0x08, 0x6d, 0x41, 0x70, 0x97, 0x26, 0x63, + 0x1a, 0x69, 0x67, 0x42, 0x66, 0xa2, 0xed, 0x56, 0x14, 0x5c, 0xfa, 0x58, 0x2e, 0xbb, 0x74, 0x29, + 0xed, 0x8b, 0x88, 0xb5, 0x89, 0xb5, 0x9d, 0xa0, 0xd9, 0xce, 0xef, 0xfb, 0xce, 0x9c, 0x73, 0x92, + 0x81, 0x55, 0xb7, 0xeb, 0x76, 0x82, 0x90, 0x4b, 0xee, 0xf0, 0x96, 0x29, 0x28, 0x73, 0x7d, 0xe6, + 0x99, 0xb2, 0x63, 0x0c, 0xcf, 0xf0, 0xc2, 0x78, 0x6c, 0x8c, 0x62, 0xad, 0xa8, 0x96, 0x42, 0x9b, + 0x89, 0x6b, 0x1a, 0x7e, 0xaa, 0xfa, 0x05, 0xcc, 0x59, 0xc2, 0x3b, 0x0d, 0xa9, 0x2d, 0x69, 0x7d, + 0x14, 0xe1, 0x23, 0xf8, 0x1f, 0x63, 0x79, 0x54, 0x40, 0x5b, 0x33, 0x25, 0x62, 0xa8, 0xae, 0x30, + 0x62, 0xa3, 0x9a, 0xf0, 0xfa, 0x0a, 0x2c, 0x4f, 0x15, 0xac, 0x52, 0x11, 0x70, 0x26, 0xa8, 0x5e, + 0x00, 0x62, 0x09, 0xef, 0x8c, 0x06, 0x5c, 0xf8, 0xb2, 0xce, 0x6b, 0x51, 0xc3, 0x76, 0x1c, 0x1e, + 0x31, 0x99, 0x10, 0x45, 0x58, 0xb3, 0x84, 0x77, 0xe9, 0xcb, 0xa6, 0x1b, 0xda, 0x77, 0xe7, 0x21, + 0x6f, 0x2b, 0xa0, 0x75, 0xd0, 0x2d, 0xe1, 0xd5, 0x28, 0x73, 0x3f, 0x00, 0x8b, 0xbb, 0x51, 0x8b, + 0xd6, 0xf9, 0xf1, 0x04, 0xb5, 0x01, 0xc5, 0x31, 0x6a, 0x94, 0x4e, 0x61, 0xa5, 0x87, 0xbf, 0xf0, + 0xc7, 0x12, 0x1e, 0xbe, 0x81, 0xd9, 0x89, 0x35, 0x6c, 0xaa, 0x87, 0x9e, 0x1a, 0x4f, 0x33, 0x7f, + 0x09, 0xc6, 0x77, 0xe2, 0x2e, 0xcc, 0x2b, 0x96, 0x80, 0xb7, 0x53, 0xeb, 0x28, 0x68, 0xad, 0x9c, + 0x85, 0x4e, 0xae, 0xbe, 0x47, 0xb0, 0xa8, 0x5e, 0x2f, 0x4e, 0x1f, 0x43, 0x2d, 0x68, 0x07, 0x19, + 0x85, 0xa4, 0x89, 0x47, 0x04, 0x4b, 0x29, 0x9f, 0x0f, 0xef, 0xa6, 0x16, 0x4d, 0x31, 0xb4, 0xc3, + 0xac, 0x46, 0xd2, 0xc7, 0x13, 0x82, 0x7c, 0xda, 0x0f, 0x82, 0xf7, 0x7e, 0x2c, 0x3b, 0xa9, 0x68, + 0x95, 0xcc, 0x4a, 0xdc, 0xca, 0x49, 0xed, 0xa5, 0x4f, 0x50, 0xaf, 0x4f, 0xd0, 0x5b, 0x9f, 0xa0, + 0xe7, 0x01, 0xc9, 0xf5, 0x06, 0x24, 0xf7, 0x3a, 0x20, 0xb9, 0xab, 0x8a, 0xe7, 0xcb, 0x66, 0xd4, + 0x30, 0x1c, 0xde, 0x36, 0xbf, 0x3d, 0xe9, 0xdb, 0xf2, 0x8e, 0xd3, 0xb4, 0x7d, 0x66, 0x26, 0x27, + 0x9d, 0xaf, 0x67, 0xde, 0x0d, 0xa8, 0x68, 0xfc, 0x1b, 0x26, 0xfb, 0xef, 0x01, 0x00, 0x00, 0xff, + 0xff, 0xbc, 0x44, 0xca, 0x0e, 0x40, 0x04, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -282,6 +323,9 @@ type MsgClient interface { // SendFromModuleToAccount initiates a new transfer from a module to an // `x/bank` account (should only be executed by governance). SendFromModuleToAccount(ctx context.Context, in *MsgSendFromModuleToAccount, opts ...grpc.CallOption) (*MsgSendFromModuleToAccountResponse, error) + // SendFromAccountToAccount initiates a new transfer from an `x/bank` account + // to another `x/bank` account (should only be executed by governance). + SendFromAccountToAccount(ctx context.Context, in *MsgSendFromAccountToAccount, opts ...grpc.CallOption) (*MsgSendFromAccountToAccountResponse, error) } type msgClient struct { @@ -328,6 +372,15 @@ func (c *msgClient) SendFromModuleToAccount(ctx context.Context, in *MsgSendFrom return out, nil } +func (c *msgClient) SendFromAccountToAccount(ctx context.Context, in *MsgSendFromAccountToAccount, opts ...grpc.CallOption) (*MsgSendFromAccountToAccountResponse, error) { + out := new(MsgSendFromAccountToAccountResponse) + err := c.cc.Invoke(ctx, "/dydxprotocol.sending.Msg/SendFromAccountToAccount", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { // CreateTransfer initiates a new transfer between subaccounts. @@ -341,6 +394,9 @@ type MsgServer interface { // SendFromModuleToAccount initiates a new transfer from a module to an // `x/bank` account (should only be executed by governance). SendFromModuleToAccount(context.Context, *MsgSendFromModuleToAccount) (*MsgSendFromModuleToAccountResponse, error) + // SendFromAccountToAccount initiates a new transfer from an `x/bank` account + // to another `x/bank` account (should only be executed by governance). + SendFromAccountToAccount(context.Context, *MsgSendFromAccountToAccount) (*MsgSendFromAccountToAccountResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. @@ -359,6 +415,9 @@ func (*UnimplementedMsgServer) WithdrawFromSubaccount(ctx context.Context, req * func (*UnimplementedMsgServer) SendFromModuleToAccount(ctx context.Context, req *MsgSendFromModuleToAccount) (*MsgSendFromModuleToAccountResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method SendFromModuleToAccount not implemented") } +func (*UnimplementedMsgServer) SendFromAccountToAccount(ctx context.Context, req *MsgSendFromAccountToAccount) (*MsgSendFromAccountToAccountResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method SendFromAccountToAccount not implemented") +} func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) @@ -436,6 +495,24 @@ func _Msg_SendFromModuleToAccount_Handler(srv interface{}, ctx context.Context, return interceptor(ctx, in, info, handler) } +func _Msg_SendFromAccountToAccount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgSendFromAccountToAccount) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).SendFromAccountToAccount(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dydxprotocol.sending.Msg/SendFromAccountToAccount", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).SendFromAccountToAccount(ctx, req.(*MsgSendFromAccountToAccount)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "dydxprotocol.sending.Msg", HandlerType: (*MsgServer)(nil), @@ -456,6 +533,10 @@ var _Msg_serviceDesc = grpc.ServiceDesc{ MethodName: "SendFromModuleToAccount", Handler: _Msg_SendFromModuleToAccount_Handler, }, + { + MethodName: "SendFromAccountToAccount", + Handler: _Msg_SendFromAccountToAccount_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "dydxprotocol/sending/tx.proto", @@ -588,6 +669,29 @@ func (m *MsgSendFromModuleToAccountResponse) MarshalToSizedBuffer(dAtA []byte) ( return len(dAtA) - i, nil } +func (m *MsgSendFromAccountToAccountResponse) 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 *MsgSendFromAccountToAccountResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgSendFromAccountToAccountResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + func encodeVarintTx(dAtA []byte, offset int, v uint64) int { offset -= sovTx(v) base := offset @@ -648,6 +752,15 @@ func (m *MsgSendFromModuleToAccountResponse) Size() (n int) { return n } +func (m *MsgSendFromAccountToAccountResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + func sovTx(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -940,6 +1053,56 @@ func (m *MsgSendFromModuleToAccountResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *MsgSendFromAccountToAccountResponse) 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 ErrIntOverflowTx + } + 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: MsgSendFromAccountToAccountResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgSendFromAccountToAccountResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipTx(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/protocol/x/sending/types/types.go b/protocol/x/sending/types/types.go index 798eb15052..4b60810786 100644 --- a/protocol/x/sending/types/types.go +++ b/protocol/x/sending/types/types.go @@ -18,5 +18,9 @@ type SendingKeeper interface { ctx sdk.Context, msg *MsgSendFromModuleToAccount, ) error + SendFromAccountToAccount( + ctx sdk.Context, + msg *MsgSendFromAccountToAccount, + ) error HasAuthority(authority string) bool } From b93a866d903534ee215d900e8dde5ec2b2ddc20f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 23:21:50 -0500 Subject: [PATCH 02/14] Fix issue with 30d volume not being removed, and commission not being capped (backport #3250) (#3256) Co-authored-by: Justin Barnett <61020572+jusbar23@users.noreply.github.com> --- .../dydxprotocol/affiliates/affiliates.ts | 14 +- .../codegen/dydxprotocol/affiliates/query.ts | 18 +- .../src/codegen/dydxprotocol/stats/stats.ts | 191 +++- .../dydxprotocol/affiliates/affiliates.proto | 3 +- proto/dydxprotocol/affiliates/query.proto | 6 + proto/dydxprotocol/stats/stats.proto | 25 + protocol/x/affiliates/abci.go | 4 - protocol/x/affiliates/keeper/grpc_query.go | 12 +- .../x/affiliates/keeper/grpc_query_test.go | 48 +- protocol/x/affiliates/keeper/keeper.go | 108 +-- protocol/x/affiliates/keeper/keeper_test.go | 446 +-------- protocol/x/affiliates/types/affiliates.pb.go | 78 +- .../x/affiliates/types/expected_keepers.go | 2 + protocol/x/affiliates/types/query.pb.go | 154 +-- .../x/clob/keeper/process_single_match.go | 103 ++ ...ocess_single_match_affiliate_stats_test.go | 890 ++++++++++++++++++ protocol/x/clob/types/expected_keepers.go | 11 +- protocol/x/revshare/keeper/revshare.go | 14 + protocol/x/revshare/keeper/revshare_test.go | 68 +- protocol/x/stats/keeper/grpc_query_test.go | 31 +- protocol/x/stats/keeper/keeper.go | 51 + protocol/x/stats/keeper/keeper_test.go | 635 ++++++++++++- protocol/x/stats/types/stats.pb.go | 487 +++++++++- 23 files changed, 2665 insertions(+), 734 deletions(-) create mode 100644 protocol/x/clob/keeper/process_single_match_affiliate_stats_test.go 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 6c3e30c866..23ea834128 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/affiliates.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/affiliates.ts @@ -17,7 +17,12 @@ export interface AffiliateTiersSDKType { export interface AffiliateTiers_Tier { /** Required all-time referred volume in quote quantums. */ reqReferredVolumeQuoteQuantums: Long; - /** Required currently staked native tokens (in whole coins). */ + /** + * Required currently staked native tokens (in whole coins). + * This is deprecated + */ + + /** @deprecated */ reqStakedWholeCoins: number; /** Taker fee share in parts-per-million. */ @@ -29,7 +34,12 @@ export interface AffiliateTiers_Tier { export interface AffiliateTiers_TierSDKType { /** Required all-time referred volume in quote quantums. */ req_referred_volume_quote_quantums: Long; - /** Required currently staked native tokens (in whole coins). */ + /** + * Required currently staked native tokens (in whole coins). + * This is deprecated + */ + + /** @deprecated */ req_staked_whole_coins: number; /** Taker fee share in parts-per-million. */ 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 554822559d..3525a0d648 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/query.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/affiliates/query.ts @@ -48,6 +48,9 @@ export interface AffiliateInfoResponse { /** The affiliate's 30d referred volume in quote quantums. */ referredVolume_30dRolling: Uint8Array; + /** The affiliate's 30d attributed volume in quote quantums (from referees). */ + + attributedVolume_30dRolling: Uint8Array; } /** * AffiliateInfoResponse is the response type for the Query/AffiliateInfo RPC @@ -80,6 +83,9 @@ export interface AffiliateInfoResponseSDKType { /** The affiliate's 30d referred volume in quote quantums. */ referred_volume_30d_rolling: Uint8Array; + /** The affiliate's 30d attributed volume in quote quantums (from referees). */ + + attributed_volume_30d_rolling: Uint8Array; } /** ReferredByRequest is the request type for the Query/ReferredBy RPC method. */ @@ -272,7 +278,8 @@ function createBaseAffiliateInfoResponse(): AffiliateInfoResponse { feeSharePpm: 0, referredVolume: new Uint8Array(), stakedAmount: new Uint8Array(), - referredVolume_30dRolling: new Uint8Array() + referredVolume_30dRolling: new Uint8Array(), + attributedVolume_30dRolling: new Uint8Array() }; } @@ -302,6 +309,10 @@ export const AffiliateInfoResponse = { writer.uint32(50).bytes(message.referredVolume_30dRolling); } + if (message.attributedVolume_30dRolling.length !== 0) { + writer.uint32(58).bytes(message.attributedVolume_30dRolling); + } + return writer; }, @@ -338,6 +349,10 @@ export const AffiliateInfoResponse = { message.referredVolume_30dRolling = reader.bytes(); break; + case 7: + message.attributedVolume_30dRolling = reader.bytes(); + break; + default: reader.skipType(tag & 7); break; @@ -355,6 +370,7 @@ export const AffiliateInfoResponse = { message.referredVolume = object.referredVolume ?? new Uint8Array(); message.stakedAmount = object.stakedAmount ?? new Uint8Array(); message.referredVolume_30dRolling = object.referredVolume_30dRolling ?? new Uint8Array(); + message.attributedVolume_30dRolling = object.attributedVolume_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 63e5a2d4f6..d8bfa0751a 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/stats.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/stats.ts @@ -1,6 +1,82 @@ import { Timestamp } from "../../google/protobuf/timestamp"; import * as _m0 from "protobufjs/minimal"; -import { DeepPartial, Long, toTimestamp, fromTimestamp } from "../../helpers"; +import { Long, DeepPartial, toTimestamp, fromTimestamp } from "../../helpers"; +/** Role indicates whether this attribution is for the taker or maker */ + +export enum AffiliateAttribution_Role { + ROLE_UNSPECIFIED = 0, + ROLE_TAKER = 1, + ROLE_MAKER = 2, + UNRECOGNIZED = -1, +} +/** Role indicates whether this attribution is for the taker or maker */ + +export enum AffiliateAttribution_RoleSDKType { + ROLE_UNSPECIFIED = 0, + ROLE_TAKER = 1, + ROLE_MAKER = 2, + UNRECOGNIZED = -1, +} +export function affiliateAttribution_RoleFromJSON(object: any): AffiliateAttribution_Role { + switch (object) { + case 0: + case "ROLE_UNSPECIFIED": + return AffiliateAttribution_Role.ROLE_UNSPECIFIED; + + case 1: + case "ROLE_TAKER": + return AffiliateAttribution_Role.ROLE_TAKER; + + case 2: + case "ROLE_MAKER": + return AffiliateAttribution_Role.ROLE_MAKER; + + case -1: + case "UNRECOGNIZED": + default: + return AffiliateAttribution_Role.UNRECOGNIZED; + } +} +export function affiliateAttribution_RoleToJSON(object: AffiliateAttribution_Role): string { + switch (object) { + case AffiliateAttribution_Role.ROLE_UNSPECIFIED: + return "ROLE_UNSPECIFIED"; + + case AffiliateAttribution_Role.ROLE_TAKER: + return "ROLE_TAKER"; + + case AffiliateAttribution_Role.ROLE_MAKER: + return "ROLE_MAKER"; + + case AffiliateAttribution_Role.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} +/** AffiliateAttribution represents the affiliate attribution for a fill. */ + +export interface AffiliateAttribution { + /** Role of the trader (taker or maker) whose affiliate is being attributed */ + role: AffiliateAttribution_Role; + /** Referrer address (the affiliate receiving the fee) */ + + referrerAddress: string; + /** Referred volume in quote quantums (capped based on 30-day volume limits) */ + + referredVolumeQuoteQuantums: Long; +} +/** AffiliateAttribution represents the affiliate attribution for a fill. */ + +export interface AffiliateAttributionSDKType { + /** Role of the trader (taker or maker) whose affiliate is being attributed */ + role: AffiliateAttribution_RoleSDKType; + /** Referrer address (the affiliate receiving the fee) */ + + referrer_address: string; + /** Referred volume in quote quantums (capped based on 30-day volume limits) */ + + referred_volume_quote_quantums: Long; +} /** BlockStats is used to store stats transiently within the scope of a block. */ export interface BlockStats { @@ -34,6 +110,12 @@ export interface BlockStats_Fill { */ affiliateFeeGeneratedQuantums: Long; + /** + * Affiliate revenue attributions for this fill (can include both taker and + * maker) + */ + + affiliateAttributions: AffiliateAttribution[]; } /** Fill records data about a fill on this block. */ @@ -56,6 +138,12 @@ export interface BlockStats_FillSDKType { */ affiliate_fee_generated_quantums: Long; + /** + * Affiliate revenue attributions for this fill (can include both taker and + * maker) + */ + + affiliate_attributions: AffiliateAttributionSDKType[]; } /** StatsMetadata stores metadata for the x/stats module */ @@ -134,6 +222,12 @@ export interface UserStats { /** Referred volume in quote quantums with this user being an affiliate */ affiliate_30dReferredVolumeQuoteQuantums: Long; + /** + * Attributed volume in quote quantums - volume from this user (as referee) + * that has been attributed to their affiliate in the last 30 days + */ + + affiliate_30dAttributedVolumeQuoteQuantums: Long; } /** * UserStats stores stats for a User. This is the sum of all stats for a user in @@ -152,6 +246,12 @@ export interface UserStatsSDKType { /** Referred volume in quote quantums with this user being an affiliate */ affiliate_30d_referred_volume_quote_quantums: Long; + /** + * Attributed volume in quote quantums - volume from this user (as referee) + * that has been attributed to their affiliate in the last 30 days + */ + + affiliate_30d_attributed_volume_quote_quantums: Long; } /** CachedStakedBaseTokens stores the last calculated total staked base tokens */ @@ -178,6 +278,71 @@ export interface CachedStakedBaseTokensSDKType { cached_at: Long; } +function createBaseAffiliateAttribution(): AffiliateAttribution { + return { + role: 0, + referrerAddress: "", + referredVolumeQuoteQuantums: Long.UZERO + }; +} + +export const AffiliateAttribution = { + encode(message: AffiliateAttribution, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.role !== 0) { + writer.uint32(8).int32(message.role); + } + + if (message.referrerAddress !== "") { + writer.uint32(18).string(message.referrerAddress); + } + + if (!message.referredVolumeQuoteQuantums.isZero()) { + writer.uint32(24).uint64(message.referredVolumeQuoteQuantums); + } + + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): AffiliateAttribution { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseAffiliateAttribution(); + + while (reader.pos < end) { + const tag = reader.uint32(); + + switch (tag >>> 3) { + case 1: + message.role = (reader.int32() as any); + break; + + case 2: + message.referrerAddress = reader.string(); + break; + + case 3: + message.referredVolumeQuoteQuantums = (reader.uint64() as Long); + break; + + default: + reader.skipType(tag & 7); + break; + } + } + + return message; + }, + + fromPartial(object: DeepPartial): AffiliateAttribution { + const message = createBaseAffiliateAttribution(); + message.role = object.role ?? 0; + message.referrerAddress = object.referrerAddress ?? ""; + message.referredVolumeQuoteQuantums = object.referredVolumeQuoteQuantums !== undefined && object.referredVolumeQuoteQuantums !== null ? Long.fromValue(object.referredVolumeQuoteQuantums) : Long.UZERO; + return message; + } + +}; + function createBaseBlockStats(): BlockStats { return { fills: [] @@ -228,7 +393,8 @@ function createBaseBlockStats_Fill(): BlockStats_Fill { taker: "", maker: "", notional: Long.UZERO, - affiliateFeeGeneratedQuantums: Long.UZERO + affiliateFeeGeneratedQuantums: Long.UZERO, + affiliateAttributions: [] }; } @@ -250,6 +416,10 @@ export const BlockStats_Fill = { writer.uint32(32).uint64(message.affiliateFeeGeneratedQuantums); } + for (const v of message.affiliateAttributions) { + AffiliateAttribution.encode(v!, writer.uint32(42).fork()).ldelim(); + } + return writer; }, @@ -278,6 +448,10 @@ export const BlockStats_Fill = { message.affiliateFeeGeneratedQuantums = (reader.uint64() as Long); break; + case 5: + message.affiliateAttributions.push(AffiliateAttribution.decode(reader, reader.uint32())); + break; + default: reader.skipType(tag & 7); break; @@ -293,6 +467,7 @@ export const BlockStats_Fill = { message.maker = object.maker ?? ""; message.notional = object.notional !== undefined && object.notional !== null ? Long.fromValue(object.notional) : Long.UZERO; message.affiliateFeeGeneratedQuantums = object.affiliateFeeGeneratedQuantums !== undefined && object.affiliateFeeGeneratedQuantums !== null ? Long.fromValue(object.affiliateFeeGeneratedQuantums) : Long.UZERO; + message.affiliateAttributions = object.affiliateAttributions?.map(e => AffiliateAttribution.fromPartial(e)) || []; return message; } @@ -503,7 +678,8 @@ function createBaseUserStats(): UserStats { takerNotional: Long.UZERO, makerNotional: Long.UZERO, affiliate_30dRevenueGeneratedQuantums: Long.UZERO, - affiliate_30dReferredVolumeQuoteQuantums: Long.UZERO + affiliate_30dReferredVolumeQuoteQuantums: Long.UZERO, + affiliate_30dAttributedVolumeQuoteQuantums: Long.UZERO }; } @@ -525,6 +701,10 @@ export const UserStats = { writer.uint32(32).uint64(message.affiliate_30dReferredVolumeQuoteQuantums); } + if (!message.affiliate_30dAttributedVolumeQuoteQuantums.isZero()) { + writer.uint32(40).uint64(message.affiliate_30dAttributedVolumeQuoteQuantums); + } + return writer; }, @@ -553,6 +733,10 @@ export const UserStats = { message.affiliate_30dReferredVolumeQuoteQuantums = (reader.uint64() as Long); break; + case 5: + message.affiliate_30dAttributedVolumeQuoteQuantums = (reader.uint64() as Long); + break; + default: reader.skipType(tag & 7); break; @@ -568,6 +752,7 @@ export const UserStats = { message.makerNotional = object.makerNotional !== undefined && object.makerNotional !== null ? Long.fromValue(object.makerNotional) : Long.UZERO; 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; + message.affiliate_30dAttributedVolumeQuoteQuantums = object.affiliate_30dAttributedVolumeQuoteQuantums !== undefined && object.affiliate_30dAttributedVolumeQuoteQuantums !== null ? Long.fromValue(object.affiliate_30dAttributedVolumeQuoteQuantums) : Long.UZERO; return message; } diff --git a/proto/dydxprotocol/affiliates/affiliates.proto b/proto/dydxprotocol/affiliates/affiliates.proto index ecc1fb3658..a6badf8805 100644 --- a/proto/dydxprotocol/affiliates/affiliates.proto +++ b/proto/dydxprotocol/affiliates/affiliates.proto @@ -13,7 +13,8 @@ message AffiliateTiers { // Required all-time referred volume in quote quantums. uint64 req_referred_volume_quote_quantums = 1; // Required currently staked native tokens (in whole coins). - uint32 req_staked_whole_coins = 2; + // This is deprecated + uint32 req_staked_whole_coins = 2 [ deprecated = true ]; // Taker fee share in parts-per-million. uint32 taker_fee_share_ppm = 3; } diff --git a/proto/dydxprotocol/affiliates/query.proto b/proto/dydxprotocol/affiliates/query.proto index 6d6697ef97..0bd060ff20 100644 --- a/proto/dydxprotocol/affiliates/query.proto +++ b/proto/dydxprotocol/affiliates/query.proto @@ -82,6 +82,12 @@ message AffiliateInfoResponse { "github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt", (gogoproto.nullable) = false ]; + // The affiliate's 30d attributed volume in quote quantums (from referees). + bytes attributed_volume_30d_rolling = 7 [ + (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 ca1841ac9f..ce755f07c7 100644 --- a/proto/dydxprotocol/stats/stats.proto +++ b/proto/dydxprotocol/stats/stats.proto @@ -6,6 +6,23 @@ option go_package = "github.com/dydxprotocol/v4-chain/protocol/x/stats/types"; import "gogoproto/gogo.proto"; import "google/protobuf/timestamp.proto"; +// AffiliateAttribution represents the affiliate attribution for a fill. +message AffiliateAttribution { + // Role indicates whether this attribution is for the taker or maker + enum Role { + ROLE_UNSPECIFIED = 0; + ROLE_TAKER = 1; + ROLE_MAKER = 2; + } + + // Role of the trader (taker or maker) whose affiliate is being attributed + Role role = 1; + // Referrer address (the affiliate receiving the fee) + string referrer_address = 2; + // Referred volume in quote quantums (capped based on 30-day volume limits) + uint64 referred_volume_quote_quantums = 3; +} + // BlockStats is used to store stats transiently within the scope of a block. message BlockStats { // Fill records data about a fill on this block. @@ -24,6 +41,10 @@ message BlockStats { // Used to calculate affiliate revenue attributed for taker. This is dynamic // per affiliate tier uint64 affiliate_fee_generated_quantums = 4; + + // Affiliate revenue attributions for this fill (can include both taker and + // maker) + repeated AffiliateAttribution affiliate_attributions = 5; } // The fills that occured on this block. @@ -73,6 +94,10 @@ message UserStats { // Referred volume in quote quantums with this user being an affiliate uint64 affiliate_30d_referred_volume_quote_quantums = 4; + + // Attributed volume in quote quantums - volume from this user (as referee) + // that has been attributed to their affiliate in the last 30 days + uint64 affiliate_30d_attributed_volume_quote_quantums = 5; } // CachedStakedBaseTokens stores the last calculated total staked base tokens diff --git a/protocol/x/affiliates/abci.go b/protocol/x/affiliates/abci.go index 90063b492f..6199e68223 100644 --- a/protocol/x/affiliates/abci.go +++ b/protocol/x/affiliates/abci.go @@ -2,7 +2,6 @@ package affiliates import ( sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/dydxprotocol/v4-chain/protocol/lib/log" "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/keeper" ) @@ -10,7 +9,4 @@ func EndBlocker( ctx sdk.Context, keeper *keeper.Keeper, ) { - if err := keeper.AggregateAffiliateReferredVolumeForFills(ctx); err != nil { - log.ErrorLogWithError(ctx, "error aggregating affiliate volume for fills", err) - } } diff --git a/protocol/x/affiliates/keeper/grpc_query.go b/protocol/x/affiliates/keeper/grpc_query.go index 287e4d6ba0..3526aee137 100644 --- a/protocol/x/affiliates/keeper/grpc_query.go +++ b/protocol/x/affiliates/keeper/grpc_query.go @@ -40,14 +40,16 @@ func (k Keeper) AffiliateInfo(c context.Context, userStats := k.statsKeeper.GetUserStats(ctx, addr.String()) referredVolume := userStats.Affiliate_30DReferredVolumeQuoteQuantums + attributedVolume := userStats.Affiliate_30DAttributedVolumeQuoteQuantums stakedAmount := k.statsKeeper.GetStakedBaseTokens(ctx, req.GetAddress()) return &types.AffiliateInfoResponse{ - IsWhitelisted: isWhitelisted, - Tier: tierLevel, - FeeSharePpm: feeSharePpm, - StakedAmount: dtypes.NewIntFromBigInt(stakedAmount), - ReferredVolume_30DRolling: dtypes.NewIntFromBigInt(lib.BigU(referredVolume)), + IsWhitelisted: isWhitelisted, + Tier: tierLevel, + FeeSharePpm: feeSharePpm, + StakedAmount: dtypes.NewIntFromBigInt(stakedAmount), + ReferredVolume_30DRolling: dtypes.NewIntFromBigInt(lib.BigU(referredVolume)), + AttributedVolume_30DRolling: dtypes.NewIntFromBigInt(lib.BigU(attributedVolume)), }, nil } diff --git a/protocol/x/affiliates/keeper/grpc_query_test.go b/protocol/x/affiliates/keeper/grpc_query_test.go index c990b89837..ec21cc071c 100644 --- a/protocol/x/affiliates/keeper/grpc_query_test.go +++ b/protocol/x/affiliates/keeper/grpc_query_test.go @@ -10,6 +10,7 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/dtypes" "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/keeper" "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/types" + statstypes "github.com/dydxprotocol/v4-chain/protocol/x/stats/types" "github.com/stretchr/testify/require" testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" @@ -35,6 +36,7 @@ func TestAffiliateInfo(t *testing.T) { ReferredVolume_30DRolling: dtypes.NewIntFromUint64( types.DefaultAffiliateTiers.Tiers[0].ReqReferredVolumeQuoteQuantums, ), + AttributedVolume_30DRolling: dtypes.NewIntFromUint64(0), StakedAmount: dtypes.NewIntFromUint64( uint64(types.DefaultAffiliateTiers.Tiers[0].ReqStakedWholeCoins) * 1e18, ), @@ -69,6 +71,7 @@ func TestAffiliateInfo(t *testing.T) { ReferredVolume_30DRolling: dtypes.NewIntFromUint64( types.DefaultAffiliateTiers.Tiers[0].ReqReferredVolumeQuoteQuantums, ), + AttributedVolume_30DRolling: dtypes.NewIntFromUint64(0), StakedAmount: dtypes.NewIntFromUint64( uint64(types.DefaultAffiliateTiers.Tiers[0].ReqStakedWholeCoins) * 1e18, ), @@ -104,11 +107,12 @@ func TestAffiliateInfo(t *testing.T) { Address: constants.AliceAccAddress.String(), }, res: &types.AffiliateInfoResponse{ - IsWhitelisted: true, - Tier: 4, - FeeSharePpm: 250_000, - ReferredVolume_30DRolling: dtypes.NewIntFromUint64(0), - StakedAmount: dtypes.NewIntFromUint64(0), + IsWhitelisted: true, + Tier: 4, + FeeSharePpm: 250_000, + ReferredVolume_30DRolling: dtypes.NewIntFromUint64(0), + AttributedVolume_30DRolling: dtypes.NewIntFromUint64(0), + StakedAmount: dtypes.NewIntFromUint64(0), }, setup: func(ctx sdk.Context, k keeper.Keeper, tApp *testapp.TestApp) { err := k.RegisterAffiliate(ctx, constants.BobAccAddress.String(), constants.AliceAccAddress.String()) @@ -132,6 +136,40 @@ func TestAffiliateInfo(t *testing.T) { require.NoError(t, err) }, }, + "With Attributed Volume": { + req: &types.AffiliateInfoRequest{ + Address: constants.AliceAccAddress.String(), + }, + res: &types.AffiliateInfoResponse{ + IsWhitelisted: false, + Tier: 0, + FeeSharePpm: types.DefaultAffiliateTiers.Tiers[0].TakerFeeSharePpm, + ReferredVolume_30DRolling: dtypes.NewIntFromUint64(5_000_000), + AttributedVolume_30DRolling: dtypes.NewIntFromUint64(3_000_000), + StakedAmount: dtypes.NewIntFromUint64(0), + }, + setup: func(ctx sdk.Context, k keeper.Keeper, tApp *testapp.TestApp) { + err := k.RegisterAffiliate(ctx, constants.BobAccAddress.String(), constants.AliceAccAddress.String()) + require.NoError(t, err) + + // Set user stats with both referred and attributed volume + statsKeeper := tApp.App.StatsKeeper + statsKeeper.SetUserStats(ctx, constants.AliceAccAddress.String(), &statstypes.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 5_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 3_000_000, + }) + + stakingKeeper := tApp.App.StakingKeeper + err = stakingKeeper.SetDelegation(ctx, + stakingtypes.NewDelegation(constants.AliceAccAddress.String(), + constants.AliceValAddress.String(), math.LegacyNewDecFromBigInt( + big.NewInt(0), + ), + ), + ) + require.NoError(t, err) + }, + }, } for name, tc := range testCases { diff --git a/protocol/x/affiliates/keeper/keeper.go b/protocol/x/affiliates/keeper/keeper.go index 0af1433c08..fbf1b2aafa 100644 --- a/protocol/x/affiliates/keeper/keeper.go +++ b/protocol/x/affiliates/keeper/keeper.go @@ -233,10 +233,9 @@ func (k Keeper) UpdateAffiliateTiers(ctx sdk.Context, affiliateTiers types.Affil tiers[i].TakerFeeSharePpm, types.AffiliatesRevSharePpmCap) } // Check if the tiers are strictly increasing. - if tiers[i].ReqReferredVolumeQuoteQuantums <= tiers[i-1].ReqReferredVolumeQuoteQuantums || - tiers[i].ReqStakedWholeCoins < tiers[i-1].ReqStakedWholeCoins { + if tiers[i].ReqReferredVolumeQuoteQuantums <= tiers[i-1].ReqReferredVolumeQuoteQuantums { return errorsmod.Wrapf(types.ErrInvalidAffiliateTiers, - "volume must be strictly increasing; staked coins must be non-decreasing") + "volume must be strictly increasing") } } store.Set([]byte(types.AffiliateTiersKey), affiliateTiersBytes) @@ -376,106 +375,3 @@ func (k Keeper) GetAffiliateOverridesMap(ctx sdk.Context) (map[string]bool, erro } 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 -} - -func (k Keeper) AggregateAffiliateReferredVolumeForFills( - ctx sdk.Context, -) error { - blockStats := k.statsKeeper.GetBlockStats(ctx) - affiliateParams, err := k.GetAffiliateParameters(ctx) - if err != nil { - return err - } - referredByCache := make(map[string]string) - - // Multiple fills within the same block can happen, so we want to keep track of those to properly attribute volume. - previouslyAttributedVolume := make(map[string]uint64) - - for _, fill := range blockStats.Fills { - // Process taker's referred volume - referredByAddrTaker, cached := referredByCache[fill.Taker] - if !cached { - var found bool - referredByAddrTaker, found = k.GetReferredBy(ctx, fill.Taker) - if found { - referredByCache[fill.Taker] = referredByAddrTaker - } - } - if referredByAddrTaker != "" { - // Add referred volume, this decides affiliate tier and is limited by the maximum volume on a 30d window - if err := k.addReferredVolumeIfQualified( - ctx, - fill.Taker, - referredByAddrTaker, - fill.Notional, - affiliateParams, - previouslyAttributedVolume, - ); err != nil { - return err - } - } - - // Process maker's referred volume - referredByAddrMaker, cached := referredByCache[fill.Maker] - if !cached { - var found bool - referredByAddrMaker, found = k.GetReferredBy(ctx, fill.Maker) - if found { - referredByCache[fill.Maker] = referredByAddrMaker - } - } - if referredByAddrMaker != "" { - if err := k.addReferredVolumeIfQualified( - ctx, - fill.Maker, - referredByAddrMaker, - fill.Notional, - affiliateParams, - previouslyAttributedVolume, - ); err != nil { - return err - } - } - } - return nil -} diff --git a/protocol/x/affiliates/keeper/keeper_test.go b/protocol/x/affiliates/keeper/keeper_test.go index 017c2dd930..e64c1d3eaa 100644 --- a/protocol/x/affiliates/keeper/keeper_test.go +++ b/protocol/x/affiliates/keeper/keeper_test.go @@ -16,7 +16,6 @@ import ( keepertest "github.com/dydxprotocol/v4-chain/protocol/testutil/keeper" "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/keeper" "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/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" ) @@ -318,24 +317,38 @@ func TestUpdateAffiliateTiers(t *testing.T) { expectedError: types.ErrInvalidAffiliateTiers, }, { - name: "Invalid tiers - decreasing staking requirement", + name: "Taker fee share ppm greater than cap", affiliateTiers: types.AffiliateTiers{ Tiers: []types.AffiliateTiers_Tier{ - {ReqReferredVolumeQuoteQuantums: 1000, ReqStakedWholeCoins: 200, TakerFeeSharePpm: 100}, - {ReqReferredVolumeQuoteQuantums: 2000, ReqStakedWholeCoins: 100, TakerFeeSharePpm: 200}, + {ReqReferredVolumeQuoteQuantums: 1000, ReqStakedWholeCoins: 100, TakerFeeSharePpm: 100}, + {ReqReferredVolumeQuoteQuantums: 2000, ReqStakedWholeCoins: 200, TakerFeeSharePpm: 550_000}, // 55% }, }, - expectedError: types.ErrInvalidAffiliateTiers, + expectedError: types.ErrRevShareSafetyViolation, }, { - name: "Taker fee share ppm greater than cap", + name: "Valid tiers with req_staked_whole_coins set to 0 (deprecated field)", affiliateTiers: types.AffiliateTiers{ Tiers: []types.AffiliateTiers_Tier{ - {ReqReferredVolumeQuoteQuantums: 1000, ReqStakedWholeCoins: 100, TakerFeeSharePpm: 100}, - {ReqReferredVolumeQuoteQuantums: 2000, ReqStakedWholeCoins: 200, TakerFeeSharePpm: 550_000}, // 55% + {ReqReferredVolumeQuoteQuantums: 0, ReqStakedWholeCoins: 0, TakerFeeSharePpm: 50_000}, // 5% + {ReqReferredVolumeQuoteQuantums: 1_000_000, ReqStakedWholeCoins: 0, TakerFeeSharePpm: 100_000}, // 10% + {ReqReferredVolumeQuoteQuantums: 10_000_000, ReqStakedWholeCoins: 0, TakerFeeSharePpm: 150_000}, // 15% + {ReqReferredVolumeQuoteQuantums: 100_000_000, ReqStakedWholeCoins: 0, TakerFeeSharePpm: 200_000}, // 20% }, }, - expectedError: types.ErrRevShareSafetyViolation, + expectedError: nil, + }, + { + name: "Valid tiers with mixed req_staked_whole_coins values including 0", + affiliateTiers: types.AffiliateTiers{ + Tiers: []types.AffiliateTiers_Tier{ + {ReqReferredVolumeQuoteQuantums: 0, ReqStakedWholeCoins: 0, TakerFeeSharePpm: 50_000}, + {ReqReferredVolumeQuoteQuantums: 1_000_000, ReqStakedWholeCoins: 0, TakerFeeSharePpm: 100_000}, + {ReqReferredVolumeQuoteQuantums: 10_000_000, ReqStakedWholeCoins: 100, TakerFeeSharePpm: 150_000}, + {ReqReferredVolumeQuoteQuantums: 100_000_000, ReqStakedWholeCoins: 500, TakerFeeSharePpm: 200_000}, + }, + }, + expectedError: nil, }, } @@ -654,421 +667,6 @@ func TestGetTakerFeeShareViaWhitelist(t *testing.T) { } } -func TestAggregateAffiliateReferredVolumeForFills(t *testing.T) { - affiliate := constants.AliceAccAddress.String() - referee1 := constants.BobAccAddress.String() - referee2 := constants.DaveAccAddress.String() - maker := constants.CarlAccAddress.String() - testCases := []struct { - 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{}, - setup: func(t *testing.T, ctx sdk.Context, k *keeper.Keeper, statsKeeper *statskeeper.Keeper) { - statsKeeper.SetBlockStats(ctx, &statstypes.BlockStats{ - Fills: []*statstypes.BlockStats_Fill{ - { - Taker: referee1, - Maker: maker, - Notional: 100_000_000_000, - }, - }, - }) - }, - }, - { - name: "1 referral", - referrals: 1, - expectedVolume: big.NewInt(100_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) - - // 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{ - { - Taker: referee1, - Maker: maker, - Notional: 100_000_000_000, - AffiliateFeeGeneratedQuantums: 1_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, - }, - 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{ - { - 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, - }, - }, - }) - }, - }, - { - name: "2 referrals, maker also referred", - referrals: 2, - expectedVolume: big.NewInt(600_000_000_000), - expectedReferrer: []string{ - affiliate, - }, - expectedAttributedVolume: []uint64{ - 600_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) - 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: 3_000_000_000, - }, - }, - }) - err = k.UpdateAffiliateParameters(ctx, &types.MsgUpdateAffiliateParameters{ - Authority: constants.GovAuthority, - AffiliateParameters: types.AffiliateParameters{ - Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 800_000_000_000, - }, - }) - require.NoError(t, err) - }, - }, - { - name: "2 referrals, takers not referred, maker referred", - 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, 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{ - { - Taker: referee1, - Maker: maker, - Notional: 100_000_000_000, - AffiliateFeeGeneratedQuantums: 1_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) - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - tApp := testapp.NewTestAppBuilder(t).Build() - ctx := tApp.InitChain() - k := tApp.App.AffiliatesKeeper - statsKeeper := tApp.App.StatsKeeper - - err := k.UpdateAffiliateTiers(ctx, types.DefaultAffiliateTiers) - require.NoError(t, err) - - tc.setup(t, ctx, &k, &statsKeeper) - - err = k.AggregateAffiliateReferredVolumeForFills(ctx) - require.NoError(t, err) - - 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) - } - }) - } -} - func TestGetTierForAffiliateEmptyTiers(t *testing.T) { tApp := testapp.NewTestAppBuilder(t).Build() ctx := tApp.InitChain() diff --git a/protocol/x/affiliates/types/affiliates.pb.go b/protocol/x/affiliates/types/affiliates.pb.go index 4389973ae2..343b32a932 100644 --- a/protocol/x/affiliates/types/affiliates.pb.go +++ b/protocol/x/affiliates/types/affiliates.pb.go @@ -75,7 +75,8 @@ type AffiliateTiers_Tier struct { // Required all-time referred volume in quote quantums. ReqReferredVolumeQuoteQuantums uint64 `protobuf:"varint,1,opt,name=req_referred_volume_quote_quantums,json=reqReferredVolumeQuoteQuantums,proto3" json:"req_referred_volume_quote_quantums,omitempty"` // Required currently staked native tokens (in whole coins). - ReqStakedWholeCoins uint32 `protobuf:"varint,2,opt,name=req_staked_whole_coins,json=reqStakedWholeCoins,proto3" json:"req_staked_whole_coins,omitempty"` + // This is deprecated + ReqStakedWholeCoins uint32 `protobuf:"varint,2,opt,name=req_staked_whole_coins,json=reqStakedWholeCoins,proto3" json:"req_staked_whole_coins,omitempty"` // Deprecated: Do not use. // Taker fee share in parts-per-million. TakerFeeSharePpm uint32 `protobuf:"varint,3,opt,name=taker_fee_share_ppm,json=takerFeeSharePpm,proto3" json:"taker_fee_share_ppm,omitempty"` } @@ -120,6 +121,7 @@ func (m *AffiliateTiers_Tier) GetReqReferredVolumeQuoteQuantums() uint64 { return 0 } +// Deprecated: Do not use. func (m *AffiliateTiers_Tier) GetReqStakedWholeCoins() uint32 { if m != nil { return m.ReqStakedWholeCoins @@ -364,43 +366,43 @@ func init() { } var fileDescriptor_7de5ba9c426e9350 = []byte{ - // 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, + // 567 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x54, 0x41, 0x8f, 0xd2, 0x40, + 0x14, 0xa6, 0x80, 0x26, 0x3b, 0x46, 0x63, 0xca, 0x46, 0x2b, 0xd9, 0xd4, 0x0d, 0x27, 0x0e, 0x52, + 0x88, 0x98, 0xd5, 0x83, 0xd9, 0x08, 0x26, 0x1b, 0x35, 0x6b, 0x64, 0x8b, 0xee, 0x26, 0x5e, 0x26, + 0x03, 0x7d, 0xc0, 0xc4, 0x4e, 0xa7, 0xcc, 0x4c, 0x91, 0xbd, 0x78, 0xf0, 0x17, 0x78, 0xf3, 0x8f, + 0x98, 0x98, 0xf8, 0x0b, 0xf6, 0xb8, 0xf1, 0x64, 0x3c, 0x18, 0x03, 0x7f, 0xc4, 0x4c, 0x5b, 0xa0, + 0xc4, 0x45, 0xf7, 0xd2, 0x0c, 0xef, 0xfb, 0xde, 0x37, 0xef, 0x7b, 0x5f, 0x29, 0xaa, 0x7a, 0xa7, + 0xde, 0x34, 0x14, 0x5c, 0xf1, 0x3e, 0xf7, 0xeb, 0x64, 0x30, 0xa0, 0x3e, 0x25, 0x0a, 0x64, 0xe6, + 0xe8, 0xc4, 0xb0, 0x79, 0x3b, 0xcb, 0x74, 0x56, 0x70, 0xf9, 0x4e, 0x9f, 0x4b, 0xc6, 0x25, 0x8e, + 0xb1, 0x7a, 0xf2, 0x23, 0xe9, 0x29, 0x6f, 0x0f, 0xf9, 0x90, 0x27, 0x75, 0x7d, 0x4a, 0xaa, 0x95, + 0xcf, 0x79, 0x74, 0xa3, 0xb5, 0xe8, 0x7f, 0x4d, 0x41, 0x48, 0xf3, 0x19, 0xba, 0xa2, 0xf4, 0xc1, + 0x32, 0x76, 0x0b, 0xd5, 0x6b, 0xf7, 0xef, 0x39, 0x1b, 0x2e, 0x73, 0xd6, 0xfb, 0x1c, 0xfd, 0x6c, + 0x17, 0xcf, 0x7e, 0xdd, 0xcd, 0xb9, 0x89, 0x40, 0xf9, 0x9b, 0x81, 0x8a, 0xba, 0x6a, 0xbe, 0x40, + 0x15, 0x01, 0x63, 0x2c, 0x60, 0x00, 0x42, 0x80, 0x87, 0x27, 0xdc, 0x8f, 0x18, 0xe0, 0x71, 0xc4, + 0x95, 0x7e, 0x92, 0x40, 0x45, 0x4c, 0xdf, 0x67, 0x54, 0x8b, 0xae, 0x2d, 0x60, 0xec, 0xa6, 0xc4, + 0xe3, 0x98, 0x77, 0xa4, 0x69, 0x47, 0x29, 0xcb, 0x7c, 0x88, 0x6e, 0x69, 0x2d, 0xa9, 0xc8, 0x3b, + 0xf0, 0xf0, 0xfb, 0x11, 0xf7, 0x01, 0xf7, 0x39, 0x0d, 0xa4, 0x95, 0xdf, 0x35, 0xaa, 0xd7, 0xdb, + 0x79, 0xcb, 0x70, 0x4b, 0x02, 0xc6, 0xdd, 0x98, 0x70, 0xa2, 0xf1, 0xa7, 0x1a, 0x36, 0x6b, 0xa8, + 0xa4, 0x4b, 0x02, 0x0f, 0x00, 0xb0, 0x1c, 0x11, 0x01, 0x38, 0x0c, 0x99, 0x55, 0xd0, 0x5d, 0xee, + 0xcd, 0x18, 0x3a, 0x00, 0xe8, 0x6a, 0xa0, 0x13, 0xb2, 0xca, 0x57, 0x03, 0x99, 0x4b, 0x87, 0x27, + 0x23, 0xaa, 0xc0, 0xa7, 0x52, 0x99, 0x87, 0xeb, 0xdb, 0x69, 0xfc, 0x7f, 0x3b, 0xcb, 0xde, 0x0b, + 0x36, 0xd4, 0x4d, 0x17, 0xb4, 0x83, 0xb6, 0x88, 0xe7, 0x09, 0x90, 0x12, 0x12, 0xe5, 0x2d, 0x77, + 0x55, 0xd8, 0x34, 0x79, 0x7e, 0xc3, 0xe4, 0x3f, 0xf3, 0xa8, 0xb4, 0xbc, 0xbd, 0x43, 0x04, 0x61, + 0xa0, 0x74, 0xb0, 0x1f, 0x0d, 0xf4, 0x84, 0x91, 0x29, 0x65, 0x11, 0xc3, 0xcd, 0x86, 0x87, 0x89, + 0x52, 0x82, 0xf6, 0x22, 0x45, 0x7a, 0x3e, 0x2c, 0x22, 0x09, 0x41, 0xac, 0x62, 0x8a, 0x24, 0x88, + 0x8b, 0x43, 0x7a, 0x94, 0xea, 0x34, 0x1b, 0x5e, 0x2b, 0xa3, 0x92, 0xe4, 0xd5, 0x01, 0xb1, 0x08, + 0xf0, 0x8d, 0x04, 0xb1, 0x1e, 0xdf, 0x3e, 0xda, 0x89, 0xf5, 0x01, 0x30, 0xa3, 0x41, 0x3c, 0x8b, + 0x76, 0xa5, 0xd7, 0x81, 0xa9, 0x37, 0x4d, 0x4d, 0x59, 0x29, 0xe7, 0x65, 0x42, 0x39, 0x80, 0xf8, + 0x1d, 0x7b, 0xee, 0x4d, 0xcd, 0x0f, 0x68, 0x7f, 0xcd, 0xc3, 0xc2, 0x27, 0x16, 0x30, 0x81, 0x20, + 0xba, 0x84, 0x83, 0x42, 0xec, 0x60, 0x2f, 0xe3, 0x60, 0xa1, 0xe1, 0x26, 0x12, 0xff, 0x9a, 0xbf, + 0x72, 0x98, 0x79, 0x2b, 0x5e, 0x4d, 0x40, 0x08, 0xea, 0x81, 0x34, 0xf7, 0xfe, 0xca, 0xaf, 0x6d, + 0x7d, 0xff, 0x52, 0xdb, 0x4e, 0xff, 0x81, 0xad, 0x04, 0xeb, 0x2a, 0x41, 0x83, 0x61, 0x26, 0xd9, + 0xf6, 0xf1, 0xd9, 0xcc, 0x36, 0xce, 0x67, 0xb6, 0xf1, 0x7b, 0x66, 0x1b, 0x9f, 0xe6, 0x76, 0xee, + 0x7c, 0x6e, 0xe7, 0x7e, 0xcc, 0xed, 0xdc, 0xdb, 0xc7, 0x43, 0xaa, 0x46, 0x51, 0xcf, 0xe9, 0x73, + 0x56, 0x5f, 0xfb, 0x2e, 0x4c, 0x1e, 0xd4, 0xfa, 0x23, 0x42, 0x83, 0xfa, 0xb2, 0x32, 0xcd, 0x7e, + 0x2b, 0xd4, 0x69, 0x08, 0xb2, 0x77, 0x35, 0x06, 0x9b, 0x7f, 0x02, 0x00, 0x00, 0xff, 0xff, 0xe3, + 0x02, 0x97, 0xa7, 0x53, 0x04, 0x00, 0x00, } func (m *AffiliateTiers) Marshal() (dAtA []byte, err error) { diff --git a/protocol/x/affiliates/types/expected_keepers.go b/protocol/x/affiliates/types/expected_keepers.go index bc7ccf321b..4de8a55b4d 100644 --- a/protocol/x/affiliates/types/expected_keepers.go +++ b/protocol/x/affiliates/types/expected_keepers.go @@ -12,6 +12,8 @@ type StatsKeeper interface { GetBlockStats(ctx sdk.Context) *stattypes.BlockStats GetUserStats(ctx sdk.Context, address string) *stattypes.UserStats SetUserStats(ctx sdk.Context, address string, userStats *stattypes.UserStats) + GetEpochStatsOrNil(ctx sdk.Context, epoch uint32) *stattypes.EpochStats + SetEpochStats(ctx sdk.Context, epoch uint32, epochStats *stattypes.EpochStats) } type FeetiersKeeper interface { diff --git a/protocol/x/affiliates/types/query.pb.go b/protocol/x/affiliates/types/query.pb.go index 58e529a11a..305363f9b3 100644 --- a/protocol/x/affiliates/types/query.pb.go +++ b/protocol/x/affiliates/types/query.pb.go @@ -94,6 +94,8 @@ type AffiliateInfoResponse struct { 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"` + // The affiliate's 30d attributed volume in quote quantums (from referees). + AttributedVolume_30DRolling github_com_dydxprotocol_v4_chain_protocol_dtypes.SerializableInt `protobuf:"bytes,7,opt,name=attributed_volume_30d_rolling,json=attributedVolume30dRolling,proto3,customtype=github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt" json:"attributed_volume_30d_rolling"` } func (m *AffiliateInfoResponse) Reset() { *m = AffiliateInfoResponse{} } @@ -599,59 +601,60 @@ func init() { } var fileDescriptor_2edc1b3ea39b05a9 = []byte{ - // 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, + // 843 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x95, 0xcd, 0x6e, 0xeb, 0x44, + 0x14, 0xc7, 0xe3, 0x4b, 0x7a, 0x3f, 0xe6, 0x36, 0x85, 0x0e, 0x45, 0xb8, 0x6e, 0x49, 0x2b, 0x23, + 0x44, 0x44, 0x1b, 0xbb, 0x4d, 0x0a, 0x12, 0x12, 0x0b, 0x12, 0x84, 0xa0, 0xdd, 0xb4, 0xb8, 0xa8, + 0x48, 0xb0, 0x30, 0x4e, 0x3c, 0x49, 0x46, 0xd8, 0x1e, 0x77, 0x66, 0x52, 0x1a, 0x10, 0x1b, 0x36, + 0x2c, 0xd8, 0x20, 0xb1, 0xe6, 0x01, 0x90, 0x58, 0x20, 0x81, 0xc4, 0x2b, 0x74, 0x59, 0xc1, 0x06, + 0xb1, 0xa8, 0x50, 0xcb, 0x13, 0xf0, 0x04, 0x28, 0xf6, 0x78, 0xec, 0x7c, 0x29, 0x8e, 0xe8, 0xce, + 0x39, 0x67, 0xce, 0xf9, 0xff, 0x7c, 0xe2, 0xff, 0x19, 0xf0, 0xb2, 0x3b, 0x70, 0x2f, 0x43, 0x4a, + 0x38, 0x69, 0x13, 0xcf, 0x74, 0x3a, 0x1d, 0xec, 0x61, 0x87, 0x23, 0x66, 0x9e, 0xf7, 0x11, 0x1d, + 0x18, 0x51, 0x06, 0xbe, 0x98, 0x3d, 0x64, 0xa4, 0x87, 0xb4, 0xf5, 0x36, 0x61, 0x3e, 0x61, 0x76, + 0x94, 0x33, 0xe3, 0x1f, 0x71, 0x8d, 0xb6, 0xd6, 0x25, 0x5d, 0x12, 0xc7, 0x87, 0x4f, 0x22, 0x5a, + 0x99, 0x25, 0x97, 0x3e, 0x8a, 0x93, 0x9b, 0x5d, 0x42, 0xba, 0x1e, 0x32, 0x9d, 0x10, 0x9b, 0x4e, + 0x10, 0x10, 0xee, 0x70, 0x4c, 0x02, 0x91, 0xd5, 0x8f, 0xc0, 0x5a, 0x23, 0xa9, 0x38, 0x0c, 0x3a, + 0xc4, 0x42, 0xe7, 0x7d, 0xc4, 0x38, 0xac, 0x81, 0x47, 0x8e, 0xeb, 0x52, 0xc4, 0x98, 0xaa, 0x6c, + 0x2b, 0x95, 0x27, 0x4d, 0xf5, 0xf7, 0x5f, 0xab, 0x6b, 0x02, 0xac, 0x11, 0x67, 0x4e, 0x39, 0xc5, + 0x41, 0xd7, 0x4a, 0x0e, 0xea, 0xff, 0x16, 0xc1, 0x0b, 0x63, 0xcd, 0x58, 0x48, 0x02, 0x86, 0xe0, + 0x2b, 0x60, 0x05, 0x33, 0xfb, 0xf3, 0x1e, 0xe6, 0xc8, 0xc3, 0x8c, 0x23, 0x37, 0x6a, 0xfa, 0xd8, + 0x2a, 0x61, 0xf6, 0x51, 0x1a, 0x84, 0x10, 0x14, 0x39, 0x46, 0x54, 0x7d, 0xb0, 0xad, 0x54, 0x4a, + 0x56, 0xf4, 0x0c, 0x75, 0x50, 0xea, 0x20, 0x64, 0xb3, 0x9e, 0x43, 0x91, 0x1d, 0x86, 0xbe, 0xfa, + 0x4c, 0x94, 0x7c, 0xda, 0x41, 0xe8, 0x74, 0x18, 0x3b, 0x09, 0x7d, 0xc8, 0xc0, 0xb3, 0x14, 0x75, + 0x10, 0xa5, 0xc8, 0xb5, 0x2f, 0x88, 0xd7, 0xf7, 0x91, 0x5a, 0xdc, 0x56, 0x2a, 0xcb, 0xcd, 0xa3, + 0xab, 0x9b, 0xad, 0xc2, 0x5f, 0x37, 0x5b, 0x6f, 0x77, 0x31, 0xef, 0xf5, 0x5b, 0x46, 0x9b, 0xf8, + 0xe6, 0xc8, 0xe0, 0x2e, 0x0e, 0xaa, 0xed, 0x9e, 0x83, 0x03, 0x53, 0x46, 0x5c, 0x3e, 0x08, 0x11, + 0x33, 0x4e, 0x11, 0xc5, 0x8e, 0x87, 0xbf, 0x70, 0x5a, 0x1e, 0x3a, 0x0c, 0xb8, 0xaa, 0x58, 0x2b, + 0x89, 0xc4, 0x59, 0xa4, 0x00, 0x7d, 0x50, 0x62, 0xdc, 0xf9, 0x0c, 0xb9, 0xb6, 0xe3, 0x93, 0x7e, + 0xc0, 0xd5, 0xa5, 0x48, 0xf2, 0xfd, 0xfb, 0x92, 0xb4, 0x96, 0xe3, 0xf6, 0x8d, 0xa8, 0x3b, 0xfc, + 0x46, 0x01, 0x1b, 0x63, 0x2f, 0x69, 0xd7, 0xf7, 0x5c, 0x9b, 0x12, 0xcf, 0xc3, 0x41, 0x57, 0x7d, + 0x78, 0xcf, 0xea, 0xea, 0xe8, 0xeb, 0xd6, 0xf7, 0x5c, 0x2b, 0x56, 0x82, 0xdf, 0x2a, 0xe0, 0x25, + 0x87, 0x73, 0x8a, 0x5b, 0x7d, 0x3e, 0x9d, 0xe5, 0xd1, 0x3d, 0xb3, 0x68, 0xa9, 0xdc, 0x38, 0x8d, + 0xfe, 0x1e, 0x58, 0xb5, 0x04, 0x69, 0x73, 0xf0, 0x7f, 0xbe, 0xde, 0x4f, 0x00, 0xcc, 0x36, 0x12, + 0x5f, 0xee, 0xbb, 0x60, 0x55, 0x3a, 0xca, 0xce, 0xdb, 0xf3, 0x39, 0x59, 0x22, 0xe2, 0xba, 0x06, + 0xd4, 0x86, 0xe7, 0x49, 0x73, 0x7c, 0x88, 0x11, 0x65, 0x02, 0x56, 0xff, 0x14, 0xac, 0x4f, 0xc9, + 0x09, 0xfd, 0x77, 0xc0, 0xd2, 0xd0, 0x06, 0xb1, 0xe6, 0xd3, 0xda, 0xab, 0xc6, 0x8c, 0x0d, 0x62, + 0x8c, 0xd6, 0x37, 0x8b, 0xc3, 0xe1, 0x5b, 0x71, 0xad, 0xbe, 0x01, 0xd6, 0x65, 0x5a, 0xfa, 0x2d, + 0x91, 0xf7, 0x81, 0x36, 0x2d, 0x29, 0xf4, 0x8f, 0xc1, 0x13, 0x69, 0x5b, 0xc1, 0xb0, 0x33, 0x9f, + 0x41, 0xf6, 0x11, 0x1c, 0x69, 0x8f, 0x11, 0x96, 0xe3, 0x0b, 0x44, 0x29, 0x76, 0x11, 0x9b, 0xc6, + 0x92, 0x49, 0xa6, 0x2c, 0x24, 0x09, 0xe6, 0x67, 0x91, 0x7d, 0x12, 0x16, 0xd9, 0x43, 0xdf, 0xcc, + 0xc8, 0x9d, 0x38, 0xd4, 0xf1, 0x11, 0xcf, 0xfc, 0x2f, 0xe7, 0x60, 0x63, 0x6a, 0x56, 0xd0, 0x58, + 0x00, 0x84, 0x32, 0x2a, 0x70, 0x76, 0xe7, 0xe3, 0xa4, 0x9d, 0x04, 0x4f, 0xa6, 0x4b, 0xed, 0xa7, + 0xc7, 0x60, 0xe9, 0x83, 0xe1, 0x7d, 0x01, 0x7f, 0x54, 0x40, 0x69, 0x64, 0x97, 0xc2, 0xea, 0xfc, + 0xde, 0x99, 0x05, 0xae, 0x19, 0x79, 0x8f, 0xc7, 0xaf, 0xa3, 0xbf, 0xf9, 0xf5, 0x1f, 0xff, 0x7c, + 0xff, 0xa0, 0x0e, 0xf7, 0xcd, 0xb9, 0x37, 0x8b, 0x8d, 0x83, 0x0e, 0x31, 0xbf, 0x14, 0x6e, 0xf8, + 0x0a, 0xfe, 0xa0, 0x00, 0x90, 0x5a, 0x07, 0xbe, 0x36, 0x53, 0x79, 0xc2, 0xa8, 0xda, 0x4e, 0xae, + 0xb3, 0x02, 0xf1, 0x8d, 0x08, 0x71, 0x0f, 0x1a, 0x33, 0x11, 0xe5, 0x82, 0x6c, 0x0d, 0x32, 0x7c, + 0x3f, 0x2b, 0x60, 0x75, 0xc2, 0x61, 0x70, 0x7f, 0xf6, 0x80, 0x66, 0x38, 0x55, 0xab, 0x2d, 0x52, + 0x22, 0xa0, 0x0f, 0x22, 0x68, 0x03, 0xee, 0xce, 0x9e, 0xab, 0xe7, 0xd9, 0xe9, 0x6c, 0x23, 0xc7, + 0xc2, 0x5f, 0x14, 0x00, 0x27, 0xdd, 0x04, 0x6b, 0x0b, 0x58, 0x2f, 0x81, 0xae, 0x2f, 0x54, 0x93, + 0x9f, 0x5a, 0x12, 0x4b, 0x6f, 0xc3, 0xdf, 0x14, 0xf0, 0xfc, 0x94, 0x0f, 0x1d, 0xd6, 0x17, 0xb1, + 0x45, 0xc2, 0x7d, 0xb0, 0x58, 0x91, 0x00, 0x7f, 0x3d, 0x02, 0x37, 0x61, 0x35, 0x07, 0x78, 0x6a, + 0xbc, 0xd1, 0x79, 0xcb, 0x8d, 0x91, 0x67, 0xde, 0xe3, 0x3b, 0x2c, 0xcf, 0xbc, 0x27, 0x56, 0xdb, + 0x42, 0xf3, 0x96, 0xfb, 0xab, 0x79, 0x76, 0x75, 0x5b, 0x56, 0xae, 0x6f, 0xcb, 0xca, 0xdf, 0xb7, + 0x65, 0xe5, 0xbb, 0xbb, 0x72, 0xe1, 0xfa, 0xae, 0x5c, 0xf8, 0xf3, 0xae, 0x5c, 0xf8, 0xf8, 0xad, + 0xfc, 0x77, 0xee, 0x65, 0x56, 0x25, 0xba, 0x7f, 0x5b, 0x0f, 0xa3, 0x64, 0xfd, 0xbf, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x88, 0x2f, 0xce, 0x39, 0xd4, 0x0a, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -976,6 +979,16 @@ func (m *AffiliateInfoResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + { + size := m.AttributedVolume_30DRolling.Size() + i -= size + if _, err := m.AttributedVolume_30DRolling.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x3a { size := m.ReferredVolume_30DRolling.Size() i -= size @@ -1358,6 +1371,8 @@ func (m *AffiliateInfoResponse) Size() (n int) { n += 1 + l + sovQuery(uint64(l)) l = m.ReferredVolume_30DRolling.Size() n += 1 + l + sovQuery(uint64(l)) + l = m.AttributedVolume_30DRolling.Size() + n += 1 + l + sovQuery(uint64(l)) return n } @@ -1741,6 +1756,39 @@ func (m *AffiliateInfoResponse) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 7: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AttributedVolume_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.AttributedVolume_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/process_single_match.go b/protocol/x/clob/keeper/process_single_match.go index 9b6cf65ac2..6f0567f20f 100644 --- a/protocol/x/clob/keeper/process_single_match.go +++ b/protocol/x/clob/keeper/process_single_match.go @@ -16,6 +16,7 @@ import ( assettypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/types" "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" revsharetypes "github.com/dydxprotocol/v4-chain/protocol/x/revshare/types" + statstypes "github.com/dydxprotocol/v4-chain/protocol/x/stats/types" satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" gometrics "github.com/hashicorp/go-metrics" ) @@ -576,12 +577,21 @@ func (k Keeper) persistMatchedOrders( revSharesForFill, ) + attributableVolumeAttributions := k.buildAttributableVolumeAttributions( + ctx, + revSharesForFill, + bigFillQuoteQuantums, + matchWithOrders, + affiliateParameters, + ) + k.statsKeeper.RecordFill( ctx, matchWithOrders.TakerOrder.GetSubaccountId().Owner, matchWithOrders.MakerOrder.GetSubaccountId().Owner, bigFillQuoteQuantums, affiliateRevSharesQuoteQuantums, + attributableVolumeAttributions, ) takerOrderRouterFeeQuoteQuantums := big.NewInt(0) @@ -629,6 +639,99 @@ func (k Keeper) persistMatchedOrders( return takerUpdateResult, makerUpdateResult, affiliateRevSharesQuoteQuantums, nil } +// getAttributableVolume calculates the attributable volume for a referee based on their +// already-attributed volume in the last 30 days and the maximum attributable volume cap. +// This does not modify any state. +func (k Keeper) getAttributableVolume( + ctx sdk.Context, + referee string, + volume uint64, + affiliateParameters affiliatetypes.AffiliateParameters, +) uint64 { + // Get the user stats from the referee + refereeUserStats := k.statsKeeper.GetUserStats(ctx, referee) + if refereeUserStats == nil { + return 0 + } + + // Use the ATTRIBUTED volume (how much has already been attributed to their affiliate) + // NOT total trading volume (TakerNotional + MakerNotional) + previouslyAttributedVolume := refereeUserStats.Affiliate_30DAttributedVolumeQuoteQuantums + + // If parameter is 0 then no limit is applied + cap := affiliateParameters.Maximum_30DAttributableVolumePerReferredUserQuoteQuantums + if cap == 0 { + return volume + } + + if previouslyAttributedVolume >= cap { + return 0 + } else if previouslyAttributedVolume+volume > cap { + // Remainder of the volume to get them to the cap + return cap - previouslyAttributedVolume + } + + return volume +} + +func (k Keeper) buildAttributableVolumeAttributions( + ctx sdk.Context, + revSharesForFill revsharetypes.RevSharesForFill, + bigFillQuoteQuantums *big.Int, + matchWithOrders *types.MatchWithOrders, + affiliateParameters affiliatetypes.AffiliateParameters, +) []*statstypes.AffiliateAttribution { + // Build affiliate revenue attributions array (can include both taker and maker) + var affiliateRevenueAttributions []*statstypes.AffiliateAttribution + + // Add taker affiliate attribution if present + if revSharesForFill.AffiliateRevShare != nil && + revSharesForFill.AffiliateRevShare.Recipient != "" && + bigFillQuoteQuantums.Sign() > 0 { + // Calculate the attributable volume based on the taker's current 30-day volume + // and the maximum attributable volume cap from affiliate parameters + takerAttributableVolume := k.getAttributableVolume( + ctx, + matchWithOrders.TakerOrder.GetSubaccountId().Owner, + bigFillQuoteQuantums.Uint64(), + affiliateParameters, + ) + if takerAttributableVolume > 0 { + affiliateRevenueAttributions = append(affiliateRevenueAttributions, &statstypes.AffiliateAttribution{ + Role: statstypes.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: revSharesForFill.AffiliateRevShare.Recipient, + ReferredVolumeQuoteQuantums: takerAttributableVolume, + }) + } + } + + // Add maker affiliate attribution if present + // Check if maker has an affiliate referrer + makerReferrer, makerHasReferrer := k.affiliatesKeeper.GetReferredBy( + ctx, + matchWithOrders.MakerOrder.GetSubaccountId().Owner, + ) + if makerHasReferrer && makerReferrer != "" && bigFillQuoteQuantums.Sign() > 0 { + // Calculate the attributable volume based on the maker's current 30-day volume + // and the maximum attributable volume cap from affiliate parameters + makerAttributableVolume := k.getAttributableVolume( + ctx, + matchWithOrders.MakerOrder.GetSubaccountId().Owner, + bigFillQuoteQuantums.Uint64(), + affiliateParameters, + ) + if makerAttributableVolume > 0 { + affiliateRevenueAttributions = append(affiliateRevenueAttributions, &statstypes.AffiliateAttribution{ + Role: statstypes.AffiliateAttribution_ROLE_MAKER, + ReferrerAddress: makerReferrer, + ReferredVolumeQuoteQuantums: makerAttributableVolume, + }) + } + } + + return affiliateRevenueAttributions +} + func (k Keeper) setOrderFillAmountsAndPruning( ctx sdk.Context, order types.Order, diff --git a/protocol/x/clob/keeper/process_single_match_affiliate_stats_test.go b/protocol/x/clob/keeper/process_single_match_affiliate_stats_test.go new file mode 100644 index 0000000000..14183504ba --- /dev/null +++ b/protocol/x/clob/keeper/process_single_match_affiliate_stats_test.go @@ -0,0 +1,890 @@ +package keeper_test + +import ( + "testing" + + "github.com/dydxprotocol/v4-chain/protocol/dtypes" + testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" + affiliatetypes "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/types" + clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + statstypes "github.com/dydxprotocol/v4-chain/protocol/x/stats/types" + satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" + "github.com/stretchr/testify/require" +) + +// TestProcessSingleMatch_AffiliateAttribution_TakerOnly tests that when only the taker +// has an affiliate referrer, the attribution is correctly stored in BlockStats. +func TestProcessSingleMatch_AffiliateAttribution_TakerOnly(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + + ctx := tApp.InitChain() + k := tApp.App.ClobKeeper + + // Create taker and maker subaccounts with sufficient collateral + takerSubaccount := constants.Alice_Num0 + makerSubaccount := constants.Bob_Num0 + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &takerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), // 1M USDC + }, + }, + }) + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &makerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), // 1M USDC + }, + }, + }) + + // Set up affiliate tiers + err := tApp.App.AffiliatesKeeper.UpdateAffiliateTiers(ctx, affiliatetypes.AffiliateTiers{ + Tiers: []affiliatetypes.AffiliateTiers_Tier{ + { + ReqReferredVolumeQuoteQuantums: 0, + ReqStakedWholeCoins: 0, + TakerFeeSharePpm: 100_000, // 10% + }, + }, + }) + require.NoError(t, err) + + // Register taker with an affiliate referrer (use Carl as referrer) + referrerAddr := constants.CarlAccAddress.String() + err = tApp.App.AffiliatesKeeper.RegisterAffiliate(ctx, constants.Alice_Num0.Owner, referrerAddr) + require.NoError(t, err) + + // Create orders + takerOrder := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: takerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 100_000_000, // 1 BTC (in base quantums) + Subticks: 5_000_000_000, // $50,000 per BTC + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + makerOrder := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: makerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 100_000_000, + Subticks: 5_000_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + matchWithOrders := &clobtypes.MatchWithOrders{ + TakerOrder: &takerOrder, + MakerOrder: &makerOrder, + FillAmount: satypes.BaseQuantums(100_000_000), // Fill full amount + } + + // Process the match + success, _, _, _, err := k.ProcessSingleMatch( + ctx, + matchWithOrders, + map[string]bool{}, + affiliatetypes.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 1_000_000_000_000, + }, + ) + require.NoError(t, err) + require.True(t, success) + + // Verify BlockStats contains the affiliate revenue attribution for taker only + blockStats := tApp.App.StatsKeeper.GetBlockStats(ctx) + require.Len(t, blockStats.Fills, 1) + + fill := blockStats.Fills[0] + require.Equal(t, constants.Alice_Num0.Owner, fill.Taker) + require.Equal(t, constants.Bob_Num0.Owner, fill.Maker) + + // Verify affiliate revenue attributions array + require.Len(t, fill.AffiliateAttributions, 1, "Should have exactly one attribution (taker only)") + + attribution := fill.AffiliateAttributions[0] + require.Equal(t, statstypes.AffiliateAttribution_ROLE_TAKER, attribution.Role) + require.Equal(t, referrerAddr, attribution.ReferrerAddress) +} + +// TestProcessSingleMatch_AffiliateAttribution_BothTakerAndMaker tests that when both +// taker and maker have affiliate referrers, both attributions are stored in BlockStats. +func TestProcessSingleMatch_AffiliateAttribution_BothTakerAndMaker(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + + ctx := tApp.InitChain() + k := tApp.App.ClobKeeper + + // Create subaccounts + takerSubaccount := constants.Alice_Num0 + makerSubaccount := constants.Bob_Num0 + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &takerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + }) + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &makerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + }) + + // Set up affiliate tiers + err := tApp.App.AffiliatesKeeper.UpdateAffiliateTiers(ctx, affiliatetypes.AffiliateTiers{ + Tiers: []affiliatetypes.AffiliateTiers_Tier{ + { + ReqReferredVolumeQuoteQuantums: 0, + ReqStakedWholeCoins: 0, + TakerFeeSharePpm: 100_000, // 10% + }, + }, + }) + require.NoError(t, err) + + // Register BOTH taker and maker with different affiliate referrers + takerReferrerAddr := constants.CarlAccAddress.String() + makerReferrerAddr := constants.DaveAccAddress.String() + + err = tApp.App.AffiliatesKeeper.RegisterAffiliate(ctx, constants.Alice_Num0.Owner, takerReferrerAddr) + require.NoError(t, err) + + err = tApp.App.AffiliatesKeeper.RegisterAffiliate(ctx, constants.Bob_Num0.Owner, makerReferrerAddr) + require.NoError(t, err) + + // Create orders + takerOrder := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: takerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 100_000_000, + Subticks: 5_000_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + makerOrder := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: makerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 100_000_000, + Subticks: 5_000_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + matchWithOrders := &clobtypes.MatchWithOrders{ + TakerOrder: &takerOrder, + MakerOrder: &makerOrder, + FillAmount: satypes.BaseQuantums(100_000_000), + } + + // Process the match + success, _, _, _, err := k.ProcessSingleMatch( + ctx, + matchWithOrders, + map[string]bool{}, + affiliatetypes.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: 1_000_000_000_000, + }, + ) + require.NoError(t, err) + require.True(t, success) + + // Verify BlockStats contains BOTH affiliate revenue attributions + blockStats := tApp.App.StatsKeeper.GetBlockStats(ctx) + require.Len(t, blockStats.Fills, 1) + + fill := blockStats.Fills[0] + require.Equal(t, constants.Alice_Num0.Owner, fill.Taker) + require.Equal(t, constants.Bob_Num0.Owner, fill.Maker) + + // Verify we have TWO attributions + require.Len(t, fill.AffiliateAttributions, 2, "Should have two attributions (taker and maker)") + + // Find taker and maker attributions by role + var takerAttribution, makerAttribution *statstypes.AffiliateAttribution + for _, attr := range fill.AffiliateAttributions { + if attr.Role == statstypes.AffiliateAttribution_ROLE_TAKER { + takerAttribution = attr + } else if attr.Role == statstypes.AffiliateAttribution_ROLE_MAKER { + makerAttribution = attr + } + } + + require.NotNil(t, takerAttribution, "Should have taker attribution") + require.NotNil(t, makerAttribution, "Should have maker attribution") + + // Verify roles and referrers + require.Equal(t, statstypes.AffiliateAttribution_ROLE_TAKER, takerAttribution.Role) + require.Equal(t, takerReferrerAddr, takerAttribution.ReferrerAddress) + require.Equal(t, statstypes.AffiliateAttribution_ROLE_MAKER, makerAttribution.Role) + require.Equal(t, makerReferrerAddr, makerAttribution.ReferrerAddress) +} + +// TestProcessSingleMatch_AffiliateAttribution_NoReferrers tests that when neither +// taker nor maker has an affiliate referrer, no attributions are stored. +func TestProcessSingleMatch_AffiliateAttribution_NoReferrers(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + ctx := tApp.InitChain() + k := tApp.App.ClobKeeper + + // Create subaccounts (no affiliate registrations) + takerSubaccount := constants.Alice_Num0 + makerSubaccount := constants.Bob_Num0 + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &takerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + }) + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &makerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + }) + + // Create orders + takerOrder := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: takerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 100_000_000, + Subticks: 5_000_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + makerOrder := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: makerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 100_000_000, + Subticks: 5_000_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + matchWithOrders := &clobtypes.MatchWithOrders{ + TakerOrder: &takerOrder, + MakerOrder: &makerOrder, + FillAmount: satypes.BaseQuantums(100_000_000), + } + + // Process the match + success, _, _, _, err := k.ProcessSingleMatch( + ctx, + matchWithOrders, + map[string]bool{}, + affiliatetypes.AffiliateParameters{}, + ) + require.NoError(t, err) + require.True(t, success) + + // Verify BlockStats has no affiliate revenue attributions + blockStats := tApp.App.StatsKeeper.GetBlockStats(ctx) + require.Len(t, blockStats.Fills, 1) + + fill := blockStats.Fills[0] + require.Empty(t, fill.AffiliateAttributions, "Should have no attributions when neither has referrer") +} + +// TestProcessSingleMatch_AffiliateAttribution_VolumeCapApplied tests that the +// attributable volume cap is correctly applied when storing attributions. +func TestProcessSingleMatch_AffiliateAttribution_VolumeCapApplied(t *testing.T) { + lowCap := uint64(100_000_000_000) // Cap at 100k USDC + + tApp := testapp.NewTestAppBuilder(t).Build() + + ctx := tApp.InitChain() + k := tApp.App.ClobKeeper + + // Create subaccounts + takerSubaccount := constants.Alice_Num0 + makerSubaccount := constants.Bob_Num0 + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &takerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + }) + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &makerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + }) + + // Set up affiliate parameters with low cap + err := tApp.App.AffiliatesKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{ + Authority: constants.GovAuthority, + AffiliateParameters: affiliatetypes.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: lowCap, + }, + }) + require.NoError(t, err) + + // Set up affiliate tiers + err = tApp.App.AffiliatesKeeper.UpdateAffiliateTiers(ctx, affiliatetypes.AffiliateTiers{ + Tiers: []affiliatetypes.AffiliateTiers_Tier{ + { + ReqReferredVolumeQuoteQuantums: 0, + ReqStakedWholeCoins: 0, + TakerFeeSharePpm: 100_000, // 10% + }, + }, + }) + require.NoError(t, err) + + // Register taker with referrer (use Carl as referrer) + referrerAddr := constants.CarlAccAddress.String() + err = tApp.App.AffiliatesKeeper.RegisterAffiliate(ctx, constants.Alice_Num0.Owner, referrerAddr) + require.NoError(t, err) + + // Create a large trade that exceeds the cap (400k USDC notional) + // Need to create a very large trade to exceed 100k cap + takerOrder := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: takerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 8_000_000_000, // 80 BTC to get large enough notional + Subticks: 5_000_000_000, // $50,000 per BTC + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + makerOrder := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: makerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 8_000_000_000, + Subticks: 5_000_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + matchWithOrders := &clobtypes.MatchWithOrders{ + TakerOrder: &takerOrder, + MakerOrder: &makerOrder, + FillAmount: satypes.BaseQuantums(8_000_000_000), + } + + // Process the match + success, _, _, _, err := k.ProcessSingleMatch( + ctx, + matchWithOrders, + map[string]bool{}, + affiliatetypes.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: lowCap, + }, + ) + require.NoError(t, err) + require.True(t, success) + + // Verify the attributed volume is capped + blockStats := tApp.App.StatsKeeper.GetBlockStats(ctx) + require.Len(t, blockStats.Fills, 1) + + fill := blockStats.Fills[0] + require.Len(t, fill.AffiliateAttributions, 1) + + attribution := fill.AffiliateAttributions[0] + require.Equal(t, statstypes.AffiliateAttribution_ROLE_TAKER, attribution.Role) + require.Equal(t, referrerAddr, attribution.ReferrerAddress) + + // Verify the trade notional exceeds the cap + require.Greater(t, fill.Notional, lowCap, "Trade notional should exceed the cap for this test") +} + +// TestProcessSingleMatch_AffiliateAttribution_AlreadyAtCap tests that when a user +// has already reached the 30-day attributable volume cap, no new volume is attributed. +func TestProcessSingleMatch_AffiliateAttribution_AlreadyAtCap(t *testing.T) { + cap := uint64(100_000_000_000) // 100k USDC cap + + tApp := testapp.NewTestAppBuilder(t).Build() + ctx := tApp.InitChain() + k := tApp.App.ClobKeeper + + // Create subaccounts + takerSubaccount := constants.Alice_Num0 + makerSubaccount := constants.Bob_Num0 + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &takerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + }) + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &makerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + }) + + // Set up affiliate parameters with cap + err := tApp.App.AffiliatesKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{ + Authority: constants.GovAuthority, + AffiliateParameters: affiliatetypes.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: cap, + }, + }) + require.NoError(t, err) + + // Set up affiliate tiers + err = tApp.App.AffiliatesKeeper.UpdateAffiliateTiers(ctx, affiliatetypes.AffiliateTiers{ + Tiers: []affiliatetypes.AffiliateTiers_Tier{ + { + ReqReferredVolumeQuoteQuantums: 0, + ReqStakedWholeCoins: 0, + TakerFeeSharePpm: 100_000, // 10% + }, + }, + }) + require.NoError(t, err) + + // Register taker with referrer + referrerAddr := constants.CarlAccAddress.String() + err = tApp.App.AffiliatesKeeper.RegisterAffiliate(ctx, constants.Alice_Num0.Owner, referrerAddr) + require.NoError(t, err) + + // Set taker's previous ATTRIBUTED volume to EXACTLY the cap + // (This is the key fix - we track attributed volume, not total trading volume) + tApp.App.StatsKeeper.SetUserStats(ctx, constants.Alice_Num0.Owner, &statstypes.UserStats{ + TakerNotional: 200_000_000_000, // User has traded 200k total + MakerNotional: 0, + Affiliate_30DAttributedVolumeQuoteQuantums: cap, // But only 100k was attributed (at cap) + }) + + // Create a normal trade + takerOrder := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: takerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 100_000_000, // 1 BTC + Subticks: 5_000_000_000, // $50,000 per BTC + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + makerOrder := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: makerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 100_000_000, + Subticks: 5_000_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + matchWithOrders := &clobtypes.MatchWithOrders{ + TakerOrder: &takerOrder, + MakerOrder: &makerOrder, + FillAmount: satypes.BaseQuantums(100_000_000), + } + + // Process the match + success, _, _, _, err := k.ProcessSingleMatch( + ctx, + matchWithOrders, + map[string]bool{}, + affiliatetypes.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: cap, + }, + ) + require.NoError(t, err) + require.True(t, success) + + // Verify NO attribution was made since user is already at cap + blockStats := tApp.App.StatsKeeper.GetBlockStats(ctx) + require.Len(t, blockStats.Fills, 1) + + fill := blockStats.Fills[0] + + // Should have empty attributions array since no volume can be attributed + require.Empty(t, fill.AffiliateAttributions, + "Should have no attributions when referee is already at cap") +} + +// TestProcessSingleMatch_AffiliateAttribution_OverCap tests that when a user +// has volume EXCEEDING the 30-day cap, no new volume is attributed. +func TestProcessSingleMatch_AffiliateAttribution_OverCap(t *testing.T) { + cap := uint64(100_000_000_000) // 100k USDC cap + + tApp := testapp.NewTestAppBuilder(t).Build() + ctx := tApp.InitChain() + k := tApp.App.ClobKeeper + + // Create subaccounts + takerSubaccount := constants.Alice_Num0 + makerSubaccount := constants.Bob_Num0 + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &takerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + }) + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &makerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + }) + + // Set up affiliate parameters with cap + err := tApp.App.AffiliatesKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{ + Authority: constants.GovAuthority, + AffiliateParameters: affiliatetypes.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: cap, + }, + }) + require.NoError(t, err) + + // Set up affiliate tiers + err = tApp.App.AffiliatesKeeper.UpdateAffiliateTiers(ctx, affiliatetypes.AffiliateTiers{ + Tiers: []affiliatetypes.AffiliateTiers_Tier{ + { + ReqReferredVolumeQuoteQuantums: 0, + ReqStakedWholeCoins: 0, + TakerFeeSharePpm: 100_000, // 10% + }, + }, + }) + require.NoError(t, err) + + // Register taker with referrer + referrerAddr := constants.CarlAccAddress.String() + err = tApp.App.AffiliatesKeeper.RegisterAffiliate(ctx, constants.Alice_Num0.Owner, referrerAddr) + require.NoError(t, err) + + // Set taker's previous ATTRIBUTED volume to EXCEED the cap + // (User has traded 300k total, but 150k was attributed, which exceeds 100k cap) + tApp.App.StatsKeeper.SetUserStats(ctx, constants.Alice_Num0.Owner, &statstypes.UserStats{ + TakerNotional: 200_000_000_000, // User traded 200k as taker + MakerNotional: 100_000_000_000, // User traded 100k as maker + Affiliate_30DAttributedVolumeQuoteQuantums: 150_000_000_000, // 150k attributed > 100k cap + }) + + // Create a normal trade + takerOrder := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: takerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 100_000_000, // 1 BTC + Subticks: 5_000_000_000, // $50,000 per BTC + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + makerOrder := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: makerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 100_000_000, + Subticks: 5_000_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + matchWithOrders := &clobtypes.MatchWithOrders{ + TakerOrder: &takerOrder, + MakerOrder: &makerOrder, + FillAmount: satypes.BaseQuantums(100_000_000), + } + + // Process the match + success, _, _, _, err := k.ProcessSingleMatch( + ctx, + matchWithOrders, + map[string]bool{}, + affiliatetypes.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: cap, + }, + ) + require.NoError(t, err) + require.True(t, success) + + // Verify NO attribution was made since user exceeds cap + blockStats := tApp.App.StatsKeeper.GetBlockStats(ctx) + require.Len(t, blockStats.Fills, 1) + + fill := blockStats.Fills[0] + + // Should have empty attributions array since user is over cap + require.Empty(t, fill.AffiliateAttributions, + "Should have no attributions when referee exceeds cap") +} + +// TestProcessSingleMatch_AffiliateAttribution_CapWithExpiration tests that when +// old stats expire from the 30-day window, a user who was over the cap can start +// receiving attributions again. +func TestProcessSingleMatch_AffiliateAttribution_CapWithExpiration(t *testing.T) { + cap := uint64(100_000_000_000) // 100k USDC cap + + tApp := testapp.NewTestAppBuilder(t).Build() + ctx := tApp.InitChain() + k := tApp.App.ClobKeeper + + // Create subaccounts + takerSubaccount := constants.Alice_Num0 + makerSubaccount := constants.Bob_Num0 + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &takerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + }) + + tApp.App.SubaccountsKeeper.SetSubaccount(ctx, satypes.Subaccount{ + Id: &makerSubaccount, + AssetPositions: []*satypes.AssetPosition{ + { + AssetId: 0, + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + }) + + // Set up affiliate parameters with cap + err := tApp.App.AffiliatesKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{ + Authority: constants.GovAuthority, + AffiliateParameters: affiliatetypes.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: cap, + }, + }) + require.NoError(t, err) + + // Set up affiliate tiers + err = tApp.App.AffiliatesKeeper.UpdateAffiliateTiers(ctx, affiliatetypes.AffiliateTiers{ + Tiers: []affiliatetypes.AffiliateTiers_Tier{ + { + ReqReferredVolumeQuoteQuantums: 0, + ReqStakedWholeCoins: 0, + TakerFeeSharePpm: 100_000, // 10% + }, + }, + }) + require.NoError(t, err) + + // Register taker with referrer + referrerAddr := constants.CarlAccAddress.String() + err = tApp.App.AffiliatesKeeper.RegisterAffiliate(ctx, constants.Alice_Num0.Owner, referrerAddr) + require.NoError(t, err) + + // SCENARIO 1: User is over cap (150k attributed volume) + tApp.App.StatsKeeper.SetUserStats(ctx, constants.Alice_Num0.Owner, &statstypes.UserStats{ + TakerNotional: 200_000_000_000, // User traded 200k + MakerNotional: 100_000_000_000, // User traded 100k + Affiliate_30DAttributedVolumeQuoteQuantums: 150_000_000_000, // 150k attributed > 100k cap + }) + + // Create first trade + takerOrder1 := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: takerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 100_000_000, // 1 BTC = ~5k USDC + Subticks: 5_000_000_000, // $50,000 per BTC + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + makerOrder1 := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: makerSubaccount, + ClientId: 1, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 100_000_000, + Subticks: 5_000_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + matchWithOrders1 := &clobtypes.MatchWithOrders{ + TakerOrder: &takerOrder1, + MakerOrder: &makerOrder1, + FillAmount: satypes.BaseQuantums(100_000_000), + } + + // Process first match - should have NO attribution + success, _, _, _, err := k.ProcessSingleMatch( + ctx, + matchWithOrders1, + map[string]bool{}, + affiliatetypes.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: cap, + }, + ) + require.NoError(t, err) + require.True(t, success) + + blockStats := tApp.App.StatsKeeper.GetBlockStats(ctx) + require.Len(t, blockStats.Fills, 1) + require.Empty(t, blockStats.Fills[0].AffiliateAttributions, + "First trade: No attribution since user is over cap") + + // SCENARIO 2: Simulate expiration - old attributed volume expires from 30-day window + // Now user only has 80k attributed volume, which is BELOW the 100k cap + tApp.App.StatsKeeper.SetUserStats(ctx, constants.Alice_Num0.Owner, &statstypes.UserStats{ + TakerNotional: 150_000_000_000, // Still trading (down from 200k as old trades expired) + MakerNotional: 70_000_000_000, // Still trading (down from 100k) + Affiliate_30DAttributedVolumeQuoteQuantums: 80_000_000_000, // 80k attributed (down from + // 150k - old attributions expired) + // 80k < 100k cap, so 20k capacity available + }) + + // Clear block stats for second trade + tApp.App.StatsKeeper.SetBlockStats(ctx, &statstypes.BlockStats{}) + + // Create second trade + takerOrder2 := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: takerSubaccount, + ClientId: 2, // Different client ID + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_BUY, + Quantums: 100_000_000, // 1 BTC = ~5k USDC + Subticks: 5_000_000_000, // $50,000 per BTC + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + makerOrder2 := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: makerSubaccount, + ClientId: 2, + OrderFlags: clobtypes.OrderIdFlags_ShortTerm, + ClobPairId: 0, + }, + Side: clobtypes.Order_SIDE_SELL, + Quantums: 100_000_000, + Subticks: 5_000_000_000, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 100}, + } + + matchWithOrders2 := &clobtypes.MatchWithOrders{ + TakerOrder: &takerOrder2, + MakerOrder: &makerOrder2, + FillAmount: satypes.BaseQuantums(100_000_000), + } + + // Process second match - should NOW have attribution since user is below cap + success, _, _, _, err = k.ProcessSingleMatch( + ctx, + matchWithOrders2, + map[string]bool{}, + affiliatetypes.AffiliateParameters{ + Maximum_30DAttributableVolumePerReferredUserQuoteQuantums: cap, + }, + ) + require.NoError(t, err) + require.True(t, success) + + blockStats = tApp.App.StatsKeeper.GetBlockStats(ctx) + require.Len(t, blockStats.Fills, 1) + + fill2 := blockStats.Fills[0] + require.Len(t, fill2.AffiliateAttributions, 1, + "Second trade: Should have attribution after old stats expired") + + attribution := fill2.AffiliateAttributions[0] + require.Equal(t, statstypes.AffiliateAttribution_ROLE_TAKER, attribution.Role) + require.Equal(t, referrerAddr, attribution.ReferrerAddress) +} diff --git a/protocol/x/clob/types/expected_keepers.go b/protocol/x/clob/types/expected_keepers.go index cd7bcef9b4..84bfd08206 100644 --- a/protocol/x/clob/types/expected_keepers.go +++ b/protocol/x/clob/types/expected_keepers.go @@ -183,8 +183,14 @@ type PricesKeeper interface { } type StatsKeeper interface { - RecordFill(ctx sdk.Context, takerAddress string, makerAddress string, - notional *big.Int, affiliateFeeGenerated *big.Int) + RecordFill( + ctx sdk.Context, + takerAddress string, + makerAddress string, + notional *big.Int, + affiliateFeeGenerated *big.Int, + affiliateAttributions []*stattypes.AffiliateAttribution, + ) GetUserStats(ctx sdk.Context, address string) *stattypes.UserStats } @@ -224,6 +230,7 @@ type AffiliatesKeeper interface { GetAffiliateWhitelistMap(ctx sdk.Context) (map[string]uint32, error) GetAffiliateOverridesMap(ctx sdk.Context) (map[string]bool, error) GetAffiliateParameters(ctx sdk.Context) (affiliatetypes.AffiliateParameters, error) + GetReferredBy(ctx sdk.Context, referee string) (referrer string, found bool) } type AccountPlusKeeper interface { diff --git a/protocol/x/revshare/keeper/revshare.go b/protocol/x/revshare/keeper/revshare.go index 5b1ef5b191..4fba70e74a 100644 --- a/protocol/x/revshare/keeper/revshare.go +++ b/protocol/x/revshare/keeper/revshare.go @@ -327,6 +327,20 @@ func (k Keeper) getAffiliateRevShares( return nil, big.NewInt(0), nil } feesShared := lib.BigMulPpm(takerFee, lib.BigU(feeSharePpm), false) + + // Cap the affiliate revenue share if it exceeds the maximum 30d affiliate revenue per referred user + if userStats != nil { + cap := affiliateParams.Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums + if cap != 0 { + revenueGenerated := userStats.Affiliate_30DRevenueGeneratedQuantums + // We know revenueGenerated < cap here because of the check above + maxFee := new(big.Int).SetUint64(cap - revenueGenerated) + if feesShared.Cmp(maxFee) > 0 { + feesShared = maxFee + } + } + } + return []types.RevShare{ { Recipient: takerAffiliateAddr, diff --git a/protocol/x/revshare/keeper/revshare_test.go b/protocol/x/revshare/keeper/revshare_test.go index 0ff9e43280..43f2472d6b 100644 --- a/protocol/x/revshare/keeper/revshare_test.go +++ b/protocol/x/revshare/keeper/revshare_test.go @@ -421,6 +421,66 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { require.NoError(t, err) }, }, + { + name: "Affiliates has about to go over the edge", + expectedRevSharesForFill: types.RevSharesForFill{ + AllRevShares: []types.RevShare{ + { + Recipient: constants.BobAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_TAKER_FEE, + RevShareType: types.REV_SHARE_TYPE_AFFILIATE, + QuoteQuantums: big.NewInt(50_000), + RevSharePpm: 50_000, + }, + }, + AffiliateRevShare: &types.RevShare{ + Recipient: constants.BobAccAddress.String(), + RevShareFeeSource: types.REV_SHARE_FEE_SOURCE_TAKER_FEE, + RevShareType: types.REV_SHARE_TYPE_AFFILIATE, + QuoteQuantums: big.NewInt(50_000), + RevSharePpm: 50_000, + }, + FeeSourceToQuoteQuantums: map[types.RevShareFeeSource]*big.Int{ + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: big.NewInt(50_000), // affiliate rev share fees + // unconditional + market mapper rev shares fees + types.REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE: big.NewInt(0), + types.REV_SHARE_FEE_SOURCE_MAKER_FEE: big.NewInt(0), + }, + FeeSourceToRevSharePpm: map[types.RevShareFeeSource]uint32{ + types.REV_SHARE_FEE_SOURCE_TAKER_FEE: 50_000, // affiliate rev share fee ppm + types.REV_SHARE_FEE_SOURCE_NET_PROTOCOL_REVENUE: 0, // 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) { + require.NoError(t, affiliatesKeeper.UpdateAffiliateParameters(ctx, &affiliatetypes.MsgUpdateAffiliateParameters{ + AffiliateParameters: affiliatetypes.AffiliateParameters{ + Maximum_30DAffiliateRevenuePerReferredUserQuoteQuantums: 1_000_000, + }, + })) + + statsKeeper.SetUserStats(ctx, constants.AliceAccAddress.String(), &statstypes.UserStats{ + Affiliate_30DRevenueGeneratedQuantums: 950_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{ @@ -1376,16 +1436,16 @@ func TestKeeper_GetAllRevShares_Valid(t *testing.T) { keeper := tApp.App.RevShareKeeper affiliatesKeeper := tApp.App.AffiliatesKeeper statsKeeper := tApp.App.StatsKeeper - if tc.setup != nil { - 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, }, })) + if tc.setup != nil { + tc.setup(tApp, ctx, &keeper, &affiliatesKeeper, &statsKeeper) + } + keeper.CreateNewMarketRevShare(ctx, marketId) affiliateOverridesMap, err := affiliatesKeeper.GetAffiliateOverridesMap(ctx) require.NoError(t, err) diff --git a/protocol/x/stats/keeper/grpc_query_test.go b/protocol/x/stats/keeper/grpc_query_test.go index 3f7852d1da..ba294ac949 100644 --- a/protocol/x/stats/keeper/grpc_query_test.go +++ b/protocol/x/stats/keeper/grpc_query_test.go @@ -130,8 +130,11 @@ func TestUserStats(t *testing.T) { k := tApp.App.StatsKeeper user := "alice" userStats := &types.UserStats{ - TakerNotional: 10, - MakerNotional: 10, + TakerNotional: 10, + MakerNotional: 10, + Affiliate_30DRevenueGeneratedQuantums: 100, + Affiliate_30DReferredVolumeQuoteQuantums: 500, + Affiliate_30DAttributedVolumeQuoteQuantums: 250, } k.SetUserStats(ctx, user, userStats) @@ -149,6 +152,21 @@ func TestUserStats(t *testing.T) { }, err: nil, }, + "Success with attributed volume": { + req: &types.QueryUserStatsRequest{ + User: user, + }, + res: &types.QueryUserStatsResponse{ + Stats: &types.UserStats{ + TakerNotional: 10, + MakerNotional: 10, + Affiliate_30DRevenueGeneratedQuantums: 100, + Affiliate_30DReferredVolumeQuoteQuantums: 500, + Affiliate_30DAttributedVolumeQuoteQuantums: 250, + }, + }, + err: nil, + }, "Nil": { req: nil, res: nil, @@ -162,6 +180,15 @@ func TestUserStats(t *testing.T) { } else { require.NoError(t, err) require.Equal(t, tc.res, res) + // Explicitly verify attributed volume field is present + if tc.res != nil && tc.res.Stats != nil { + require.Equal( + t, + tc.res.Stats.Affiliate_30DAttributedVolumeQuoteQuantums, + res.Stats.Affiliate_30DAttributedVolumeQuoteQuantums, + "Attributed volume should be included in response", + ) + } } }) } diff --git a/protocol/x/stats/keeper/keeper.go b/protocol/x/stats/keeper/keeper.go index a99efb8411..e55c3033cc 100644 --- a/protocol/x/stats/keeper/keeper.go +++ b/protocol/x/stats/keeper/keeper.go @@ -85,6 +85,7 @@ func (k Keeper) RecordFill( makerAddress string, notional *big.Int, affiliateFeeGenerated *big.Int, + affiliateAttributions []*types.AffiliateAttribution, ) { blockStats := k.GetBlockStats(ctx) blockStats.Fills = append( @@ -94,6 +95,7 @@ func (k Keeper) RecordFill( Maker: makerAddress, Notional: notional.Uint64(), AffiliateFeeGeneratedQuantums: affiliateFeeGenerated.Uint64(), + AffiliateAttributions: affiliateAttributions, }, ) k.SetBlockStats(ctx, blockStats) @@ -231,6 +233,53 @@ func (k Keeper) ProcessBlockStats(ctx sdk.Context) { } userStatsMap[fill.Taker].Stats.TakerNotional += fill.Notional userStatsMap[fill.Maker].Stats.MakerNotional += fill.Notional + + // Track affiliate revenue attributions if present (can include both taker and maker) + for _, attribution := range fill.AffiliateAttributions { + if attribution != nil { + referrer := attribution.ReferrerAddress + + // Determine referee address based on role + var referee string + if attribution.Role == types.AffiliateAttribution_ROLE_TAKER { + referee = fill.Taker + } else if attribution.Role == types.AffiliateAttribution_ROLE_MAKER { + referee = fill.Maker + } else { + ctx.Logger().Error("invalid affiliate attribution role. Skipping", "role", attribution.Role) + continue + } + + // Initialize referrer stats if needed + if _, ok := userStatsMap[referrer]; !ok { + userStatsMap[referrer] = &types.EpochStats_UserWithStats{ + User: referrer, + Stats: &types.UserStats{}, + } + } + // Track affiliate referred volume for the referrer in this epoch snapshot + userStatsMap[referrer].Stats.Affiliate_30DReferredVolumeQuoteQuantums += attribution.ReferredVolumeQuoteQuantums + // Track affiliate referred volume for the referrer in UserStats + referrerUserStats := k.GetUserStats(ctx, referrer) + referrerUserStats.Affiliate_30DReferredVolumeQuoteQuantums += attribution.ReferredVolumeQuoteQuantums + k.SetUserStats(ctx, referrer, referrerUserStats) + + // Initialize referee stats if needed + if _, ok := userStatsMap[referee]; !ok { + userStatsMap[referee] = &types.EpochStats_UserWithStats{ + User: referee, + Stats: &types.UserStats{}, + } + } + // Track attributed volume for the referee (the trader whose volume was attributed) + userStatsMap[referee].Stats.Affiliate_30DAttributedVolumeQuoteQuantums += attribution.ReferredVolumeQuoteQuantums + // Track attributed volume for the referee in UserStats + refereeUserStats := k.GetUserStats(ctx, referee) + refereeUserStats.Affiliate_30DAttributedVolumeQuoteQuantums += attribution.ReferredVolumeQuoteQuantums + k.SetUserStats(ctx, referee, refereeUserStats) + } + } + // Track affiliate revenue generated on the taker in this epoch snapshot userStatsMap[fill.Taker].Stats.Affiliate_30DRevenueGeneratedQuantums += fill.AffiliateFeeGeneratedQuantums @@ -283,6 +332,8 @@ func (k Keeper) ExpireOldStats(ctx sdk.Context) { stats.TakerNotional -= removedStats.Stats.TakerNotional stats.MakerNotional -= removedStats.Stats.MakerNotional stats.Affiliate_30DRevenueGeneratedQuantums -= removedStats.Stats.Affiliate_30DRevenueGeneratedQuantums + stats.Affiliate_30DReferredVolumeQuoteQuantums -= removedStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums + stats.Affiliate_30DAttributedVolumeQuoteQuantums -= removedStats.Stats.Affiliate_30DAttributedVolumeQuoteQuantums k.SetUserStats(ctx, removedStats.User, stats) // Just remove TakerNotional to avoid double counting diff --git a/protocol/x/stats/keeper/keeper_test.go b/protocol/x/stats/keeper/keeper_test.go index 3b8b55af94..a5477c9431 100644 --- a/protocol/x/stats/keeper/keeper_test.go +++ b/protocol/x/stats/keeper/keeper_test.go @@ -26,10 +26,11 @@ func TestLogger(t *testing.T) { } type recordFillArgs struct { - taker string - maker string - notional *big.Int - affiliateFee *big.Int + taker string + maker string + notional *big.Int + affiliateFee *big.Int + affiliateAttributions []*types.AffiliateAttribution } func TestRecordFill(t *testing.T) { @@ -45,34 +46,94 @@ func TestRecordFill(t *testing.T) { }, "single fill": { []recordFillArgs{ - {"taker", "maker", new(big.Int).SetUint64(123), big.NewInt(0)}, + { + taker: "taker", + maker: "maker", + notional: new(big.Int).SetUint64(123), + affiliateFee: big.NewInt(0), + affiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "referrer", + ReferredVolumeQuoteQuantums: 123, + }, + }, + }, }, &types.BlockStats{ Fills: []*types.BlockStats_Fill{ { - Taker: "taker", - Maker: "maker", - Notional: 123, + Taker: "taker", + Maker: "maker", + Notional: 123, + AffiliateFeeGeneratedQuantums: 0, + AffiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "referrer", + ReferredVolumeQuoteQuantums: 123, + }, + }, }, }, }, }, "multiple fills": { []recordFillArgs{ - {"alice", "bob", new(big.Int).SetUint64(123), big.NewInt(0)}, - {"bob", "alice", new(big.Int).SetUint64(321), big.NewInt(0)}, + { + taker: "alice", + maker: "bob", + notional: new(big.Int).SetUint64(123), + affiliateFee: big.NewInt(0), + affiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "referrer", + ReferredVolumeQuoteQuantums: 123, + }, + }, + }, + { + taker: "bob", + maker: "alice", + notional: new(big.Int).SetUint64(321), + affiliateFee: big.NewInt(0), + affiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "referrer", + ReferredVolumeQuoteQuantums: 321, + }, + }, + }, }, &types.BlockStats{ Fills: []*types.BlockStats_Fill{ { - Taker: "alice", - Maker: "bob", - Notional: 123, + Taker: "alice", + Maker: "bob", + Notional: 123, + AffiliateFeeGeneratedQuantums: 0, + AffiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "referrer", + ReferredVolumeQuoteQuantums: 123, + }, + }, }, { - Taker: "bob", - Maker: "alice", - Notional: 321, + Taker: "bob", + Maker: "alice", + Notional: 321, + AffiliateFeeGeneratedQuantums: 0, + AffiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "referrer", + ReferredVolumeQuoteQuantums: 321, + }, + }, }, }, }, @@ -86,7 +147,7 @@ func TestRecordFill(t *testing.T) { k := tApp.App.StatsKeeper for _, fill := range tc.args { - k.RecordFill(ctx, fill.taker, fill.maker, fill.notional, fill.affiliateFee) + k.RecordFill(ctx, fill.taker, fill.maker, fill.notional, fill.affiliateFee, fill.affiliateAttributions) } require.Equal(t, tc.expectedBlockStats, k.GetBlockStats(ctx)) }) @@ -192,6 +253,228 @@ func TestProcessBlockStats(t *testing.T) { }, }, }, k.GetEpochStatsOrNil(ctx, 1)) + + // Test affiliate revenue attribution + k.SetBlockStats(ctx, &types.BlockStats{ + Fills: []*types.BlockStats_Fill{ + { + Taker: "taker", + Maker: "maker", + Notional: 100_000_000_000, + AffiliateFeeGeneratedQuantums: 50_000_000, + AffiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "referrer", + ReferredVolumeQuoteQuantums: 100_000_000_000, + }, + }, + }, + }, + }) + k.ProcessBlockStats(ctx) + + // Verify referrer's UserStats has the referred volume + assert.Equal(t, &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 100_000_000_000, + }, k.GetUserStats(ctx, "referrer")) + + // Verify taker has the affiliate fee generated AND attributed volume + assert.Equal(t, &types.UserStats{ + TakerNotional: 100_000_000_000, + Affiliate_30DRevenueGeneratedQuantums: 50_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 100_000_000_000, // Taker's volume was attributed + }, k.GetUserStats(ctx, "taker")) + + // Verify maker stats + assert.Equal(t, &types.UserStats{ + MakerNotional: 100_000_000_000, + }, k.GetUserStats(ctx, "maker")) + + // Verify global stats includes the new fill + assert.Equal(t, &types.GlobalStats{ + NotionalTraded: 100_000_000_025, + }, k.GetGlobalStats(ctx)) + + // Verify referrer is in epoch stats with correct referred volume + epochStats := k.GetEpochStatsOrNil(ctx, 1) + require.NotNil(t, epochStats) + var referrerFound bool + for _, userStats := range epochStats.Stats { + if userStats.User == "referrer" { + referrerFound = true + assert.Equal(t, uint64(100_000_000_000), userStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums) + break + } + } + require.True(t, referrerFound, "referrer should be in epoch stats") + + // Test multiple fills with same referrer - referred volume should accumulate + k.SetBlockStats(ctx, &types.BlockStats{ + Fills: []*types.BlockStats_Fill{ + { + Taker: "taker2", + Maker: "maker2", + Notional: 50_000_000_000, + AffiliateFeeGeneratedQuantums: 25_000_000, + AffiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "referrer", + ReferredVolumeQuoteQuantums: 50_000_000_000, + }, + }, + }, + }, + }) + k.ProcessBlockStats(ctx) + + // Verify referrer's referred volume accumulated + assert.Equal(t, &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 150_000_000_000, + }, k.GetUserStats(ctx, "referrer")) + + // Verify referrer's epoch stats accumulated + epochStats = k.GetEpochStatsOrNil(ctx, 1) + require.NotNil(t, epochStats) + referrerFound = false + for _, userStats := range epochStats.Stats { + if userStats.User == "referrer" { + referrerFound = true + assert.Equal(t, uint64(150_000_000_000), userStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums) + break + } + } + require.True(t, referrerFound, "referrer should be in epoch stats") + + // Test fill with capped attributable volume + k.SetBlockStats(ctx, &types.BlockStats{ + Fills: []*types.BlockStats_Fill{ + { + Taker: "taker3", + Maker: "maker3", + Notional: 100_000_000_000, + AffiliateFeeGeneratedQuantums: 50_000_000, + AffiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "referrer2", + ReferredVolumeQuoteQuantums: 30_000_000_000, + }, + }, + }, + }, + }) + k.ProcessBlockStats(ctx) + + // Verify referrer2's referred volume reflects the capped amount + assert.Equal(t, &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 30_000_000_000, + }, k.GetUserStats(ctx, "referrer2")) + + // Verify referrer2's epoch stats has the capped amount + epochStats = k.GetEpochStatsOrNil(ctx, 1) + require.NotNil(t, epochStats) + var referrer2Found bool + for _, userStats := range epochStats.Stats { + if userStats.User == "referrer2" { + referrer2Found = true + assert.Equal(t, uint64(30_000_000_000), userStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums) + break + } + } + require.True(t, referrer2Found, "referrer2 should be in epoch stats") + + // Test fill without affiliate revenue attribution - should not affect referrer stats + k.SetBlockStats(ctx, &types.BlockStats{ + Fills: []*types.BlockStats_Fill{ + { + Taker: "taker4", + Maker: "maker4", + Notional: 50_000_000_000, + AffiliateFeeGeneratedQuantums: 0, + AffiliateAttributions: nil, + }, + }, + }) + k.ProcessBlockStats(ctx) + + // Verify referrer stats unchanged + assert.Equal(t, &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 150_000_000_000, + }, k.GetUserStats(ctx, "referrer")) + + // Verify referrer2 stats unchanged + assert.Equal(t, &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 30_000_000_000, + }, k.GetUserStats(ctx, "referrer2")) + + // Test fill where both taker AND maker have affiliate attributions + k.SetBlockStats(ctx, &types.BlockStats{ + Fills: []*types.BlockStats_Fill{ + { + Taker: "taker5", + Maker: "maker5", + Notional: 80_000_000_000, + AffiliateFeeGeneratedQuantums: 40_000_000, + AffiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "referrer_for_taker", + ReferredVolumeQuoteQuantums: 80_000_000_000, + }, + { + Role: types.AffiliateAttribution_ROLE_MAKER, + ReferrerAddress: "referrer_for_maker", + ReferredVolumeQuoteQuantums: 80_000_000_000, + }, + }, + }, + }, + }) + k.ProcessBlockStats(ctx) + + // Verify taker's referrer received the attributed volume + assert.Equal(t, &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 80_000_000_000, + }, k.GetUserStats(ctx, "referrer_for_taker")) + + // Verify maker's referrer also received the attributed volume + assert.Equal(t, &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 80_000_000_000, + }, k.GetUserStats(ctx, "referrer_for_maker")) + + // Verify both referrers are in epoch stats + epochStats = k.GetEpochStatsOrNil(ctx, 1) + require.NotNil(t, epochStats) + + var takerReferrerFound, makerReferrerFound bool + for _, userStats := range epochStats.Stats { + if userStats.User == "referrer_for_taker" { + takerReferrerFound = true + assert.Equal(t, uint64(80_000_000_000), userStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums) + } + if userStats.User == "referrer_for_maker" { + makerReferrerFound = true + assert.Equal(t, uint64(80_000_000_000), userStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums) + } + } + require.True(t, takerReferrerFound, "taker's referrer should be in epoch stats") + require.True(t, makerReferrerFound, "maker's referrer should be in epoch stats") + + // Verify taker5 and maker5 stats (they're different addresses) + taker5Stats := k.GetUserStats(ctx, "taker5") + assert.Equal(t, &types.UserStats{ + TakerNotional: 80_000_000_000, + Affiliate_30DRevenueGeneratedQuantums: 40_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 80_000_000_000, // Taker's volume attributed + }, taker5Stats) + + maker5Stats := k.GetUserStats(ctx, "maker5") + assert.Equal(t, &types.UserStats{ + MakerNotional: 80_000_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 80_000_000_000, // Maker's volume attributed + }, maker5Stats) } func TestExpireOldStats(t *testing.T) { @@ -223,27 +506,39 @@ func TestExpireOldStats(t *testing.T) { { User: "alice", Stats: &types.UserStats{ - TakerNotional: 1, - MakerNotional: 2, + TakerNotional: 1, + MakerNotional: 2, + Affiliate_30DReferredVolumeQuoteQuantums: 10_000_000_000, + Affiliate_30DRevenueGeneratedQuantums: 100_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 5_000_000_000, // 5k attributed per epoch }, }, { User: "bob", Stats: &types.UserStats{ - TakerNotional: 2, - MakerNotional: 1, + TakerNotional: 2, + MakerNotional: 1, + Affiliate_30DReferredVolumeQuoteQuantums: 10_000_000_000, + Affiliate_30DRevenueGeneratedQuantums: 100_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 8_000_000_000, // 8k attributed per epoch }, }, }, }) } k.SetUserStats(ctx, "alice", &types.UserStats{ - TakerNotional: 30, - MakerNotional: 60, + TakerNotional: 30, + MakerNotional: 60, + Affiliate_30DReferredVolumeQuoteQuantums: 300_000_000_000, + Affiliate_30DRevenueGeneratedQuantums: 3_000_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 150_000_000_000, // 30 epochs * 5k per epoch }) k.SetUserStats(ctx, "bob", &types.UserStats{ - TakerNotional: 60, - MakerNotional: 30, + TakerNotional: 60, + MakerNotional: 30, + Affiliate_30DReferredVolumeQuoteQuantums: 300_000_000_000, + Affiliate_30DRevenueGeneratedQuantums: 3_000_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 240_000_000_000, // 30 epochs * 8k per epoch }) k.SetGlobalStats(ctx, &types.GlobalStats{ NotionalTraded: 90, @@ -260,12 +555,20 @@ func TestExpireOldStats(t *testing.T) { k.ExpireOldStats(ctx) require.Equal(t, &types.UserStats{ - TakerNotional: 30 - uint64(i+1), - MakerNotional: 60 - 2*uint64(i+1), + TakerNotional: 30 - uint64(i+1), + MakerNotional: 60 - 2*uint64(i+1), + Affiliate_30DReferredVolumeQuoteQuantums: 300_000_000_000 - (uint64(i+1) * 10_000_000_000), + Affiliate_30DRevenueGeneratedQuantums: 3_000_000_000 - (uint64(i+1) * 100_000_000), + Affiliate_30DAttributedVolumeQuoteQuantums: 150_000_000_000 - (uint64(i+1) * 5_000_000_000), + // Decreases by 5k per expired epoch }, k.GetUserStats(ctx, "alice")) require.Equal(t, &types.UserStats{ - TakerNotional: 60 - 2*uint64(i+1), - MakerNotional: 30 - uint64(i+1), + TakerNotional: 60 - 2*uint64(i+1), + MakerNotional: 30 - uint64(i+1), + Affiliate_30DReferredVolumeQuoteQuantums: 300_000_000_000 - (uint64(i+1) * 10_000_000_000), + Affiliate_30DRevenueGeneratedQuantums: 3_000_000_000 - (uint64(i+1) * 100_000_000), + Affiliate_30DAttributedVolumeQuoteQuantums: 240_000_000_000 - (uint64(i+1) * 8_000_000_000), + // Decreases by 8k per expired epoch }, k.GetUserStats(ctx, "bob")) require.Equal(t, &types.GlobalStats{ NotionalTraded: 90 - 3*uint64(i+1), @@ -278,12 +581,18 @@ func TestExpireOldStats(t *testing.T) { // Unchanged after pruning nil epoch require.Equal(t, &types.UserStats{ - TakerNotional: 30 - uint64(i+1), - MakerNotional: 60 - 2*uint64(i+1), + TakerNotional: 30 - uint64(i+1), + MakerNotional: 60 - 2*uint64(i+1), + Affiliate_30DReferredVolumeQuoteQuantums: 300_000_000_000 - (uint64(i+1) * 10_000_000_000), + Affiliate_30DRevenueGeneratedQuantums: 3_000_000_000 - (uint64(i+1) * 100_000_000), + Affiliate_30DAttributedVolumeQuoteQuantums: 150_000_000_000 - (uint64(i+1) * 5_000_000_000), }, k.GetUserStats(ctx, "alice")) require.Equal(t, &types.UserStats{ - TakerNotional: 60 - 2*uint64(i+1), - MakerNotional: 30 - uint64(i+1), + TakerNotional: 60 - 2*uint64(i+1), + MakerNotional: 30 - uint64(i+1), + Affiliate_30DReferredVolumeQuoteQuantums: 300_000_000_000 - (uint64(i+1) * 10_000_000_000), + Affiliate_30DRevenueGeneratedQuantums: 3_000_000_000 - (uint64(i+1) * 100_000_000), + Affiliate_30DAttributedVolumeQuoteQuantums: 240_000_000_000 - (uint64(i+1) * 8_000_000_000), }, k.GetUserStats(ctx, "bob")) require.Equal(t, &types.GlobalStats{ NotionalTraded: 90 - 3*uint64(i+1), @@ -297,6 +606,266 @@ func TestExpireOldStats(t *testing.T) { require.NotNil(t, k.GetEpochStatsOrNil(ctx, uint32(12))) } +// TestAffiliateAttribution_ConsistentlyHighVolumeTrader tests the scenario where a user +// is consistently trading at high volume and hitting the attribution cap. +// This test proves there is NO equilibrium trap - the user can continue getting +// attribution as old stats expire, even while trading continuously. +func TestAffiliateAttribution_ConsistentlyHighVolumeTrader(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + + // Epochs start at block height 2 + ctx := tApp.AdvanceToBlock(2, testapp.AdvanceToBlockOptions{ + BlockTime: time.Unix(int64(1), 0).UTC(), + }) + + // Advance time so first 5 epochs will be ready to expire + windowDuration := tApp.App.StatsKeeper.GetWindowDuration(ctx) + tApp.AdvanceToBlock(3, testapp.AdvanceToBlockOptions{ + BlockTime: time.Unix(0, 0). + Add(windowDuration). + Add((time.Duration(5*epochstypes.StatsEpochDuration) + 1) * time.Second). + UTC(), + }) + + // Now advance to a stable block and set up stats + ctx = tApp.AdvanceToBlock(100, testapp.AdvanceToBlockOptions{}) + k := tApp.App.StatsKeeper + + // Scenario: User is a consistent high-volume trader + // - Cap is 100k + // - User has been trading 200k per epoch for 30 epochs + // - User has attributed 100k total (at cap) + // - Old epochs will expire, allowing new attribution + + // Create 30 epochs of history where the user traded 200k each epoch + // but only 100k total was attributed (due to cap being reached early) + for i := 0; i < 30; i++ { + var attributedThisEpoch uint64 + if i < 5 { + // First 5 epochs: 20k attributed per epoch = 100k total (reaches cap) + attributedThisEpoch = 20_000_000_000 + } else { + // Epochs 6-30: 0 attributed (already at cap) + attributedThisEpoch = 0 + } + + k.SetEpochStats(ctx, uint32(i*2), &types.EpochStats{ + EpochEndTime: time.Unix(0, 0). + Add(time.Duration(i*int(epochstypes.StatsEpochDuration)) * time.Second). + UTC(), + Stats: []*types.EpochStats_UserWithStats{ + { + User: "highVolumeTrader", + Stats: &types.UserStats{ + TakerNotional: 200_000_000_000, // 200k traded + MakerNotional: 0, + Affiliate_30DReferredVolumeQuoteQuantums: 0, + Affiliate_30DRevenueGeneratedQuantums: 0, + Affiliate_30DAttributedVolumeQuoteQuantums: attributedThisEpoch, // Only first + // 5 epochs have attribution + }, + }, + { + User: "someAffiliate", + Stats: &types.UserStats{ + TakerNotional: 0, + MakerNotional: 0, + Affiliate_30DReferredVolumeQuoteQuantums: attributedThisEpoch, // Same as attributed + // (this is what the affiliate refers) + Affiliate_30DRevenueGeneratedQuantums: 0, + }, + }, + }, + }) + } + + // User's current stats: 6000k traded (30 epochs × 200k), but only 100k attributed + k.SetUserStats(ctx, "highVolumeTrader", &types.UserStats{ + TakerNotional: 6000_000_000_000, // 6M total volume + MakerNotional: 0, + Affiliate_30DReferredVolumeQuoteQuantums: 0, + Affiliate_30DRevenueGeneratedQuantums: 0, + Affiliate_30DAttributedVolumeQuoteQuantums: 100_000_000_000, // At cap (100k) + }) + + k.SetStatsMetadata(ctx, &types.StatsMetadata{ + TrailingEpoch: 0, + }) + + // Set up affiliate's initial referred volume (also at cap from same history) + k.SetUserStats(ctx, "someAffiliate", &types.UserStats{ + TakerNotional: 0, + MakerNotional: 0, + Affiliate_30DReferredVolumeQuoteQuantums: 100_000_000_000, // Also at 100k + Affiliate_30DRevenueGeneratedQuantums: 0, + }) + + // BEFORE expiration: User is at cap (100k) + userStats := k.GetUserStats(ctx, "highVolumeTrader") + require.Equal(t, uint64(100_000_000_000), userStats.Affiliate_30DAttributedVolumeQuoteQuantums, + "User should start at cap") + + affiliateStats := k.GetUserStats(ctx, "someAffiliate") + require.Equal(t, uint64(100_000_000_000), affiliateStats.Affiliate_30DReferredVolumeQuoteQuantums, + "Affiliate should start with 100k referred volume") + + // NOW INTERWEAVE: Expire old stats while processing new blocks with attribution + // This simulates the realistic scenario where a high-volume trader continues trading + // as old attributed volume expires, keeping their attributed volume at/near the cap + + // Expire first epoch (20k attributed removed) + Process new block (20k attributed added) + k.ExpireOldStats(ctx) // Removes 20k from epoch 0 + k.SetBlockStats(ctx, &types.BlockStats{ + Fills: []*types.BlockStats_Fill{ + { + Taker: "highVolumeTrader", + Maker: "someMaker", + Notional: 20_000_000_000, + AffiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "someAffiliate", + ReferredVolumeQuoteQuantums: 20_000_000_000, + }, + }, + }, + }, + }) + k.ProcessBlockStats(ctx) + + userStats = k.GetUserStats(ctx, "highVolumeTrader") + require.Equal(t, uint64(100_000_000_000), userStats.Affiliate_30DAttributedVolumeQuoteQuantums, + "Attributed volume stays at cap: 100k - 20k (expired) + 20k (new) = 100k") + + affiliateStats = k.GetUserStats(ctx, "someAffiliate") + require.Equal(t, uint64(100_000_000_000), affiliateStats.Affiliate_30DReferredVolumeQuoteQuantums, + "Affiliate referred volume stays at maximum: 100k") + + // Skip nil epoch + k.ExpireOldStats(ctx) + + // Expire second epoch (20k removed) + Process new block (20k added) + k.ExpireOldStats(ctx) // Removes 20k from epoch 2 + k.SetBlockStats(ctx, &types.BlockStats{ + Fills: []*types.BlockStats_Fill{ + { + Taker: "highVolumeTrader", + Maker: "someMaker", + Notional: 20_000_000_000, + AffiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "someAffiliate", + ReferredVolumeQuoteQuantums: 20_000_000_000, + }, + }, + }, + }, + }) + k.ProcessBlockStats(ctx) + + userStats = k.GetUserStats(ctx, "highVolumeTrader") + require.Equal(t, uint64(100_000_000_000), userStats.Affiliate_30DAttributedVolumeQuoteQuantums, + "Attributed volume stays at cap: still 100k after second rotation") + + affiliateStats = k.GetUserStats(ctx, "someAffiliate") + require.Equal(t, uint64(100_000_000_000), affiliateStats.Affiliate_30DReferredVolumeQuoteQuantums, + "Affiliate referred volume stays at maximum: 100k") + + // Skip nil epoch + k.ExpireOldStats(ctx) + + // Expire third epoch (20k removed) + Process new block (20k added) + k.ExpireOldStats(ctx) + k.SetBlockStats(ctx, &types.BlockStats{ + Fills: []*types.BlockStats_Fill{ + { + Taker: "highVolumeTrader", + Maker: "someMaker", + Notional: 20_000_000_000, + AffiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "someAffiliate", + ReferredVolumeQuoteQuantums: 20_000_000_000, + }, + }, + }, + }, + }) + k.ProcessBlockStats(ctx) + + userStats = k.GetUserStats(ctx, "highVolumeTrader") + require.Equal(t, uint64(100_000_000_000), userStats.Affiliate_30DAttributedVolumeQuoteQuantums, + "Attributed volume stays at cap: still 100k after third rotation") + + affiliateStats = k.GetUserStats(ctx, "someAffiliate") + require.Equal(t, uint64(100_000_000_000), affiliateStats.Affiliate_30DReferredVolumeQuoteQuantums, + "Affiliate referred volume stays at maximum: 100k") + + // Skip nil epoch + k.ExpireOldStats(ctx) + + // Expire fourth epoch (20k removed) + Process new block (20k added) + k.ExpireOldStats(ctx) + k.SetBlockStats(ctx, &types.BlockStats{ + Fills: []*types.BlockStats_Fill{ + { + Taker: "highVolumeTrader", + Maker: "someMaker", + Notional: 20_000_000_000, + AffiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "someAffiliate", + ReferredVolumeQuoteQuantums: 20_000_000_000, + }, + }, + }, + }, + }) + k.ProcessBlockStats(ctx) + + userStats = k.GetUserStats(ctx, "highVolumeTrader") + require.Equal(t, uint64(100_000_000_000), userStats.Affiliate_30DAttributedVolumeQuoteQuantums, + "Attributed volume stays at cap: still 100k after fourth rotation") + + affiliateStats = k.GetUserStats(ctx, "someAffiliate") + require.Equal(t, uint64(100_000_000_000), affiliateStats.Affiliate_30DReferredVolumeQuoteQuantums, + "Affiliate referred volume stays at maximum: 100k") + + // Skip nil epoch + k.ExpireOldStats(ctx) + + // Expire fifth epoch (20k removed) + Process new block (20k added) + k.ExpireOldStats(ctx) + k.SetBlockStats(ctx, &types.BlockStats{ + Fills: []*types.BlockStats_Fill{ + { + Taker: "highVolumeTrader", + Maker: "someMaker", + Notional: 20_000_000_000, + AffiliateAttributions: []*types.AffiliateAttribution{ + { + Role: types.AffiliateAttribution_ROLE_TAKER, + ReferrerAddress: "someAffiliate", + ReferredVolumeQuoteQuantums: 20_000_000_000, + }, + }, + }, + }, + }) + k.ProcessBlockStats(ctx) + + userStats = k.GetUserStats(ctx, "highVolumeTrader") + require.Equal(t, uint64(100_000_000_000), userStats.Affiliate_30DAttributedVolumeQuoteQuantums, + "Attributed volume STAYS AT MAXIMUM: 100k throughout the entire rotation!") + + affiliateStats = k.GetUserStats(ctx, "someAffiliate") + require.Equal(t, uint64(100_000_000_000), affiliateStats.Affiliate_30DReferredVolumeQuoteQuantums, + "Affiliate referred volume STAYS AT MAXIMUM: 100k throughout the entire rotation!") +} + func TestGetStakedBaseTokens(t *testing.T) { testCases := []struct { name string diff --git a/protocol/x/stats/types/stats.pb.go b/protocol/x/stats/types/stats.pb.go index f9fa866ae3..9cde22d8ca 100644 --- a/protocol/x/stats/types/stats.pb.go +++ b/protocol/x/stats/types/stats.pb.go @@ -28,6 +28,99 @@ var _ = time.Kitchen // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// Role indicates whether this attribution is for the taker or maker +type AffiliateAttribution_Role int32 + +const ( + AffiliateAttribution_ROLE_UNSPECIFIED AffiliateAttribution_Role = 0 + AffiliateAttribution_ROLE_TAKER AffiliateAttribution_Role = 1 + AffiliateAttribution_ROLE_MAKER AffiliateAttribution_Role = 2 +) + +var AffiliateAttribution_Role_name = map[int32]string{ + 0: "ROLE_UNSPECIFIED", + 1: "ROLE_TAKER", + 2: "ROLE_MAKER", +} + +var AffiliateAttribution_Role_value = map[string]int32{ + "ROLE_UNSPECIFIED": 0, + "ROLE_TAKER": 1, + "ROLE_MAKER": 2, +} + +func (x AffiliateAttribution_Role) String() string { + return proto.EnumName(AffiliateAttribution_Role_name, int32(x)) +} + +func (AffiliateAttribution_Role) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_07475747e6dcccdc, []int{0, 0} +} + +// AffiliateAttribution represents the affiliate attribution for a fill. +type AffiliateAttribution struct { + // Role of the trader (taker or maker) whose affiliate is being attributed + Role AffiliateAttribution_Role `protobuf:"varint,1,opt,name=role,proto3,enum=dydxprotocol.stats.AffiliateAttribution_Role" json:"role,omitempty"` + // Referrer address (the affiliate receiving the fee) + ReferrerAddress string `protobuf:"bytes,2,opt,name=referrer_address,json=referrerAddress,proto3" json:"referrer_address,omitempty"` + // Referred volume in quote quantums (capped based on 30-day volume limits) + ReferredVolumeQuoteQuantums uint64 `protobuf:"varint,3,opt,name=referred_volume_quote_quantums,json=referredVolumeQuoteQuantums,proto3" json:"referred_volume_quote_quantums,omitempty"` +} + +func (m *AffiliateAttribution) Reset() { *m = AffiliateAttribution{} } +func (m *AffiliateAttribution) String() string { return proto.CompactTextString(m) } +func (*AffiliateAttribution) ProtoMessage() {} +func (*AffiliateAttribution) Descriptor() ([]byte, []int) { + return fileDescriptor_07475747e6dcccdc, []int{0} +} +func (m *AffiliateAttribution) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *AffiliateAttribution) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_AffiliateAttribution.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 *AffiliateAttribution) XXX_Merge(src proto.Message) { + xxx_messageInfo_AffiliateAttribution.Merge(m, src) +} +func (m *AffiliateAttribution) XXX_Size() int { + return m.Size() +} +func (m *AffiliateAttribution) XXX_DiscardUnknown() { + xxx_messageInfo_AffiliateAttribution.DiscardUnknown(m) +} + +var xxx_messageInfo_AffiliateAttribution proto.InternalMessageInfo + +func (m *AffiliateAttribution) GetRole() AffiliateAttribution_Role { + if m != nil { + return m.Role + } + return AffiliateAttribution_ROLE_UNSPECIFIED +} + +func (m *AffiliateAttribution) GetReferrerAddress() string { + if m != nil { + return m.ReferrerAddress + } + return "" +} + +func (m *AffiliateAttribution) GetReferredVolumeQuoteQuantums() uint64 { + if m != nil { + return m.ReferredVolumeQuoteQuantums + } + return 0 +} + // BlockStats is used to store stats transiently within the scope of a block. type BlockStats struct { // The fills that occured on this block. @@ -38,7 +131,7 @@ func (m *BlockStats) Reset() { *m = BlockStats{} } func (m *BlockStats) String() string { return proto.CompactTextString(m) } func (*BlockStats) ProtoMessage() {} func (*BlockStats) Descriptor() ([]byte, []int) { - return fileDescriptor_07475747e6dcccdc, []int{0} + return fileDescriptor_07475747e6dcccdc, []int{1} } func (m *BlockStats) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -87,13 +180,16 @@ type BlockStats_Fill struct { // Used to calculate affiliate revenue attributed for taker. This is dynamic // per affiliate tier AffiliateFeeGeneratedQuantums uint64 `protobuf:"varint,4,opt,name=affiliate_fee_generated_quantums,json=affiliateFeeGeneratedQuantums,proto3" json:"affiliate_fee_generated_quantums,omitempty"` + // Affiliate revenue attributions for this fill (can include both taker and + // maker) + AffiliateAttributions []*AffiliateAttribution `protobuf:"bytes,5,rep,name=affiliate_attributions,json=affiliateAttributions,proto3" json:"affiliate_attributions,omitempty"` } func (m *BlockStats_Fill) Reset() { *m = BlockStats_Fill{} } func (m *BlockStats_Fill) String() string { return proto.CompactTextString(m) } func (*BlockStats_Fill) ProtoMessage() {} func (*BlockStats_Fill) Descriptor() ([]byte, []int) { - return fileDescriptor_07475747e6dcccdc, []int{0, 0} + return fileDescriptor_07475747e6dcccdc, []int{1, 0} } func (m *BlockStats_Fill) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -150,6 +246,13 @@ func (m *BlockStats_Fill) GetAffiliateFeeGeneratedQuantums() uint64 { return 0 } +func (m *BlockStats_Fill) GetAffiliateAttributions() []*AffiliateAttribution { + if m != nil { + return m.AffiliateAttributions + } + return nil +} + // StatsMetadata stores metadata for the x/stats module type StatsMetadata struct { // The oldest epoch that is included in the stats. The next epoch to be @@ -161,7 +264,7 @@ func (m *StatsMetadata) Reset() { *m = StatsMetadata{} } func (m *StatsMetadata) String() string { return proto.CompactTextString(m) } func (*StatsMetadata) ProtoMessage() {} func (*StatsMetadata) Descriptor() ([]byte, []int) { - return fileDescriptor_07475747e6dcccdc, []int{1} + return fileDescriptor_07475747e6dcccdc, []int{2} } func (m *StatsMetadata) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -209,7 +312,7 @@ func (m *EpochStats) Reset() { *m = EpochStats{} } func (m *EpochStats) String() string { return proto.CompactTextString(m) } func (*EpochStats) ProtoMessage() {} func (*EpochStats) Descriptor() ([]byte, []int) { - return fileDescriptor_07475747e6dcccdc, []int{2} + return fileDescriptor_07475747e6dcccdc, []int{3} } func (m *EpochStats) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -262,7 +365,7 @@ func (m *EpochStats_UserWithStats) Reset() { *m = EpochStats_UserWithSta func (m *EpochStats_UserWithStats) String() string { return proto.CompactTextString(m) } func (*EpochStats_UserWithStats) ProtoMessage() {} func (*EpochStats_UserWithStats) Descriptor() ([]byte, []int) { - return fileDescriptor_07475747e6dcccdc, []int{2, 0} + return fileDescriptor_07475747e6dcccdc, []int{3, 0} } func (m *EpochStats_UserWithStats) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -315,7 +418,7 @@ func (m *GlobalStats) Reset() { *m = GlobalStats{} } func (m *GlobalStats) String() string { return proto.CompactTextString(m) } func (*GlobalStats) ProtoMessage() {} func (*GlobalStats) Descriptor() ([]byte, []int) { - return fileDescriptor_07475747e6dcccdc, []int{3} + return fileDescriptor_07475747e6dcccdc, []int{4} } func (m *GlobalStats) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -362,13 +465,16 @@ type UserStats struct { 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"` + // Attributed volume in quote quantums - volume from this user (as referee) + // that has been attributed to their affiliate in the last 30 days + Affiliate_30DAttributedVolumeQuoteQuantums uint64 `protobuf:"varint,5,opt,name=affiliate_30d_attributed_volume_quote_quantums,json=affiliate30dAttributedVolumeQuoteQuantums,proto3" json:"affiliate_30d_attributed_volume_quote_quantums,omitempty"` } func (m *UserStats) Reset() { *m = UserStats{} } func (m *UserStats) String() string { return proto.CompactTextString(m) } func (*UserStats) ProtoMessage() {} func (*UserStats) Descriptor() ([]byte, []int) { - return fileDescriptor_07475747e6dcccdc, []int{4} + return fileDescriptor_07475747e6dcccdc, []int{5} } func (m *UserStats) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -425,6 +531,13 @@ func (m *UserStats) GetAffiliate_30DReferredVolumeQuoteQuantums() uint64 { return 0 } +func (m *UserStats) GetAffiliate_30DAttributedVolumeQuoteQuantums() uint64 { + if m != nil { + return m.Affiliate_30DAttributedVolumeQuoteQuantums + } + return 0 +} + // CachedStakedBaseTokens stores the last calculated total staked base tokens type CachedStakedBaseTokens struct { // Last calculated total staked base tokens by the delegator. @@ -438,7 +551,7 @@ func (m *CachedStakedBaseTokens) Reset() { *m = CachedStakedBaseTokens{} func (m *CachedStakedBaseTokens) String() string { return proto.CompactTextString(m) } func (*CachedStakedBaseTokens) ProtoMessage() {} func (*CachedStakedBaseTokens) Descriptor() ([]byte, []int) { - return fileDescriptor_07475747e6dcccdc, []int{5} + return fileDescriptor_07475747e6dcccdc, []int{6} } func (m *CachedStakedBaseTokens) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -475,6 +588,8 @@ func (m *CachedStakedBaseTokens) GetCachedAt() int64 { } func init() { + proto.RegisterEnum("dydxprotocol.stats.AffiliateAttribution_Role", AffiliateAttribution_Role_name, AffiliateAttribution_Role_value) + proto.RegisterType((*AffiliateAttribution)(nil), "dydxprotocol.stats.AffiliateAttribution") proto.RegisterType((*BlockStats)(nil), "dydxprotocol.stats.BlockStats") proto.RegisterType((*BlockStats_Fill)(nil), "dydxprotocol.stats.BlockStats.Fill") proto.RegisterType((*StatsMetadata)(nil), "dydxprotocol.stats.StatsMetadata") @@ -488,49 +603,99 @@ func init() { func init() { proto.RegisterFile("dydxprotocol/stats/stats.proto", fileDescriptor_07475747e6dcccdc) } var fileDescriptor_07475747e6dcccdc = []byte{ - // 659 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x94, 0xcf, 0x4e, 0xdb, 0x4a, - 0x14, 0xc6, 0xe3, 0x10, 0xae, 0x60, 0x20, 0xb9, 0x57, 0x23, 0x74, 0x15, 0xe5, 0x8a, 0x24, 0xca, - 0x2d, 0x22, 0x0b, 0xea, 0x20, 0x52, 0x51, 0x75, 0xd7, 0xa6, 0x02, 0xda, 0x4a, 0xad, 0x84, 0xa1, - 0xb4, 0xaa, 0x54, 0x8d, 0x26, 0x9e, 0x13, 0xc7, 0x62, 0xec, 0x09, 0xf6, 0x38, 0x82, 0x3e, 0x05, - 0xbb, 0x3e, 0x45, 0xdf, 0x83, 0x25, 0xcb, 0xaa, 0x0b, 0xda, 0xc2, 0x3b, 0x74, 0x5d, 0xf9, 0x0c, - 0x76, 0x08, 0xb0, 0xe8, 0x26, 0x9a, 0xf3, 0x9b, 0xef, 0xfc, 0xc9, 0x37, 0x47, 0x26, 0x75, 0x71, - 0x22, 0x8e, 0x47, 0x91, 0xd2, 0xca, 0x55, 0xb2, 0x13, 0x6b, 0xae, 0x63, 0xf3, 0x6b, 0x23, 0xa4, - 0xf4, 0xe6, 0xbd, 0x8d, 0x37, 0xb5, 0x25, 0x4f, 0x79, 0x0a, 0x59, 0x27, 0x3d, 0x19, 0x65, 0xad, - 0xe1, 0x29, 0xe5, 0x49, 0xe8, 0x60, 0xd4, 0x4f, 0x06, 0x1d, 0xed, 0x07, 0x10, 0x6b, 0x1e, 0x8c, - 0x8c, 0xa0, 0xf5, 0xd3, 0x22, 0xa4, 0x27, 0x95, 0x7b, 0xb8, 0x97, 0x56, 0xa1, 0x4f, 0xc8, 0xec, - 0xc0, 0x97, 0x32, 0xae, 0x5a, 0xcd, 0x99, 0xf6, 0xc2, 0xc6, 0xff, 0xf6, 0xdd, 0x4e, 0xf6, 0x44, - 0x6e, 0x6f, 0xfb, 0x52, 0x3a, 0x26, 0xa3, 0xf6, 0xd9, 0x22, 0xa5, 0x34, 0xa6, 0x4b, 0x64, 0x56, - 0xf3, 0x43, 0x88, 0xaa, 0x56, 0xd3, 0x6a, 0xcf, 0x3b, 0x26, 0x48, 0x69, 0x80, 0xb4, 0x68, 0x28, - 0x06, 0xb4, 0x46, 0xe6, 0x42, 0xa5, 0x7d, 0x15, 0x72, 0x59, 0x9d, 0x69, 0x5a, 0xed, 0x92, 0x93, - 0xc7, 0x74, 0x87, 0x34, 0xf9, 0x60, 0xe0, 0x4b, 0x9f, 0x6b, 0x60, 0x03, 0x00, 0xe6, 0x41, 0x08, - 0x11, 0xd7, 0x20, 0xd8, 0x51, 0xc2, 0x43, 0x9d, 0x04, 0x71, 0xb5, 0x84, 0x39, 0xcb, 0xb9, 0x6e, - 0x1b, 0x60, 0x27, 0x53, 0xed, 0x5e, 0x8b, 0x5a, 0x9b, 0xa4, 0x8c, 0xe3, 0xbe, 0x06, 0xcd, 0x05, - 0xd7, 0x9c, 0xae, 0x90, 0x8a, 0x8e, 0xb8, 0x2f, 0xfd, 0xd0, 0x63, 0x30, 0x52, 0xee, 0x10, 0x47, - 0x2d, 0x3b, 0xe5, 0x8c, 0x6e, 0xa5, 0xb0, 0xf5, 0xcb, 0x22, 0x04, 0x4f, 0xc6, 0x9b, 0x57, 0xa4, - 0x82, 0x62, 0x06, 0xa1, 0x60, 0xa9, 0x8f, 0x98, 0xb5, 0xb0, 0x51, 0xb3, 0x8d, 0xc9, 0x76, 0x66, - 0xb2, 0xbd, 0x9f, 0x99, 0xdc, 0x9b, 0x3b, 0xbb, 0x68, 0x14, 0x4e, 0xbf, 0x37, 0x2c, 0x67, 0x11, - 0x73, 0xb7, 0x42, 0x91, 0x5e, 0xd2, 0x1e, 0x99, 0x45, 0x33, 0xab, 0x45, 0xf4, 0x79, 0xed, 0x3e, - 0x9f, 0x27, 0xad, 0xed, 0xb7, 0x31, 0x44, 0xef, 0x7c, 0x6d, 0x22, 0xc7, 0xa4, 0xd6, 0xde, 0x93, - 0xf2, 0x14, 0xa7, 0x94, 0x94, 0x92, 0x38, 0xf7, 0x1d, 0xcf, 0xb4, 0x3b, 0x69, 0x94, 0xce, 0xba, - 0x7c, 0x5f, 0xa3, 0xb4, 0xca, 0xcd, 0xca, 0xad, 0x4d, 0xb2, 0xb0, 0x23, 0x55, 0x9f, 0x4b, 0x53, - 0x77, 0x95, 0xfc, 0x9d, 0x3d, 0x0a, 0xd3, 0x11, 0x17, 0x20, 0xb0, 0x45, 0xc9, 0xa9, 0x64, 0x78, - 0x1f, 0x69, 0xeb, 0xb4, 0x48, 0xe6, 0xf3, 0x62, 0xe8, 0x72, 0xfa, 0xc8, 0x2c, 0x7f, 0x61, 0x93, - 0x55, 0x46, 0xfa, 0x26, 0x7b, 0xe6, 0x15, 0x52, 0x09, 0xa6, 0x65, 0x45, 0x23, 0x0b, 0xa6, 0x64, - 0x07, 0xa4, 0x3d, 0xd9, 0x86, 0xee, 0xba, 0x60, 0x11, 0x8c, 0x21, 0x4c, 0xee, 0xdd, 0x0a, 0xb3, - 0x49, 0x0f, 0x72, 0x7d, 0x77, 0x5d, 0x38, 0x46, 0x7d, 0x67, 0x39, 0xe8, 0x47, 0xb2, 0x76, 0xbb, - 0xee, 0x00, 0xa2, 0x08, 0x04, 0x1b, 0x2b, 0x99, 0x04, 0xc0, 0x8e, 0x12, 0xa5, 0xe1, 0xf6, 0xc6, - 0xad, 0x4e, 0xd7, 0x36, 0x19, 0x07, 0x98, 0xb0, 0x9b, 0xea, 0xf3, 0xdd, 0xfb, 0x62, 0x91, 0x7f, - 0x9f, 0x73, 0x77, 0x08, 0x62, 0x2f, 0xfd, 0xdb, 0xa2, 0xc7, 0x63, 0xd8, 0x57, 0x87, 0x10, 0xc6, - 0x74, 0x4c, 0x68, 0x8c, 0x8c, 0xf5, 0x79, 0x0c, 0x4c, 0x23, 0x45, 0x8f, 0x16, 0x7b, 0x2f, 0xd2, - 0xbd, 0xf9, 0x76, 0xd1, 0x78, 0xea, 0xf9, 0x7a, 0x98, 0xf4, 0x6d, 0x57, 0x05, 0x9d, 0xa9, 0x8f, - 0xc2, 0xf8, 0xd1, 0x43, 0x77, 0xc8, 0xfd, 0xb0, 0x93, 0x13, 0xa1, 0x4f, 0x46, 0x10, 0xdb, 0x7b, - 0x10, 0xf9, 0x5c, 0xfa, 0x9f, 0x78, 0x5f, 0xc2, 0xcb, 0x50, 0x3b, 0xff, 0xc4, 0xb7, 0xfb, 0xfe, - 0x47, 0xe6, 0x5d, 0x9c, 0x88, 0x71, 0x8d, 0x5e, 0xcf, 0x38, 0x73, 0x06, 0x3c, 0xd3, 0xbd, 0xdd, - 0xb3, 0xcb, 0xba, 0x75, 0x7e, 0x59, 0xb7, 0x7e, 0x5c, 0xd6, 0xad, 0xd3, 0xab, 0x7a, 0xe1, 0xfc, - 0xaa, 0x5e, 0xf8, 0x7a, 0x55, 0x2f, 0x7c, 0x78, 0xfc, 0xe7, 0xa3, 0x1c, 0x5f, 0x7f, 0xb3, 0x70, - 0xa2, 0xfe, 0x5f, 0xc8, 0xbb, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x78, 0xda, 0x4e, 0x4b, 0xd6, - 0x04, 0x00, 0x00, + // 824 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x55, 0x51, 0x6f, 0x1b, 0x45, + 0x10, 0xf6, 0x39, 0x36, 0x4a, 0x26, 0xb5, 0x6b, 0xad, 0x42, 0x65, 0xb9, 0xaa, 0x63, 0x19, 0xaa, + 0xba, 0x52, 0x7b, 0xae, 0x12, 0x54, 0x84, 0xc4, 0x03, 0x76, 0x70, 0x42, 0x80, 0x16, 0x72, 0x49, + 0x0b, 0x42, 0x42, 0xa7, 0xb5, 0x77, 0xec, 0x9c, 0xb2, 0xbe, 0x0d, 0xb7, 0x7b, 0x51, 0xcb, 0x1f, + 0xe0, 0xb5, 0x7f, 0x84, 0x3f, 0xc1, 0x53, 0x1f, 0xfb, 0x88, 0x78, 0x28, 0x28, 0xf9, 0x09, 0x48, + 0x88, 0x47, 0xb4, 0xb3, 0xbe, 0x73, 0xdc, 0xba, 0x28, 0x2f, 0xd6, 0xcd, 0xb7, 0xdf, 0xcc, 0xec, + 0x7c, 0xf3, 0xdd, 0x19, 0x9a, 0xe2, 0xb9, 0x78, 0x76, 0x9a, 0x28, 0xa3, 0x46, 0x4a, 0x76, 0xb5, + 0xe1, 0x46, 0xbb, 0x5f, 0x9f, 0x40, 0xc6, 0x2e, 0x9f, 0xfb, 0x74, 0xd2, 0xd8, 0x98, 0xa8, 0x89, + 0x22, 0xac, 0x6b, 0x9f, 0x1c, 0xb3, 0xb1, 0x39, 0x51, 0x6a, 0x22, 0xb1, 0x4b, 0xd1, 0x30, 0x1d, + 0x77, 0x4d, 0x34, 0x45, 0x6d, 0xf8, 0xf4, 0xd4, 0x11, 0xda, 0xbf, 0x14, 0x61, 0xa3, 0x37, 0x1e, + 0x47, 0x32, 0xe2, 0x06, 0x7b, 0xc6, 0x24, 0xd1, 0x30, 0x35, 0x91, 0x8a, 0x59, 0x0f, 0x4a, 0x89, + 0x92, 0x58, 0xf7, 0x5a, 0x5e, 0xa7, 0xba, 0x75, 0xdf, 0x7f, 0xbb, 0xa5, 0xbf, 0x2c, 0xcf, 0x0f, + 0x94, 0xc4, 0x80, 0x52, 0xd9, 0x5d, 0xa8, 0x25, 0x38, 0xc6, 0x24, 0xc1, 0x24, 0xe4, 0x42, 0x24, + 0xa8, 0x75, 0xbd, 0xd8, 0xf2, 0x3a, 0x6b, 0xc1, 0xf5, 0x0c, 0xef, 0x39, 0x98, 0xed, 0x40, 0x73, + 0x06, 0x89, 0xf0, 0x4c, 0xc9, 0x74, 0x8a, 0xe1, 0x4f, 0xa9, 0x32, 0xf6, 0x97, 0xc7, 0x26, 0x9d, + 0xea, 0xfa, 0x4a, 0xcb, 0xeb, 0x94, 0x82, 0x9b, 0x19, 0xeb, 0x29, 0x91, 0x0e, 0x2c, 0xe7, 0x60, + 0x46, 0x69, 0x7f, 0x0a, 0x25, 0xdb, 0x9d, 0x6d, 0x40, 0x2d, 0xf8, 0xe6, 0xeb, 0x41, 0xf8, 0xe4, + 0xf1, 0xe1, 0xb7, 0x83, 0x9d, 0xfd, 0xdd, 0xfd, 0xc1, 0xe7, 0xb5, 0x02, 0xab, 0x02, 0x10, 0x7a, + 0xd4, 0xfb, 0x6a, 0x10, 0xd4, 0xbc, 0x3c, 0x7e, 0x44, 0x71, 0xb1, 0xfd, 0x5b, 0x11, 0xa0, 0x2f, + 0xd5, 0xe8, 0xe4, 0xd0, 0x0e, 0xc7, 0x3e, 0x81, 0xf2, 0x38, 0x92, 0x52, 0xd7, 0xbd, 0xd6, 0x4a, + 0x67, 0x7d, 0xeb, 0x83, 0x65, 0x02, 0xcc, 0xe9, 0xfe, 0x6e, 0x24, 0x65, 0xe0, 0x32, 0x1a, 0xff, + 0x7a, 0x50, 0xb2, 0x31, 0xdb, 0x80, 0xb2, 0xe1, 0x27, 0x98, 0x90, 0x88, 0x6b, 0x81, 0x0b, 0x2c, + 0x3a, 0x25, 0xd4, 0x69, 0xe1, 0x02, 0xd6, 0x80, 0xd5, 0x58, 0x59, 0x05, 0xb9, 0x9c, 0xcd, 0x9a, + 0xc7, 0x6c, 0x0f, 0x5a, 0x3c, 0xd3, 0x3a, 0x1c, 0x23, 0x86, 0x13, 0x8c, 0x31, 0xe1, 0x06, 0xc5, + 0x5c, 0x9f, 0x12, 0xe5, 0xdc, 0xca, 0x79, 0xbb, 0x88, 0x7b, 0x19, 0x2b, 0x53, 0x88, 0x85, 0x70, + 0x63, 0x5e, 0x88, 0xcf, 0xb7, 0xa6, 0xeb, 0x65, 0x9a, 0xb2, 0x73, 0xd5, 0x35, 0x07, 0xef, 0xf3, + 0x25, 0xa8, 0x6e, 0x3f, 0x84, 0x0a, 0xe9, 0xf1, 0x08, 0x0d, 0x17, 0xdc, 0x70, 0x76, 0x1b, 0xaa, + 0x26, 0xe1, 0x91, 0x8c, 0xe2, 0x49, 0x88, 0xa7, 0x6a, 0x74, 0x4c, 0x5a, 0x54, 0x82, 0x4a, 0x86, + 0x0e, 0x2c, 0xd8, 0xfe, 0xc7, 0x03, 0xa0, 0x27, 0x27, 0xfe, 0x97, 0x50, 0x25, 0x72, 0x88, 0xb1, + 0x08, 0xad, 0x65, 0x29, 0x6b, 0x7d, 0xab, 0xe1, 0x3b, 0x3f, 0xfb, 0x99, 0x9f, 0xfd, 0xa3, 0xcc, + 0xcf, 0xfd, 0xd5, 0x97, 0xaf, 0x37, 0x0b, 0x2f, 0xfe, 0xdc, 0xf4, 0x82, 0x6b, 0x94, 0x3b, 0x88, + 0x85, 0x3d, 0x64, 0x7d, 0x28, 0xd3, 0x1c, 0xf5, 0x22, 0x8d, 0x78, 0x6f, 0xd9, 0x88, 0xf3, 0xd6, + 0xfe, 0x13, 0x8d, 0xc9, 0x77, 0x91, 0x71, 0x51, 0xe0, 0x52, 0x1b, 0xdf, 0x43, 0x65, 0x01, 0x67, + 0x0c, 0x4a, 0xa9, 0xce, 0x17, 0x4b, 0xcf, 0x6c, 0x7b, 0xde, 0xc8, 0xde, 0xf5, 0xd6, 0xb2, 0x46, + 0xb6, 0xca, 0xe5, 0xca, 0xed, 0x87, 0xb0, 0xbe, 0x27, 0xd5, 0x90, 0x4b, 0x57, 0xf7, 0x0e, 0x5c, + 0xcf, 0xb6, 0x1e, 0x9a, 0x84, 0x0b, 0x14, 0xd4, 0xa2, 0x14, 0x54, 0x33, 0xf8, 0x88, 0xd0, 0xf6, + 0xdf, 0x45, 0x58, 0xcb, 0x8b, 0x91, 0xca, 0xd6, 0x45, 0x61, 0x6e, 0x21, 0x97, 0x55, 0x21, 0xf4, + 0x71, 0xe6, 0xa3, 0xdb, 0x50, 0x9d, 0x2e, 0xd2, 0x8a, 0x8e, 0x36, 0x5d, 0xa0, 0x3d, 0x85, 0xce, + 0xdc, 0x25, 0xdb, 0x0f, 0x44, 0x98, 0xe0, 0x19, 0xc6, 0xe9, 0x52, 0xdb, 0x39, 0xab, 0x7e, 0x98, + 0xf3, 0xb7, 0x1f, 0x88, 0xc0, 0xb1, 0xdf, 0x76, 0xdf, 0x8f, 0x70, 0xef, 0xcd, 0xba, 0xff, 0xfb, + 0xca, 0x3b, 0x4b, 0xdf, 0x59, 0xac, 0xfd, 0xce, 0xd7, 0x9f, 0x71, 0xf0, 0x17, 0xcb, 0x67, 0x06, + 0x7f, 0x67, 0x83, 0x32, 0x35, 0xb8, 0x7b, 0xb9, 0x41, 0x2f, 0xcf, 0x59, 0xf6, 0x85, 0xf9, 0xd5, + 0x83, 0x1b, 0x3b, 0x7c, 0x74, 0x8c, 0xe2, 0xd0, 0x2a, 0x2b, 0xfa, 0x5c, 0xe3, 0x91, 0x3a, 0xc1, + 0x58, 0xb3, 0x33, 0x60, 0x9a, 0xb0, 0x70, 0xc8, 0x35, 0x86, 0x86, 0x50, 0x5a, 0xc3, 0xb5, 0xfe, + 0x17, 0xd6, 0x9a, 0x7f, 0xbc, 0xde, 0xfc, 0x6c, 0x12, 0x99, 0xe3, 0x74, 0xe8, 0x8f, 0xd4, 0xb4, + 0xbb, 0xf0, 0x89, 0x3f, 0xfb, 0xe8, 0xfe, 0xe8, 0x98, 0x47, 0x71, 0x37, 0x47, 0x84, 0x79, 0x7e, + 0x8a, 0xda, 0x3f, 0xc4, 0x24, 0xe2, 0x32, 0xfa, 0x99, 0x0f, 0x25, 0xee, 0xc7, 0x26, 0xa8, 0xe9, + 0x37, 0xfb, 0xde, 0x84, 0xb5, 0x11, 0xdd, 0x28, 0xe4, 0x86, 0xd6, 0xb9, 0x12, 0xac, 0x3a, 0xa0, + 0x67, 0xfa, 0x07, 0x2f, 0xcf, 0x9b, 0xde, 0xab, 0xf3, 0xa6, 0xf7, 0xd7, 0x79, 0xd3, 0x7b, 0x71, + 0xd1, 0x2c, 0xbc, 0xba, 0x68, 0x16, 0x7e, 0xbf, 0x68, 0x16, 0x7e, 0xf8, 0xf8, 0xea, 0x57, 0x79, + 0x36, 0xfb, 0x07, 0xa2, 0x1b, 0x0d, 0xdf, 0x23, 0x7c, 0xfb, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x4b, 0xc8, 0x22, 0xdf, 0xa4, 0x06, 0x00, 0x00, +} + +func (m *AffiliateAttribution) 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 *AffiliateAttribution) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *AffiliateAttribution) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.ReferredVolumeQuoteQuantums != 0 { + i = encodeVarintStats(dAtA, i, uint64(m.ReferredVolumeQuoteQuantums)) + i-- + dAtA[i] = 0x18 + } + if len(m.ReferrerAddress) > 0 { + i -= len(m.ReferrerAddress) + copy(dAtA[i:], m.ReferrerAddress) + i = encodeVarintStats(dAtA, i, uint64(len(m.ReferrerAddress))) + i-- + dAtA[i] = 0x12 + } + if m.Role != 0 { + i = encodeVarintStats(dAtA, i, uint64(m.Role)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil } func (m *BlockStats) Marshal() (dAtA []byte, err error) { @@ -590,6 +755,20 @@ func (m *BlockStats_Fill) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.AffiliateAttributions) > 0 { + for iNdEx := len(m.AffiliateAttributions) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.AffiliateAttributions[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintStats(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x2a + } + } if m.AffiliateFeeGeneratedQuantums != 0 { i = encodeVarintStats(dAtA, i, uint64(m.AffiliateFeeGeneratedQuantums)) i-- @@ -780,6 +959,11 @@ func (m *UserStats) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.Affiliate_30DAttributedVolumeQuoteQuantums != 0 { + i = encodeVarintStats(dAtA, i, uint64(m.Affiliate_30DAttributedVolumeQuoteQuantums)) + i-- + dAtA[i] = 0x28 + } if m.Affiliate_30DReferredVolumeQuoteQuantums != 0 { i = encodeVarintStats(dAtA, i, uint64(m.Affiliate_30DReferredVolumeQuoteQuantums)) i-- @@ -852,6 +1036,25 @@ func encodeVarintStats(dAtA []byte, offset int, v uint64) int { dAtA[offset] = uint8(v) return base } +func (m *AffiliateAttribution) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Role != 0 { + n += 1 + sovStats(uint64(m.Role)) + } + l = len(m.ReferrerAddress) + if l > 0 { + n += 1 + l + sovStats(uint64(l)) + } + if m.ReferredVolumeQuoteQuantums != 0 { + n += 1 + sovStats(uint64(m.ReferredVolumeQuoteQuantums)) + } + return n +} + func (m *BlockStats) Size() (n int) { if m == nil { return 0 @@ -887,6 +1090,12 @@ func (m *BlockStats_Fill) Size() (n int) { if m.AffiliateFeeGeneratedQuantums != 0 { n += 1 + sovStats(uint64(m.AffiliateFeeGeneratedQuantums)) } + if len(m.AffiliateAttributions) > 0 { + for _, e := range m.AffiliateAttributions { + l = e.Size() + n += 1 + l + sovStats(uint64(l)) + } + } return n } @@ -966,6 +1175,9 @@ func (m *UserStats) Size() (n int) { if m.Affiliate_30DReferredVolumeQuoteQuantums != 0 { n += 1 + sovStats(uint64(m.Affiliate_30DReferredVolumeQuoteQuantums)) } + if m.Affiliate_30DAttributedVolumeQuoteQuantums != 0 { + n += 1 + sovStats(uint64(m.Affiliate_30DAttributedVolumeQuoteQuantums)) + } return n } @@ -989,6 +1201,126 @@ func sovStats(x uint64) (n int) { func sozStats(x uint64) (n int) { return sovStats(uint64((x << 1) ^ uint64((int64(x) >> 63)))) } +func (m *AffiliateAttribution) 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 ErrIntOverflowStats + } + 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: AffiliateAttribution: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: AffiliateAttribution: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Role", wireType) + } + m.Role = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStats + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Role |= AffiliateAttribution_Role(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ReferrerAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStats + } + 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 ErrInvalidLengthStats + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthStats + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ReferrerAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 3: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field ReferredVolumeQuoteQuantums", wireType) + } + m.ReferredVolumeQuoteQuantums = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStats + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.ReferredVolumeQuoteQuantums |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipStats(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthStats + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *BlockStats) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 @@ -1204,6 +1536,40 @@ func (m *BlockStats_Fill) Unmarshal(dAtA []byte) error { break } } + case 5: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AffiliateAttributions", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStats + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthStats + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthStats + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AffiliateAttributions = append(m.AffiliateAttributions, &AffiliateAttribution{}) + if err := m.AffiliateAttributions[len(m.AffiliateAttributions)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipStats(dAtA[iNdEx:]) @@ -1703,6 +2069,25 @@ func (m *UserStats) Unmarshal(dAtA []byte) error { break } } + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Affiliate_30DAttributedVolumeQuoteQuantums", wireType) + } + m.Affiliate_30DAttributedVolumeQuoteQuantums = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowStats + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Affiliate_30DAttributedVolumeQuoteQuantums |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipStats(dAtA[iNdEx:]) From fbf7749f30335ccade559b12b41d129680b8d2c9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 11:03:38 -0500 Subject: [PATCH 03/14] Add rpc and cli command for getting an epoch (backport #3258) (#3259) Co-authored-by: Justin Barnett <61020572+jusbar23@users.noreply.github.com> --- .../codegen/dydxprotocol/stats/query.lcd.ts | 10 +- .../dydxprotocol/stats/query.rpc.Query.ts | 16 +- .../src/codegen/dydxprotocol/stats/query.ts | 116 ++++- proto/dydxprotocol/stats/query.proto | 10 + protocol/x/epochs/keeper/keeper.go | 1 - protocol/x/stats/client/cli/query.go | 33 ++ protocol/x/stats/client/cli/query_test.go | 10 + protocol/x/stats/keeper/grpc_query.go | 26 + protocol/x/stats/keeper/grpc_query_test.go | 70 +++ protocol/x/stats/types/query.pb.go | 443 ++++++++++++++++-- protocol/x/stats/types/query.pb.gw.go | 101 ++++ 11 files changed, 799 insertions(+), 37 deletions(-) diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/query.lcd.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/query.lcd.ts index bc4b8cec4a..b950d724c0 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/query.lcd.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/query.lcd.ts @@ -1,5 +1,5 @@ import { LCDClient } from "@osmonauts/lcd"; -import { QueryParamsRequest, QueryParamsResponseSDKType, QueryStatsMetadataRequest, QueryStatsMetadataResponseSDKType, QueryGlobalStatsRequest, QueryGlobalStatsResponseSDKType, QueryUserStatsRequest, QueryUserStatsResponseSDKType } from "./query"; +import { QueryParamsRequest, QueryParamsResponseSDKType, QueryStatsMetadataRequest, QueryStatsMetadataResponseSDKType, QueryGlobalStatsRequest, QueryGlobalStatsResponseSDKType, QueryUserStatsRequest, QueryUserStatsResponseSDKType, QueryEpochStatsRequest, QueryEpochStatsResponseSDKType } from "./query"; export class LCDQueryClient { req: LCDClient; @@ -13,6 +13,7 @@ export class LCDQueryClient { this.statsMetadata = this.statsMetadata.bind(this); this.globalStats = this.globalStats.bind(this); this.userStats = this.userStats.bind(this); + this.epochStats = this.epochStats.bind(this); } /* Queries the Params. */ @@ -50,5 +51,12 @@ export class LCDQueryClient { const endpoint = `dydxprotocol/v4/stats/user_stats`; return await this.req.get(endpoint, options); } + /* Queries EpochStats for a specific epoch. */ + + + async epochStats(params: QueryEpochStatsRequest): Promise { + const endpoint = `dydxprotocol/v4/stats/epoch_stats/${params.epoch}`; + return await this.req.get(endpoint); + } } \ No newline at end of file diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/query.rpc.Query.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/query.rpc.Query.ts index 001d097b19..28f22a6d78 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/query.rpc.Query.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/query.rpc.Query.ts @@ -1,7 +1,7 @@ import { Rpc } from "../../helpers"; import * as _m0 from "protobufjs/minimal"; import { QueryClient, createProtobufRpcClient } from "@cosmjs/stargate"; -import { QueryParamsRequest, QueryParamsResponse, QueryStatsMetadataRequest, QueryStatsMetadataResponse, QueryGlobalStatsRequest, QueryGlobalStatsResponse, QueryUserStatsRequest, QueryUserStatsResponse } from "./query"; +import { QueryParamsRequest, QueryParamsResponse, QueryStatsMetadataRequest, QueryStatsMetadataResponse, QueryGlobalStatsRequest, QueryGlobalStatsResponse, QueryUserStatsRequest, QueryUserStatsResponse, QueryEpochStatsRequest, QueryEpochStatsResponse } from "./query"; /** Query defines the gRPC querier service. */ export interface Query { @@ -16,6 +16,9 @@ export interface Query { /** Queries UserStats. */ userStats(request: QueryUserStatsRequest): Promise; + /** Queries EpochStats for a specific epoch. */ + + epochStats(request: QueryEpochStatsRequest): Promise; } export class QueryClientImpl implements Query { private readonly rpc: Rpc; @@ -26,6 +29,7 @@ export class QueryClientImpl implements Query { this.statsMetadata = this.statsMetadata.bind(this); this.globalStats = this.globalStats.bind(this); this.userStats = this.userStats.bind(this); + this.epochStats = this.epochStats.bind(this); } params(request: QueryParamsRequest = {}): Promise { @@ -52,6 +56,12 @@ export class QueryClientImpl implements Query { return promise.then(data => QueryUserStatsResponse.decode(new _m0.Reader(data))); } + epochStats(request: QueryEpochStatsRequest): Promise { + const data = QueryEpochStatsRequest.encode(request).finish(); + const promise = this.rpc.request("dydxprotocol.stats.Query", "EpochStats", data); + return promise.then(data => QueryEpochStatsResponse.decode(new _m0.Reader(data))); + } + } export const createRpcQueryExtension = (base: QueryClient) => { const rpc = createProtobufRpcClient(base); @@ -71,6 +81,10 @@ export const createRpcQueryExtension = (base: QueryClient) => { userStats(request: QueryUserStatsRequest): Promise { return queryService.userStats(request); + }, + + epochStats(request: QueryEpochStatsRequest): Promise { + return queryService.epochStats(request); } }; diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/query.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/query.ts index a98370e912..18bb7cf4b5 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/query.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/stats/query.ts @@ -1,5 +1,5 @@ import { Params, ParamsSDKType } from "./params"; -import { StatsMetadata, StatsMetadataSDKType, GlobalStats, GlobalStatsSDKType, UserStats, UserStatsSDKType } from "./stats"; +import { StatsMetadata, StatsMetadataSDKType, GlobalStats, GlobalStatsSDKType, UserStats, UserStatsSDKType, EpochStats, EpochStatsSDKType } from "./stats"; import * as _m0 from "protobufjs/minimal"; import { DeepPartial } from "../../helpers"; /** QueryParamsRequest is a request type for the Params RPC method. */ @@ -90,6 +90,30 @@ export interface QueryUserStatsResponseSDKType { /** QueryUserStatsResponse is a request type for the UserStats RPC method. */ stats?: UserStatsSDKType; } +/** QueryEpochStatsRequest is a request type for the EpochStats RPC method. */ + +export interface QueryEpochStatsRequest { + /** QueryEpochStatsRequest is a request type for the EpochStats RPC method. */ + epoch: number; +} +/** QueryEpochStatsRequest is a request type for the EpochStats RPC method. */ + +export interface QueryEpochStatsRequestSDKType { + /** QueryEpochStatsRequest is a request type for the EpochStats RPC method. */ + epoch: number; +} +/** QueryEpochStatsResponse is a response type for the EpochStats RPC method. */ + +export interface QueryEpochStatsResponse { + /** QueryEpochStatsResponse is a response type for the EpochStats RPC method. */ + stats?: EpochStats; +} +/** QueryEpochStatsResponse is a response type for the EpochStats RPC method. */ + +export interface QueryEpochStatsResponseSDKType { + /** QueryEpochStatsResponse is a response type for the EpochStats RPC method. */ + stats?: EpochStatsSDKType; +} function createBaseQueryParamsRequest(): QueryParamsRequest { return {}; @@ -416,4 +440,94 @@ export const QueryUserStatsResponse = { return message; } +}; + +function createBaseQueryEpochStatsRequest(): QueryEpochStatsRequest { + return { + epoch: 0 + }; +} + +export const QueryEpochStatsRequest = { + encode(message: QueryEpochStatsRequest, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.epoch !== 0) { + writer.uint32(8).uint32(message.epoch); + } + + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): QueryEpochStatsRequest { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseQueryEpochStatsRequest(); + + while (reader.pos < end) { + const tag = reader.uint32(); + + switch (tag >>> 3) { + case 1: + message.epoch = reader.uint32(); + break; + + default: + reader.skipType(tag & 7); + break; + } + } + + return message; + }, + + fromPartial(object: DeepPartial): QueryEpochStatsRequest { + const message = createBaseQueryEpochStatsRequest(); + message.epoch = object.epoch ?? 0; + return message; + } + +}; + +function createBaseQueryEpochStatsResponse(): QueryEpochStatsResponse { + return { + stats: undefined + }; +} + +export const QueryEpochStatsResponse = { + encode(message: QueryEpochStatsResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.stats !== undefined) { + EpochStats.encode(message.stats, writer.uint32(10).fork()).ldelim(); + } + + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): QueryEpochStatsResponse { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseQueryEpochStatsResponse(); + + while (reader.pos < end) { + const tag = reader.uint32(); + + switch (tag >>> 3) { + case 1: + message.stats = EpochStats.decode(reader, reader.uint32()); + break; + + default: + reader.skipType(tag & 7); + break; + } + } + + return message; + }, + + fromPartial(object: DeepPartial): QueryEpochStatsResponse { + const message = createBaseQueryEpochStatsResponse(); + message.stats = object.stats !== undefined && object.stats !== null ? EpochStats.fromPartial(object.stats) : undefined; + return message; + } + }; \ No newline at end of file diff --git a/proto/dydxprotocol/stats/query.proto b/proto/dydxprotocol/stats/query.proto index 9b6c9b53ea..31ac32e085 100644 --- a/proto/dydxprotocol/stats/query.proto +++ b/proto/dydxprotocol/stats/query.proto @@ -30,6 +30,11 @@ service Query { rpc UserStats(QueryUserStatsRequest) returns (QueryUserStatsResponse) { option (google.api.http).get = "/dydxprotocol/v4/stats/user_stats"; } + + // Queries EpochStats for a specific epoch. + rpc EpochStats(QueryEpochStatsRequest) returns (QueryEpochStatsResponse) { + option (google.api.http).get = "/dydxprotocol/v4/stats/epoch_stats/{epoch}"; + } } // QueryParamsRequest is a request type for the Params RPC method. @@ -57,3 +62,8 @@ message QueryGlobalStatsResponse { GlobalStats stats = 1; } message QueryUserStatsRequest { string user = 1; } // QueryUserStatsResponse is a request type for the UserStats RPC method. message QueryUserStatsResponse { UserStats stats = 1; } + +// QueryEpochStatsRequest is a request type for the EpochStats RPC method. +message QueryEpochStatsRequest { uint32 epoch = 1; } +// QueryEpochStatsResponse is a response type for the EpochStats RPC method. +message QueryEpochStatsResponse { EpochStats stats = 1; } diff --git a/protocol/x/epochs/keeper/keeper.go b/protocol/x/epochs/keeper/keeper.go index d1f31c6f32..91ac0d22c0 100644 --- a/protocol/x/epochs/keeper/keeper.go +++ b/protocol/x/epochs/keeper/keeper.go @@ -20,7 +20,6 @@ type ( func NewKeeper( cdc codec.BinaryCodec, storeKey storetypes.StoreKey, - ) *Keeper { return &Keeper{ cdc: cdc, diff --git a/protocol/x/stats/client/cli/query.go b/protocol/x/stats/client/cli/query.go index 68d4c215e0..db52f9231f 100644 --- a/protocol/x/stats/client/cli/query.go +++ b/protocol/x/stats/client/cli/query.go @@ -3,6 +3,7 @@ package cli import ( "context" "fmt" + "strconv" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/flags" @@ -25,6 +26,7 @@ func GetQueryCmd(queryRoute string) *cobra.Command { cmd.AddCommand(CmdQueryStatsMetadata()) cmd.AddCommand(CmdQueryGlobalStats()) cmd.AddCommand(CmdQueryUserStats()) + cmd.AddCommand(CmdQueryEpochStats()) return cmd } @@ -123,3 +125,34 @@ func CmdQueryUserStats() *cobra.Command { return cmd } + +func CmdQueryEpochStats() *cobra.Command { + cmd := &cobra.Command{ + Use: "get-epoch-stats [epoch]", + Short: "get stats for a specific epoch", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) (err error) { + epochNum, err := strconv.ParseUint(args[0], 10, 32) + if err != nil { + return fmt.Errorf("invalid epoch number: %w", err) + } + + clientCtx := client.GetClientContextFromCmd(cmd) + queryClient := types.NewQueryClient(clientCtx) + res, err := queryClient.EpochStats( + context.Background(), + &types.QueryEpochStatsRequest{ + Epoch: uint32(epochNum), + }, + ) + if err != nil { + return err + } + return clientCtx.PrintProto(res) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/protocol/x/stats/client/cli/query_test.go b/protocol/x/stats/client/cli/query_test.go index 10faef1a74..f0075b93f9 100644 --- a/protocol/x/stats/client/cli/query_test.go +++ b/protocol/x/stats/client/cli/query_test.go @@ -82,3 +82,13 @@ func TestQueryUserStats(t *testing.T) { var resp types.QueryUserStatsResponse require.NoError(t, net.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) } + +func TestQueryEpochStats(t *testing.T) { + net, ctx := setupNetwork(t) + + out, err := clitestutil.ExecTestCLICmd(ctx, cli.CmdQueryEpochStats(), []string{"0"}) + + require.NoError(t, err) + var resp types.QueryEpochStatsResponse + require.NoError(t, net.Config.Codec.UnmarshalJSON(out.Bytes(), &resp)) +} diff --git a/protocol/x/stats/keeper/grpc_query.go b/protocol/x/stats/keeper/grpc_query.go index fc189de124..dcde86cead 100644 --- a/protocol/x/stats/keeper/grpc_query.go +++ b/protocol/x/stats/keeper/grpc_query.go @@ -83,3 +83,29 @@ func (k Keeper) UserStats( Stats: userStats, }, nil } + +func (k Keeper) EpochStats( + c context.Context, + req *types.QueryEpochStatsRequest, +) ( + *types.QueryEpochStatsResponse, + error, +) { + if req == nil { + return nil, status.Error(codes.InvalidArgument, "invalid request") + } + + ctx := lib.UnwrapSDKContext(c, types.ModuleName) + epochStats := k.GetEpochStatsOrNil(ctx, req.Epoch) + + // Return empty stats if epoch not found + if epochStats == nil { + epochStats = &types.EpochStats{ + Stats: []*types.EpochStats_UserWithStats{}, + } + } + + return &types.QueryEpochStatsResponse{ + Stats: epochStats, + }, nil +} diff --git a/protocol/x/stats/keeper/grpc_query_test.go b/protocol/x/stats/keeper/grpc_query_test.go index ba294ac949..ebabeb256a 100644 --- a/protocol/x/stats/keeper/grpc_query_test.go +++ b/protocol/x/stats/keeper/grpc_query_test.go @@ -193,3 +193,73 @@ func TestUserStats(t *testing.T) { }) } } + +func TestEpochStats(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + ctx := tApp.InitChain() + k := tApp.App.StatsKeeper + + // Create test epoch stats for epoch 5 + epochNum := uint32(5) + epochStats := &types.EpochStats{ + Stats: []*types.EpochStats_UserWithStats{ + { + User: "alice", + Stats: &types.UserStats{ + TakerNotional: 100, + MakerNotional: 200, + }, + }, + { + User: "bob", + Stats: &types.UserStats{ + TakerNotional: 50, + MakerNotional: 75, + }, + }, + }, + } + k.SetEpochStats(ctx, epochNum, epochStats) + + for name, tc := range map[string]struct { + req *types.QueryEpochStatsRequest + res *types.QueryEpochStatsResponse + err error + }{ + "Success - existing epoch": { + req: &types.QueryEpochStatsRequest{ + Epoch: epochNum, + }, + res: &types.QueryEpochStatsResponse{ + Stats: epochStats, + }, + err: nil, + }, + "Success - non-existent epoch returns empty stats": { + req: &types.QueryEpochStatsRequest{ + Epoch: 999, + }, + res: &types.QueryEpochStatsResponse{ + Stats: &types.EpochStats{ + Stats: []*types.EpochStats_UserWithStats{}, + }, + }, + err: nil, + }, + "Nil request": { + req: nil, + res: nil, + err: status.Error(codes.InvalidArgument, "invalid request"), + }, + } { + t.Run(name, func(t *testing.T) { + res, err := k.EpochStats(ctx, tc.req) + if tc.err != nil { + require.ErrorIs(t, err, tc.err) + } else { + require.NoError(t, err) + require.Equal(t, tc.res, res) + } + }) + } +} diff --git a/protocol/x/stats/types/query.pb.go b/protocol/x/stats/types/query.pb.go index 563e422074..d6624807f9 100644 --- a/protocol/x/stats/types/query.pb.go +++ b/protocol/x/stats/types/query.pb.go @@ -366,6 +366,96 @@ func (m *QueryUserStatsResponse) GetStats() *UserStats { return nil } +// QueryEpochStatsRequest is a request type for the EpochStats RPC method. +type QueryEpochStatsRequest struct { + Epoch uint32 `protobuf:"varint,1,opt,name=epoch,proto3" json:"epoch,omitempty"` +} + +func (m *QueryEpochStatsRequest) Reset() { *m = QueryEpochStatsRequest{} } +func (m *QueryEpochStatsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryEpochStatsRequest) ProtoMessage() {} +func (*QueryEpochStatsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_17835dac31373c4f, []int{8} +} +func (m *QueryEpochStatsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryEpochStatsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryEpochStatsRequest.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 *QueryEpochStatsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryEpochStatsRequest.Merge(m, src) +} +func (m *QueryEpochStatsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryEpochStatsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryEpochStatsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryEpochStatsRequest proto.InternalMessageInfo + +func (m *QueryEpochStatsRequest) GetEpoch() uint32 { + if m != nil { + return m.Epoch + } + return 0 +} + +// QueryEpochStatsResponse is a response type for the EpochStats RPC method. +type QueryEpochStatsResponse struct { + Stats *EpochStats `protobuf:"bytes,1,opt,name=stats,proto3" json:"stats,omitempty"` +} + +func (m *QueryEpochStatsResponse) Reset() { *m = QueryEpochStatsResponse{} } +func (m *QueryEpochStatsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryEpochStatsResponse) ProtoMessage() {} +func (*QueryEpochStatsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_17835dac31373c4f, []int{9} +} +func (m *QueryEpochStatsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryEpochStatsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryEpochStatsResponse.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 *QueryEpochStatsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryEpochStatsResponse.Merge(m, src) +} +func (m *QueryEpochStatsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryEpochStatsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryEpochStatsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryEpochStatsResponse proto.InternalMessageInfo + +func (m *QueryEpochStatsResponse) GetStats() *EpochStats { + if m != nil { + return m.Stats + } + return nil +} + func init() { proto.RegisterType((*QueryParamsRequest)(nil), "dydxprotocol.stats.QueryParamsRequest") proto.RegisterType((*QueryParamsResponse)(nil), "dydxprotocol.stats.QueryParamsResponse") @@ -375,44 +465,50 @@ func init() { proto.RegisterType((*QueryGlobalStatsResponse)(nil), "dydxprotocol.stats.QueryGlobalStatsResponse") proto.RegisterType((*QueryUserStatsRequest)(nil), "dydxprotocol.stats.QueryUserStatsRequest") proto.RegisterType((*QueryUserStatsResponse)(nil), "dydxprotocol.stats.QueryUserStatsResponse") + proto.RegisterType((*QueryEpochStatsRequest)(nil), "dydxprotocol.stats.QueryEpochStatsRequest") + proto.RegisterType((*QueryEpochStatsResponse)(nil), "dydxprotocol.stats.QueryEpochStatsResponse") } func init() { proto.RegisterFile("dydxprotocol/stats/query.proto", fileDescriptor_17835dac31373c4f) } var fileDescriptor_17835dac31373c4f = []byte{ - // 501 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xc1, 0x6f, 0x12, 0x41, - 0x14, 0xc6, 0x19, 0x43, 0x89, 0x7d, 0x8d, 0x97, 0x67, 0xd5, 0x76, 0xb5, 0x8b, 0xdd, 0x06, 0x6b, - 0x45, 0x76, 0x93, 0x56, 0xa3, 0x17, 0x2f, 0xbd, 0x78, 0x6a, 0x14, 0x8c, 0x17, 0x3d, 0x34, 0x03, - 0x4c, 0xb6, 0x24, 0xb0, 0xb3, 0xdd, 0x19, 0x4c, 0xb9, 0x19, 0xef, 0x26, 0x26, 0xc6, 0xa3, 0xff, - 0x4f, 0x8f, 0x44, 0x2f, 0x9e, 0x8c, 0x01, 0xff, 0x10, 0xc3, 0x9b, 0x01, 0x59, 0xd8, 0x4d, 0xb9, - 0x6c, 0x86, 0xf7, 0xbe, 0xf7, 0xfd, 0x3e, 0xf2, 0x1e, 0xb8, 0xed, 0x41, 0xfb, 0x22, 0x4e, 0xa4, - 0x96, 0x2d, 0xd9, 0x0d, 0x94, 0xe6, 0x5a, 0x05, 0xe7, 0x7d, 0x91, 0x0c, 0x7c, 0x2a, 0x22, 0xce, - 0xf7, 0x7d, 0xea, 0x3b, 0x9b, 0xa1, 0x0c, 0x25, 0xd5, 0x82, 0xc9, 0xcb, 0x28, 0x9d, 0x7b, 0xa1, - 0x94, 0x61, 0x57, 0x04, 0x3c, 0xee, 0x04, 0x3c, 0x8a, 0xa4, 0xe6, 0xba, 0x23, 0x23, 0x65, 0xbb, - 0xe5, 0x0c, 0x4e, 0xcc, 0x13, 0xde, 0x9b, 0x0a, 0xb2, 0x82, 0xd0, 0xd7, 0xf4, 0xbd, 0x4d, 0xc0, - 0xfa, 0x24, 0xd7, 0x6b, 0x1a, 0x6a, 0x88, 0xf3, 0xbe, 0x50, 0xda, 0x7b, 0x05, 0x37, 0x53, 0x55, - 0x15, 0xcb, 0x48, 0x09, 0x7c, 0x0e, 0x25, 0x63, 0xbe, 0xc5, 0xee, 0xb3, 0x87, 0x1b, 0x87, 0x8e, - 0xbf, 0xfc, 0x37, 0x7c, 0x33, 0x73, 0x5c, 0xbc, 0xfc, 0x5d, 0x2e, 0x34, 0xac, 0xde, 0xbb, 0x0b, - 0xdb, 0x64, 0xf8, 0x66, 0x22, 0x39, 0x11, 0x9a, 0xb7, 0xb9, 0xe6, 0x53, 0xda, 0x7b, 0x70, 0xb2, - 0x9a, 0x16, 0xfa, 0x02, 0xae, 0xf7, 0x6c, 0xcd, 0x62, 0x77, 0xb3, 0xb0, 0xe9, 0xe1, 0xd9, 0x88, - 0xb7, 0x0d, 0x77, 0xc8, 0xfc, 0x65, 0x57, 0x36, 0x79, 0x97, 0x54, 0x53, 0x6e, 0x1d, 0xb6, 0x96, - 0x5b, 0x96, 0xfa, 0x14, 0xd6, 0xc8, 0xd7, 0x22, 0xcb, 0x59, 0xc8, 0xf9, 0x39, 0xa3, 0xf6, 0xaa, - 0x70, 0x8b, 0x2c, 0xdf, 0x2a, 0x91, 0xcc, 0xb3, 0x10, 0xa1, 0xd8, 0x57, 0x22, 0x21, 0xbb, 0xf5, - 0x06, 0xbd, 0xbd, 0x13, 0xb8, 0xbd, 0x28, 0xb6, 0xf4, 0xa3, 0x34, 0x7d, 0x27, 0x8b, 0xfe, 0x7f, - 0xca, 0x68, 0x0f, 0x7f, 0x14, 0x61, 0x8d, 0xfc, 0xf0, 0x23, 0x83, 0x92, 0x59, 0x03, 0x3e, 0xc8, - 0x1a, 0x5d, 0xde, 0xb8, 0xb3, 0x7f, 0xa5, 0xce, 0x44, 0xf3, 0x2a, 0x9f, 0x7e, 0xfe, 0xfd, 0x7a, - 0xad, 0x8c, 0x3b, 0x41, 0xea, 0xb2, 0x3e, 0x3c, 0x49, 0x5d, 0x1f, 0x7e, 0x67, 0x70, 0x23, 0xb5, - 0x12, 0xac, 0xe5, 0x12, 0xb2, 0x8e, 0xc2, 0xf1, 0x57, 0x95, 0xdb, 0x5c, 0x35, 0xca, 0xb5, 0x8f, - 0x95, 0x9c, 0x5c, 0xf4, 0x3d, 0x9d, 0x9e, 0x05, 0x7e, 0x63, 0xb0, 0x31, 0xb7, 0x3f, 0xac, 0xe6, - 0xe2, 0x96, 0x0f, 0xc7, 0x79, 0xbc, 0x9a, 0xd8, 0x26, 0xab, 0x52, 0xb2, 0x0a, 0xee, 0xe5, 0x24, - 0x0b, 0x69, 0xe6, 0x94, 0x7e, 0xe0, 0x67, 0x06, 0xeb, 0xb3, 0xcd, 0xe2, 0x41, 0x2e, 0x68, 0xf1, - 0xc0, 0x9c, 0x47, 0xab, 0x48, 0x6d, 0xa2, 0x03, 0x4a, 0xb4, 0x87, 0xbb, 0x39, 0x89, 0x26, 0xd7, - 0x69, 0xf2, 0x1c, 0xd7, 0x2f, 0x47, 0x2e, 0x1b, 0x8e, 0x5c, 0xf6, 0x67, 0xe4, 0xb2, 0x2f, 0x63, - 0xb7, 0x30, 0x1c, 0xbb, 0x85, 0x5f, 0x63, 0xb7, 0xf0, 0xee, 0x59, 0xd8, 0xd1, 0x67, 0xfd, 0xa6, - 0xdf, 0x92, 0xbd, 0x45, 0x9b, 0x5a, 0xeb, 0x8c, 0x77, 0xa2, 0x60, 0x56, 0xb9, 0xb0, 0xbe, 0x7a, - 0x10, 0x0b, 0xd5, 0x2c, 0x51, 0xfd, 0xe8, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x5a, 0xdb, 0xd8, - 0x74, 0x24, 0x05, 0x00, 0x00, + // 572 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x93, 0x41, 0x6b, 0x13, 0x4f, + 0x18, 0xc6, 0x33, 0x7f, 0x9a, 0xf0, 0xef, 0x5b, 0x7a, 0x79, 0x8d, 0xda, 0xae, 0x76, 0x63, 0xb7, + 0xc4, 0xda, 0xa6, 0xd9, 0x85, 0xb4, 0xa2, 0x17, 0x2f, 0x05, 0xf1, 0x54, 0x6a, 0x22, 0x5e, 0xf4, + 0x50, 0x26, 0xc9, 0xb0, 0x09, 0x24, 0x99, 0x6d, 0x76, 0x22, 0x0d, 0x22, 0x88, 0x77, 0x41, 0x10, + 0x0f, 0x1e, 0xfc, 0x3e, 0x3d, 0x16, 0xbc, 0x78, 0x12, 0x49, 0x3c, 0xfb, 0x19, 0x24, 0xef, 0x4c, + 0xd2, 0xdd, 0x64, 0x17, 0x73, 0x59, 0x76, 0xde, 0x79, 0xde, 0xe7, 0xf7, 0x24, 0x3c, 0x0b, 0x76, + 0x73, 0xd8, 0xbc, 0x08, 0xfa, 0x52, 0xc9, 0x86, 0xec, 0x78, 0xa1, 0xe2, 0x2a, 0xf4, 0xce, 0x07, + 0xa2, 0x3f, 0x74, 0x69, 0x88, 0x18, 0xbd, 0x77, 0xe9, 0xde, 0xca, 0xfb, 0xd2, 0x97, 0x34, 0xf3, + 0x26, 0x6f, 0x5a, 0x69, 0xdd, 0xf5, 0xa5, 0xf4, 0x3b, 0xc2, 0xe3, 0x41, 0xdb, 0xe3, 0xbd, 0x9e, + 0x54, 0x5c, 0xb5, 0x65, 0x2f, 0x34, 0xb7, 0x85, 0x04, 0x4e, 0xc0, 0xfb, 0xbc, 0x3b, 0x15, 0x24, + 0x05, 0xa1, 0xa7, 0xbe, 0x77, 0xf2, 0x80, 0xd5, 0x49, 0xae, 0xe7, 0xb4, 0x54, 0x13, 0xe7, 0x03, + 0x11, 0x2a, 0xe7, 0x14, 0x6e, 0xc4, 0xa6, 0x61, 0x20, 0x7b, 0xa1, 0xc0, 0xc7, 0x90, 0xd3, 0xe6, + 0x1b, 0xec, 0x1e, 0x7b, 0xb0, 0x56, 0xb1, 0xdc, 0xc5, 0x9f, 0xe1, 0xea, 0x9d, 0xe3, 0x95, 0xcb, + 0x9f, 0x85, 0x4c, 0xcd, 0xe8, 0x9d, 0x3b, 0xb0, 0x49, 0x86, 0x2f, 0x26, 0x92, 0x13, 0xa1, 0x78, + 0x93, 0x2b, 0x3e, 0xa5, 0xbd, 0x06, 0x2b, 0xe9, 0xd2, 0x40, 0x9f, 0xc0, 0xff, 0x5d, 0x33, 0x33, + 0xd8, 0xed, 0x24, 0x6c, 0x7c, 0x79, 0xb6, 0xe2, 0x6c, 0xc2, 0x6d, 0x32, 0x7f, 0xd6, 0x91, 0x75, + 0xde, 0x21, 0xd5, 0x94, 0x5b, 0x85, 0x8d, 0xc5, 0x2b, 0x43, 0x7d, 0x08, 0x59, 0xf2, 0x35, 0xc8, + 0x42, 0x12, 0x32, 0xba, 0xa7, 0xd5, 0x4e, 0x09, 0x6e, 0x92, 0xe5, 0xcb, 0x50, 0xf4, 0xa3, 0x2c, + 0x44, 0x58, 0x19, 0x84, 0xa2, 0x4f, 0x76, 0xab, 0x35, 0x7a, 0x77, 0x4e, 0xe0, 0xd6, 0xbc, 0xd8, + 0xd0, 0x0f, 0xe3, 0xf4, 0xad, 0x24, 0xfa, 0xf5, 0x96, 0x61, 0xbb, 0xc6, 0xee, 0x69, 0x20, 0x1b, + 0xad, 0x18, 0x3c, 0x0f, 0x59, 0x31, 0x19, 0x92, 0xdd, 0x7a, 0x4d, 0x1f, 0x9c, 0x53, 0xf3, 0xcf, + 0x44, 0xf5, 0x86, 0x7f, 0x14, 0xe7, 0xdb, 0x49, 0xfc, 0xc8, 0x9a, 0x16, 0x57, 0xfe, 0x64, 0x21, + 0x4b, 0x8e, 0xf8, 0x9e, 0x41, 0x4e, 0xf7, 0x00, 0xef, 0x27, 0xed, 0x2e, 0x56, 0xce, 0xda, 0xfd, + 0xa7, 0x4e, 0x67, 0x73, 0x8a, 0x1f, 0xbe, 0xff, 0xfe, 0xfc, 0x5f, 0x01, 0xb7, 0xbc, 0x58, 0xb5, + 0xdf, 0x1c, 0xc5, 0xea, 0x8f, 0xdf, 0x18, 0xac, 0xc7, 0x3a, 0x81, 0xe5, 0x54, 0x42, 0x52, 0x2b, + 0x2d, 0x77, 0x59, 0xb9, 0xc9, 0x55, 0xa6, 0x5c, 0xbb, 0x58, 0x4c, 0xc9, 0x45, 0xcf, 0xb3, 0x69, + 0x2f, 0xf1, 0x0b, 0x83, 0xb5, 0x48, 0x81, 0xb0, 0x94, 0x8a, 0x5b, 0x6c, 0xae, 0x75, 0xb0, 0x9c, + 0xd8, 0x24, 0x2b, 0x51, 0xb2, 0x22, 0xee, 0xa4, 0x24, 0xf3, 0x69, 0xe7, 0x8c, 0x0e, 0xf8, 0x91, + 0xc1, 0xea, 0xac, 0x5a, 0xb8, 0x97, 0x0a, 0x9a, 0x6f, 0xb8, 0xb5, 0xbf, 0x8c, 0xd4, 0x24, 0xda, + 0xa3, 0x44, 0x3b, 0xb8, 0x9d, 0x92, 0x68, 0xf2, 0x79, 0x98, 0x3c, 0x5f, 0x19, 0xc0, 0x75, 0xd5, + 0x30, 0x9d, 0xb2, 0x50, 0x7b, 0xab, 0xb4, 0x94, 0xd6, 0x44, 0xaa, 0x50, 0xa4, 0x03, 0xdc, 0x4f, + 0x89, 0x44, 0xdf, 0x8c, 0xce, 0xe4, 0xbd, 0xa5, 0xc3, 0xbb, 0xe3, 0xea, 0xe5, 0xc8, 0x66, 0x57, + 0x23, 0x9b, 0xfd, 0x1a, 0xd9, 0xec, 0xd3, 0xd8, 0xce, 0x5c, 0x8d, 0xed, 0xcc, 0x8f, 0xb1, 0x9d, + 0x79, 0xf5, 0xc8, 0x6f, 0xab, 0xd6, 0xa0, 0xee, 0x36, 0x64, 0x77, 0xde, 0xaf, 0xdc, 0x68, 0xf1, + 0x76, 0xcf, 0x9b, 0x4d, 0x2e, 0x0c, 0x40, 0x0d, 0x03, 0x11, 0xd6, 0x73, 0x34, 0x3f, 0xfc, 0x1b, + 0x00, 0x00, 0xff, 0xff, 0x97, 0x67, 0xc3, 0xb2, 0x41, 0x06, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -435,6 +531,8 @@ type QueryClient interface { GlobalStats(ctx context.Context, in *QueryGlobalStatsRequest, opts ...grpc.CallOption) (*QueryGlobalStatsResponse, error) // Queries UserStats. UserStats(ctx context.Context, in *QueryUserStatsRequest, opts ...grpc.CallOption) (*QueryUserStatsResponse, error) + // Queries EpochStats for a specific epoch. + EpochStats(ctx context.Context, in *QueryEpochStatsRequest, opts ...grpc.CallOption) (*QueryEpochStatsResponse, error) } type queryClient struct { @@ -481,6 +579,15 @@ func (c *queryClient) UserStats(ctx context.Context, in *QueryUserStatsRequest, return out, nil } +func (c *queryClient) EpochStats(ctx context.Context, in *QueryEpochStatsRequest, opts ...grpc.CallOption) (*QueryEpochStatsResponse, error) { + out := new(QueryEpochStatsResponse) + err := c.cc.Invoke(ctx, "/dydxprotocol.stats.Query/EpochStats", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // QueryServer is the server API for Query service. type QueryServer interface { // Queries the Params. @@ -491,6 +598,8 @@ type QueryServer interface { GlobalStats(context.Context, *QueryGlobalStatsRequest) (*QueryGlobalStatsResponse, error) // Queries UserStats. UserStats(context.Context, *QueryUserStatsRequest) (*QueryUserStatsResponse, error) + // Queries EpochStats for a specific epoch. + EpochStats(context.Context, *QueryEpochStatsRequest) (*QueryEpochStatsResponse, error) } // UnimplementedQueryServer can be embedded to have forward compatible implementations. @@ -509,6 +618,9 @@ func (*UnimplementedQueryServer) GlobalStats(ctx context.Context, req *QueryGlob func (*UnimplementedQueryServer) UserStats(ctx context.Context, req *QueryUserStatsRequest) (*QueryUserStatsResponse, error) { return nil, status.Errorf(codes.Unimplemented, "method UserStats not implemented") } +func (*UnimplementedQueryServer) EpochStats(ctx context.Context, req *QueryEpochStatsRequest) (*QueryEpochStatsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method EpochStats not implemented") +} func RegisterQueryServer(s grpc1.Server, srv QueryServer) { s.RegisterService(&_Query_serviceDesc, srv) @@ -586,6 +698,24 @@ func _Query_UserStats_Handler(srv interface{}, ctx context.Context, dec func(int return interceptor(ctx, in, info, handler) } +func _Query_EpochStats_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryEpochStatsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).EpochStats(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dydxprotocol.stats.Query/EpochStats", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).EpochStats(ctx, req.(*QueryEpochStatsRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _Query_serviceDesc = grpc.ServiceDesc{ ServiceName: "dydxprotocol.stats.Query", HandlerType: (*QueryServer)(nil), @@ -606,6 +736,10 @@ var _Query_serviceDesc = grpc.ServiceDesc{ MethodName: "UserStats", Handler: _Query_UserStats_Handler, }, + { + MethodName: "EpochStats", + Handler: _Query_EpochStats_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "dydxprotocol/stats/query.proto", @@ -848,6 +982,69 @@ func (m *QueryUserStatsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) return len(dAtA) - i, nil } +func (m *QueryEpochStatsRequest) 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 *QueryEpochStatsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryEpochStatsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Epoch != 0 { + i = encodeVarintQuery(dAtA, i, uint64(m.Epoch)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *QueryEpochStatsResponse) 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 *QueryEpochStatsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryEpochStatsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Stats != nil { + { + size, err := m.Stats.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { offset -= sovQuery(v) base := offset @@ -949,6 +1146,31 @@ func (m *QueryUserStatsResponse) Size() (n int) { return n } +func (m *QueryEpochStatsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Epoch != 0 { + n += 1 + sovQuery(uint64(m.Epoch)) + } + return n +} + +func (m *QueryEpochStatsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Stats != nil { + l = m.Stats.Size() + n += 1 + l + sovQuery(uint64(l)) + } + return n +} + func sovQuery(x uint64) (n int) { return (math_bits.Len64(x|1) + 6) / 7 } @@ -1528,6 +1750,161 @@ func (m *QueryUserStatsResponse) Unmarshal(dAtA []byte) error { } return nil } +func (m *QueryEpochStatsRequest) 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 ErrIntOverflowQuery + } + 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: QueryEpochStatsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryEpochStatsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Epoch", wireType) + } + m.Epoch = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Epoch |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryEpochStatsResponse) 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 ErrIntOverflowQuery + } + 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: QueryEpochStatsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryEpochStatsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Stats", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.Stats == nil { + m.Stats = &EpochStats{} + } + if err := m.Stats.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func skipQuery(dAtA []byte) (n int, err error) { l := len(dAtA) iNdEx := 0 diff --git a/protocol/x/stats/types/query.pb.gw.go b/protocol/x/stats/types/query.pb.gw.go index b736721c7d..c1c06708ab 100644 --- a/protocol/x/stats/types/query.pb.gw.go +++ b/protocol/x/stats/types/query.pb.gw.go @@ -123,6 +123,60 @@ func local_request_Query_UserStats_0(ctx context.Context, marshaler runtime.Mars } +func request_Query_EpochStats_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryEpochStatsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["epoch"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "epoch") + } + + protoReq.Epoch, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "epoch", err) + } + + msg, err := client.EpochStats(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_EpochStats_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryEpochStatsRequest + var metadata runtime.ServerMetadata + + var ( + val string + ok bool + err error + _ = err + ) + + val, ok = pathParams["epoch"] + if !ok { + return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "epoch") + } + + protoReq.Epoch, err = runtime.Uint32(val) + + if err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "epoch", err) + } + + msg, err := server.EpochStats(ctx, &protoReq) + return msg, metadata, err + +} + // RegisterQueryHandlerServer registers the http handlers for service Query to "mux". // UnaryRPC :call QueryServer directly. // StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. @@ -221,6 +275,29 @@ func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, serv }) + mux.Handle("GET", pattern_Query_EpochStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_EpochStats_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_EpochStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -342,6 +419,26 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie }) + mux.Handle("GET", pattern_Query_EpochStats_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_EpochStats_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_EpochStats_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + return nil } @@ -353,6 +450,8 @@ var ( pattern_Query_GlobalStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"dydxprotocol", "v4", "stats", "global_stats"}, "", runtime.AssumeColonVerbOpt(false))) pattern_Query_UserStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"dydxprotocol", "v4", "stats", "user_stats"}, "", runtime.AssumeColonVerbOpt(false))) + + pattern_Query_EpochStats_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"dydxprotocol", "v4", "stats", "epoch_stats", "epoch"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( @@ -363,4 +462,6 @@ var ( forward_Query_GlobalStats_0 = runtime.ForwardResponseMessage forward_Query_UserStats_0 = runtime.ForwardResponseMessage + + forward_Query_EpochStats_0 = runtime.ForwardResponseMessage ) From d9d41f2636cfc7e4fe81ea45f450c81d573d6983 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 25 Nov 2025 14:59:38 -0500 Subject: [PATCH 04/14] Increase ShortBlockWindow to 30 due to decreased blocktimes (backport #3226) (#3262) Signed-off-by: Shrenuj Bansal Co-authored-by: shrenujb <98204323+shrenujb@users.noreply.github.com> Co-authored-by: Kefan Cao --- protocol/cmd/dydxprotocold/cmd/config.go | 4 ++-- protocol/x/clob/e2e/app_test.go | 12 ++++++------ protocol/x/clob/e2e/rate_limit_test.go | 6 +++--- protocol/x/clob/types/constants.go | 2 +- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/protocol/cmd/dydxprotocold/cmd/config.go b/protocol/cmd/dydxprotocold/cmd/config.go index b532455109..ff235a8927 100644 --- a/protocol/cmd/dydxprotocold/cmd/config.go +++ b/protocol/cmd/dydxprotocold/cmd/config.go @@ -101,8 +101,8 @@ func initTendermintConfig() *tmcfg.Config { // a users rate limit if the entry is evicted from the mempool cache as it would be possible for the transaction // to go through `CheckTx` again causing it to hit rate limit code against the users account. cfg.Mempool.CacheSize = 5000 * int(clobtypes.ShortBlockWindow) - cfg.Mempool.Size = 50000 - cfg.Mempool.TTLNumBlocks = 20 //nolint:staticcheck + cfg.Mempool.Size = 100000 + cfg.Mempool.TTLNumBlocks = 40 //nolint:staticcheck cfg.Mempool.KeepInvalidTxsInCache = true // Enable pex. diff --git a/protocol/x/clob/e2e/app_test.go b/protocol/x/clob/e2e/app_test.go index 0427a9c233..bf60a01905 100644 --- a/protocol/x/clob/e2e/app_test.go +++ b/protocol/x/clob/e2e/app_test.go @@ -78,13 +78,13 @@ var ( }, testapp.DefaultGenesis(), )) - PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB27 = *clobtypes.NewMsgPlaceOrder(testapp.MustScaleOrder( + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB47 = *clobtypes.NewMsgPlaceOrder(testapp.MustScaleOrder( clobtypes.Order{ OrderId: clobtypes.OrderId{SubaccountId: constants.Alice_Num0, ClientId: 0, ClobPairId: 0}, Side: clobtypes.Order_SIDE_BUY, Quantums: 5, Subticks: 10, - GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 27}, + GoodTilOneof: &clobtypes.Order_GoodTilBlock{GoodTilBlock: 47}, }, testapp.DefaultGenesis(), )) @@ -175,13 +175,13 @@ var ( }, 20, ) - CancelOrder_Alice_Num0_Id0_Clob0_GTB27 = *clobtypes.NewMsgCancelOrderShortTerm( + CancelOrder_Alice_Num0_Id0_Clob0_GTB47 = *clobtypes.NewMsgCancelOrderShortTerm( clobtypes.OrderId{ SubaccountId: constants.Alice_Num0, ClientId: 0, ClobPairId: 0, }, - 27, + 47, ) CancelOrder_Alice_Num0_Id0_Clob0_GTB23 = *clobtypes.NewMsgCancelOrderShortTerm( clobtypes.OrderId{ @@ -275,7 +275,7 @@ var ( }, 5, ) - BatchCancel_Alice_Num0_Clob0_1_2_3_GTB27 = *clobtypes.NewMsgBatchCancel( + BatchCancel_Alice_Num0_Clob0_1_2_3_GTB47 = *clobtypes.NewMsgBatchCancel( constants.Alice_Num0, []clobtypes.OrderBatch{ { @@ -283,7 +283,7 @@ var ( ClientIds: []uint32{1, 2, 3}, }, }, - 27, + 47, ) BatchCancel_Alice_Num0_Clob0_1_2_3_GTB20 = *clobtypes.NewMsgBatchCancel( constants.Alice_Num0, diff --git a/protocol/x/clob/e2e/rate_limit_test.go b/protocol/x/clob/e2e/rate_limit_test.go index 1c199c551f..cc998f671c 100644 --- a/protocol/x/clob/e2e/rate_limit_test.go +++ b/protocol/x/clob/e2e/rate_limit_test.go @@ -706,7 +706,7 @@ func TestRateLimitingShortTermOrders_GuardedAgainstReplayAttacks(t *testing.T) { }, }, replayLessGTB: &PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB5, - replayGreaterGTB: &PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB27, + replayGreaterGTB: &PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB47, firstValidGTB: &PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB20, secondValidGTB: &PlaceOrder_Alice_Num0_Id0_Clob1_Buy5_Price10_GTB20, }, @@ -720,7 +720,7 @@ func TestRateLimitingShortTermOrders_GuardedAgainstReplayAttacks(t *testing.T) { }, }, replayLessGTB: &CancelOrder_Alice_Num0_Id0_Clob0_GTB5, - replayGreaterGTB: &CancelOrder_Alice_Num0_Id0_Clob0_GTB27, + replayGreaterGTB: &CancelOrder_Alice_Num0_Id0_Clob0_GTB47, firstValidGTB: &CancelOrder_Alice_Num0_Id0_Clob0_GTB20, secondValidGTB: &CancelOrder_Alice_Num0_Id1_Clob0_GTB20, }, @@ -734,7 +734,7 @@ func TestRateLimitingShortTermOrders_GuardedAgainstReplayAttacks(t *testing.T) { }, }, replayLessGTB: &BatchCancel_Alice_Num0_Clob0_1_2_3_GTB5, - replayGreaterGTB: &BatchCancel_Alice_Num0_Clob0_1_2_3_GTB27, + replayGreaterGTB: &BatchCancel_Alice_Num0_Clob0_1_2_3_GTB47, firstValidGTB: &BatchCancel_Alice_Num0_Clob0_1_2_3_GTB20, secondValidGTB: &BatchCancel_Alice_Num0_Clob1_1_2_3_GTB20, }, diff --git a/protocol/x/clob/types/constants.go b/protocol/x/clob/types/constants.go index 069e7646e7..b3dbf79dc6 100644 --- a/protocol/x/clob/types/constants.go +++ b/protocol/x/clob/types/constants.go @@ -6,7 +6,7 @@ import ( // ShortBlockWindow represents the maximum number of blocks past the current block height that a // `MsgPlaceOrder` or `MsgCancelOrder` message will be considered valid by the validator. -const ShortBlockWindow uint32 = 20 +const ShortBlockWindow uint32 = 40 // MaxMsgBatchCancelBatchSize represents the maximum number of cancels that a MsgBatchCancel // can have in one Msg. From f887bfc1b17ce0af71408d2e7ad9407202743cb7 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 2 Dec 2025 10:47:51 -0500 Subject: [PATCH 05/14] 9.5 upgrade & add underflow protection for referred volume (backport #3261) (#3271) Co-authored-by: Justin Barnett <61020572+jusbar23@users.noreply.github.com> --- protocol/app/upgrades.go | 15 +- .../upgrades/v9.4/upgrade_container_test.go | 89 ------- protocol/app/upgrades/v9.5/constants.go | 15 ++ protocol/app/upgrades/v9.5/upgrade.go | 127 ++++++++++ .../upgrades/v9.5/upgrade_container_test.go | 200 ++++++++++++++++ protocol/testing/version/VERSION_CURRENT | 2 +- .../version/VERSION_FULL_NAME_PREUPGRADE | 2 +- protocol/testing/version/VERSION_PREUPGRADE | 2 +- protocol/x/stats/keeper/keeper.go | 43 +++- protocol/x/stats/keeper/keeper_test.go | 225 ++++++++++++++++++ 10 files changed, 618 insertions(+), 102 deletions(-) delete mode 100644 protocol/app/upgrades/v9.4/upgrade_container_test.go create mode 100644 protocol/app/upgrades/v9.5/constants.go create mode 100644 protocol/app/upgrades/v9.5/upgrade.go create mode 100644 protocol/app/upgrades/v9.5/upgrade_container_test.go diff --git a/protocol/app/upgrades.go b/protocol/app/upgrades.go index 00725c3070..e264962e65 100644 --- a/protocol/app/upgrades.go +++ b/protocol/app/upgrades.go @@ -3,7 +3,7 @@ package app import ( "fmt" - v_9_4 "github.com/dydxprotocol/v4-chain/protocol/app/upgrades/v9.4" + v_9_5 "github.com/dydxprotocol/v4-chain/protocol/app/upgrades/v9.5" upgradetypes "cosmossdk.io/x/upgrade/types" sdk "github.com/cosmos/cosmos-sdk/types" @@ -14,7 +14,7 @@ var ( // `Upgrades` defines the upgrade handlers and store loaders for the application. // New upgrades should be added to this slice after they are implemented. Upgrades = []upgrades.Upgrade{ - v_9_4.Upgrade, + v_9_5.Upgrade, } Forks = []upgrades.Fork{} ) @@ -22,15 +22,16 @@ var ( // setupUpgradeHandlers registers the upgrade handlers to perform custom upgrade // logic and state migrations for software upgrades. func (app *App) setupUpgradeHandlers() { - if app.UpgradeKeeper.HasHandler(v_9_4.UpgradeName) { - panic(fmt.Sprintf("Cannot register duplicate upgrade handler '%s'", v_9_4.UpgradeName)) + if app.UpgradeKeeper.HasHandler(v_9_5.UpgradeName) { + panic(fmt.Sprintf("Cannot register duplicate upgrade handler '%s'", v_9_5.UpgradeName)) } app.UpgradeKeeper.SetUpgradeHandler( - v_9_4.UpgradeName, - v_9_4.CreateUpgradeHandler( + v_9_5.UpgradeName, + v_9_5.CreateUpgradeHandler( app.ModuleManager, - app.AffiliatesKeeper, app.configurator, + app.StatsKeeper, + app.EpochsKeeper, ), ) } diff --git a/protocol/app/upgrades/v9.4/upgrade_container_test.go b/protocol/app/upgrades/v9.4/upgrade_container_test.go deleted file mode 100644 index 53224db006..0000000000 --- a/protocol/app/upgrades/v9.4/upgrade_container_test.go +++ /dev/null @@ -1,89 +0,0 @@ -//go:build all || container_test - -package v_9_4_test - -import ( - "testing" - - "github.com/cosmos/gogoproto/proto" - v_9_4 "github.com/dydxprotocol/v4-chain/protocol/app/upgrades/v9.4" - "github.com/dydxprotocol/v4-chain/protocol/testing/containertest" - "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" - affiliatetypes "github.com/dydxprotocol/v4-chain/protocol/x/affiliates/types" - "github.com/stretchr/testify/require" -) - -func TestStateUpgrade(t *testing.T) { - testnet, err := containertest.NewTestnetWithPreupgradeGenesis() - require.NoError(t, err, "failed to create testnet - is docker daemon running?") - err = testnet.Start() - require.NoError(t, err) - defer testnet.MustCleanUp() - node := testnet.Nodes["alice"] - nodeAddress := constants.AliceAccAddress.String() - - preUpgradeSetups(node, t) - preUpgradeChecks(node, t) - - err = containertest.UpgradeTestnet(nodeAddress, t, node, v_9_4.UpgradeName) - require.NoError(t, err) - - postUpgradeChecks(node, t) -} - -func preUpgradeSetups(node *containertest.Node, t *testing.T) {} - -func preUpgradeChecks(node *containertest.Node, t *testing.T) { - // Verify affiliate tiers are set to default values - tiersResp := &affiliatetypes.AllAffiliateTiersResponse{} - resp, err := containertest.Query( - node, - affiliatetypes.NewQueryClient, - affiliatetypes.QueryClient.AllAffiliateTiers, - &affiliatetypes.AllAffiliateTiersRequest{}, - ) - require.NoError(t, err) - err = proto.UnmarshalText(resp.String(), tiersResp) - require.NoError(t, err) - require.Equal(t, v_9_4.PreviousAffilliateTiers, tiersResp.Tiers) -} - -func postUpgradeChecks(node *containertest.Node, t *testing.T) { - // Verify affiliate tiers are set to default values - tiersResp := &affiliatetypes.AllAffiliateTiersResponse{} - resp, err := containertest.Query( - node, - affiliatetypes.NewQueryClient, - affiliatetypes.QueryClient.AllAffiliateTiers, - &affiliatetypes.AllAffiliateTiersRequest{}, - ) - require.NoError(t, err) - err = proto.UnmarshalText(resp.String(), tiersResp) - require.NoError(t, err) - require.Equal(t, v_9_4.UpdatedAffiliateTiers, tiersResp.Tiers) - - // Verify affiliate parameters are set to default values - paramsResp := &affiliatetypes.AffiliateParametersResponse{} - resp, err = containertest.Query( - node, - affiliatetypes.NewQueryClient, - affiliatetypes.QueryClient.AffiliateParameters, - &affiliatetypes.AffiliateParametersRequest{}, - ) - require.NoError(t, err) - err = proto.UnmarshalText(resp.String(), paramsResp) - require.NoError(t, err) - require.Equal(t, v_9_4.UpdatedAffiliateParameters, paramsResp.Parameters) - - // Verify affiliate overrides were migrated from whitelist - overridesResp := &affiliatetypes.AffiliateOverridesResponse{} - resp, err = containertest.Query( - node, - affiliatetypes.NewQueryClient, - affiliatetypes.QueryClient.AffiliateOverrides, - &affiliatetypes.AffiliateOverridesRequest{}, - ) - require.NoError(t, err) - err = proto.UnmarshalText(resp.String(), overridesResp) - require.NoError(t, err) -} diff --git a/protocol/app/upgrades/v9.5/constants.go b/protocol/app/upgrades/v9.5/constants.go new file mode 100644 index 0000000000..7170949ab0 --- /dev/null +++ b/protocol/app/upgrades/v9.5/constants.go @@ -0,0 +1,15 @@ +package v_9_5 + +import ( + store "cosmossdk.io/store/types" + "github.com/dydxprotocol/v4-chain/protocol/app/upgrades" +) + +const ( + UpgradeName = "v9.5" +) + +var Upgrade = upgrades.Upgrade{ + UpgradeName: UpgradeName, + StoreUpgrades: store.StoreUpgrades{}, +} diff --git a/protocol/app/upgrades/v9.5/upgrade.go b/protocol/app/upgrades/v9.5/upgrade.go new file mode 100644 index 0000000000..a8cd1f03d1 --- /dev/null +++ b/protocol/app/upgrades/v9.5/upgrade.go @@ -0,0 +1,127 @@ +package v_9_5 + +import ( + "context" + "fmt" + "sort" + + upgradetypes "cosmossdk.io/x/upgrade/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + + "github.com/dydxprotocol/v4-chain/protocol/lib" + epochskeeper "github.com/dydxprotocol/v4-chain/protocol/x/epochs/keeper" + statskeeper "github.com/dydxprotocol/v4-chain/protocol/x/stats/keeper" + statstypes "github.com/dydxprotocol/v4-chain/protocol/x/stats/types" +) + +// Migrate30dReferredVolumeToEpochStats migrates all users' 30d referred volume +// to epoch stats in the current epoch. +func Migrate30dReferredVolumeToEpochStats( + ctx sdk.Context, + statsKeeper statskeeper.Keeper, + epochsKeeper epochskeeper.Keeper, +) { + // Get the current stats epoch + statsEpochInfo := epochsKeeper.MustGetStatsEpochInfo(ctx) + currentEpoch := statsEpochInfo.CurrentEpoch + + ctx.Logger().Info(fmt.Sprintf( + "Migrating 30d referred volume to epoch stats for epoch %d", + currentEpoch, + )) + + // Get or create epoch stats for current epoch + epochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch) + if epochStats == nil { + epochStats = &statstypes.EpochStats{ + Stats: []*statstypes.EpochStats_UserWithStats{}, + } + } + + // Create a map for existing epoch stats for quick lookup + userStatsMap := make(map[string]*statstypes.EpochStats_UserWithStats) + for _, userWithStats := range epochStats.Stats { + userStatsMap[userWithStats.User] = userWithStats + } + + // Get all addresses with referred volume from the global UserStats + allAddressesWithReferredVolume := statsKeeper.GetAllAddressesWithReferredVolume(ctx) + + migratedCount := 0 + + for _, address := range allAddressesWithReferredVolume { + // Get the global user stats which contains the 30d referred volume + globalUserStats := statsKeeper.GetUserStats(ctx, address) + if globalUserStats == nil { + continue + } + + referredVolume := globalUserStats.Affiliate_30DReferredVolumeQuoteQuantums + + // Get or create user stats for this epoch + epochUserStats, exists := userStatsMap[address] + if !exists { + // User not in epoch stats yet, create new entry + epochUserStats = &statstypes.EpochStats_UserWithStats{ + User: address, + Stats: &statstypes.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: referredVolume, + }, + } + userStatsMap[address] = epochUserStats + } else { + // User already in epoch stats, add the referred volume + epochUserStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums += referredVolume + } + + migratedCount++ + + ctx.Logger().Info(fmt.Sprintf( + "Migrated referred volume for address %s (%d of %d): Affiliate_30DReferredVolumeQuoteQuantums=%d", + address, + migratedCount, + len(allAddressesWithReferredVolume), + referredVolume, + )) + } + + // Convert map back to slice - must be deterministic to avoid state hash mismatch + keys := make([]string, 0, len(userStatsMap)) + for k := range userStatsMap { + keys = append(keys, k) + } + sort.Strings(keys) + epochStats.Stats = make([]*statstypes.EpochStats_UserWithStats, 0, len(userStatsMap)) + for _, k := range keys { + epochStats.Stats = append(epochStats.Stats, userStatsMap[k]) + } + + // Save the updated epoch stats + statsKeeper.SetEpochStats(ctx, currentEpoch, epochStats) + + ctx.Logger().Info(fmt.Sprintf( + "Successfully migrated 30d referred volume for %d addresses to epoch %d", + migratedCount, + currentEpoch, + )) +} + +func CreateUpgradeHandler( + mm *module.Manager, + configurator module.Configurator, + statsKeeper statskeeper.Keeper, + epochsKeeper epochskeeper.Keeper, +) upgradetypes.UpgradeHandler { + return func(ctx context.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { + sdkCtx := lib.UnwrapSDKContext(ctx, "app/upgrades") + sdkCtx.Logger().Info(fmt.Sprintf("Running %s Upgrade...", UpgradeName)) + + // Migrate 30d referred volume to epoch stats + Migrate30dReferredVolumeToEpochStats(sdkCtx, statsKeeper, epochsKeeper) + + sdkCtx.Logger().Info(fmt.Sprintf("Successfully completed %s Upgrade", UpgradeName)) + + return mm.RunMigrations(ctx, configurator, vm) + } +} diff --git a/protocol/app/upgrades/v9.5/upgrade_container_test.go b/protocol/app/upgrades/v9.5/upgrade_container_test.go new file mode 100644 index 0000000000..bd07514152 --- /dev/null +++ b/protocol/app/upgrades/v9.5/upgrade_container_test.go @@ -0,0 +1,200 @@ +package v_9_5_test + +import ( + "testing" + + "github.com/stretchr/testify/require" + + v_9_5 "github.com/dydxprotocol/v4-chain/protocol/app/upgrades/v9.5" + "github.com/dydxprotocol/v4-chain/protocol/testing/containertest" + testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" + statstypes "github.com/dydxprotocol/v4-chain/protocol/x/stats/types" +) + +func TestMigrate30dReferredVolumeToEpochStats(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + tApp.InitChain() + + statsKeeper := tApp.App.StatsKeeper + epochsKeeper := tApp.App.EpochsKeeper + + // Advance to the next epoch so we have a current epoch with some activity + ctx := tApp.AdvanceToBlock(10, testapp.AdvanceToBlockOptions{}) + + // Query epochs info to get current epoch + statsEpochInfo := epochsKeeper.MustGetStatsEpochInfo(ctx) + currentEpoch := statsEpochInfo.CurrentEpoch + + // Create some initial epoch stats (simulating existing trading activity) + initialEpochStats := &statstypes.EpochStats{ + Stats: []*statstypes.EpochStats_UserWithStats{ + { + User: constants.AliceAccAddress.String(), + Stats: &statstypes.UserStats{ + TakerNotional: 50, + MakerNotional: 75, + // No referred volume set yet + }, + }, + { + User: constants.BobAccAddress.String(), + Stats: &statstypes.UserStats{ + TakerNotional: 100, + MakerNotional: 150, + // No referred volume set yet + }, + }, + }, + } + statsKeeper.SetEpochStats(ctx, currentEpoch, initialEpochStats) + + // Set up global user stats with referred volume (this represents the 30d cumulative volume) + testUsers := []struct { + address string + referredVolume uint64 + }{ + {constants.AliceAccAddress.String(), 1_000_000_000}, // 1k volume + {constants.BobAccAddress.String(), 5_000_000_000}, // 5k volume + {constants.CarlAccAddress.String(), 10_000_000_000}, // 10k volume + {constants.DaveAccAddress.String(), 0}, // no referred volume + } + + for _, user := range testUsers { + userStats := &statstypes.UserStats{ + TakerNotional: 100, + MakerNotional: 200, + Affiliate_30DReferredVolumeQuoteQuantums: user.referredVolume, + } + statsKeeper.SetUserStats(ctx, user.address, userStats) + } + + // Verify initial state - epoch stats should not have referred volume + preUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch) + require.NotNil(t, preUpgradeEpochStats) + for _, userStats := range preUpgradeEpochStats.Stats { + require.Equal(t, uint64(0), userStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums, + "Referred volume should be 0 before migration for user %s", userStats.User) + } + + // Run the migration function directly + v_9_5.Migrate30dReferredVolumeToEpochStats(ctx, statsKeeper, epochsKeeper) + + // Verify migration results + postUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch) + require.NotNil(t, postUpgradeEpochStats) + + // Create a map for easier verification + epochStatsMap := make(map[string]*statstypes.EpochStats_UserWithStats) + for _, userStats := range postUpgradeEpochStats.Stats { + epochStatsMap[userStats.User] = userStats + } + + // Verify Alice's referred volume was migrated + aliceStats, exists := epochStatsMap[constants.AliceAccAddress.String()] + require.True(t, exists, "Alice should exist in epoch stats") + require.Equal(t, uint64(1_000_000_000), aliceStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums, + "Alice's referred volume should be migrated") + require.Equal(t, uint64(50), aliceStats.Stats.TakerNotional, + "Alice's taker notional should be preserved") + require.Equal(t, uint64(75), aliceStats.Stats.MakerNotional, + "Alice's maker notional should be preserved") + + // Verify Bob's referred volume was migrated + bobStats, exists := epochStatsMap[constants.BobAccAddress.String()] + require.True(t, exists, "Bob should exist in epoch stats") + require.Equal(t, uint64(5_000_000_000), bobStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums, + "Bob's referred volume should be migrated") + require.Equal(t, uint64(100), bobStats.Stats.TakerNotional, + "Bob's taker notional should be preserved") + require.Equal(t, uint64(150), bobStats.Stats.MakerNotional, + "Bob's maker notional should be preserved") + + // Verify Carl is NOW in epoch stats (even though he wasn't trading, he has referred volume) + carlStats, carlExists := epochStatsMap[constants.CarlAccAddress.String()] + require.True(t, carlExists, "Carl should be in epoch stats because he has referred volume") + require.Equal(t, uint64(10_000_000_000), carlStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums, + "Carl's referred volume should be migrated") + require.Equal(t, uint64(0), carlStats.Stats.TakerNotional, + "Carl's taker notional should be 0 (he wasn't trading)") + require.Equal(t, uint64(0), carlStats.Stats.MakerNotional, + "Carl's maker notional should be 0 (he wasn't trading)") + + // Verify Dave is NOT in epoch stats (he has no referred volume) + _, daveExists := epochStatsMap[constants.DaveAccAddress.String()] + require.False(t, daveExists, "Dave should not be in epoch stats as he has no referred volume") +} + +func TestMigrate30dReferredVolumeToEpochStats_EmptyEpochStats(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + tApp.InitChain() + + statsKeeper := tApp.App.StatsKeeper + epochsKeeper := tApp.App.EpochsKeeper + + // Advance to next epoch + ctx := tApp.AdvanceToBlock(10, testapp.AdvanceToBlockOptions{}) + + // Query epochs info to get current epoch + statsEpochInfo := epochsKeeper.MustGetStatsEpochInfo(ctx) + currentEpoch := statsEpochInfo.CurrentEpoch + + // Setup: Create user stats with referred volume but no epoch stats + userStats := &statstypes.UserStats{ + TakerNotional: 100, + MakerNotional: 200, + Affiliate_30DReferredVolumeQuoteQuantums: 1_000_000_000, + } + statsKeeper.SetUserStats(ctx, constants.AliceAccAddress.String(), userStats) + + // Verify no epoch stats exist initially + preUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch) + require.Nil(t, preUpgradeEpochStats) + + // Run the migration function directly + v_9_5.Migrate30dReferredVolumeToEpochStats(ctx, statsKeeper, epochsKeeper) + + // Verify Alice was added to epoch stats even though she wasn't trading + postUpgradeEpochStats := statsKeeper.GetEpochStatsOrNil(ctx, currentEpoch) + require.NotNil(t, postUpgradeEpochStats) + require.Len(t, postUpgradeEpochStats.Stats, 1, + "Alice should be added to epoch stats because she has referred volume") + + aliceStats := postUpgradeEpochStats.Stats[0] + require.Equal(t, constants.AliceAccAddress.String(), aliceStats.User) + require.Equal(t, uint64(1_000_000_000), aliceStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums, + "Alice's referred volume should be migrated") + require.Equal(t, uint64(0), aliceStats.Stats.TakerNotional, + "Alice's taker notional should be 0 (she wasn't trading)") + require.Equal(t, uint64(0), aliceStats.Stats.MakerNotional, + "Alice's maker notional should be 0 (she wasn't trading)") +} + +func TestStateUpgrade(t *testing.T) { + testnet, err := containertest.NewTestnetWithPreupgradeGenesis() + require.NoError(t, err, "failed to create testnet - is docker daemon running?") + err = testnet.Start() + require.NoError(t, err) + defer testnet.MustCleanUp() + node := testnet.Nodes["alice"] + nodeAddress := constants.AliceAccAddress.String() + + preUpgradeSetups(node, t) + preUpgradeChecks(node, t) + + err = containertest.UpgradeTestnet(nodeAddress, t, node, v_9_5.UpgradeName) + require.NoError(t, err) + + postUpgradeChecks(node, t) +} + +func preUpgradeSetups(node *containertest.Node, t *testing.T) { + // Set up user stats with referred volume before upgrade + // This simulates users having 30d referred volume in global stats +} + +func preUpgradeChecks(node *containertest.Node, t *testing.T) { +} + +func postUpgradeChecks(node *containertest.Node, t *testing.T) { +} diff --git a/protocol/testing/version/VERSION_CURRENT b/protocol/testing/version/VERSION_CURRENT index d3b4227d55..1909a1b474 100644 --- a/protocol/testing/version/VERSION_CURRENT +++ b/protocol/testing/version/VERSION_CURRENT @@ -1 +1 @@ -v9.4 \ No newline at end of file +v9.5 \ No newline at end of file diff --git a/protocol/testing/version/VERSION_FULL_NAME_PREUPGRADE b/protocol/testing/version/VERSION_FULL_NAME_PREUPGRADE index 934167e7e2..cf8b7743ae 100644 --- a/protocol/testing/version/VERSION_FULL_NAME_PREUPGRADE +++ b/protocol/testing/version/VERSION_FULL_NAME_PREUPGRADE @@ -1 +1 @@ -v9.3.0 \ No newline at end of file +v9.4.0 \ No newline at end of file diff --git a/protocol/testing/version/VERSION_PREUPGRADE b/protocol/testing/version/VERSION_PREUPGRADE index a26c2c72d5..d3b4227d55 100644 --- a/protocol/testing/version/VERSION_PREUPGRADE +++ b/protocol/testing/version/VERSION_PREUPGRADE @@ -1 +1 @@ -v9.3 \ No newline at end of file +v9.4 \ No newline at end of file diff --git a/protocol/x/stats/keeper/keeper.go b/protocol/x/stats/keeper/keeper.go index e55c3033cc..766c76f4d8 100644 --- a/protocol/x/stats/keeper/keeper.go +++ b/protocol/x/stats/keeper/keeper.go @@ -331,9 +331,26 @@ func (k Keeper) ExpireOldStats(ctx sdk.Context) { stats := k.GetUserStats(ctx, removedStats.User) stats.TakerNotional -= removedStats.Stats.TakerNotional stats.MakerNotional -= removedStats.Stats.MakerNotional - stats.Affiliate_30DRevenueGeneratedQuantums -= removedStats.Stats.Affiliate_30DRevenueGeneratedQuantums - stats.Affiliate_30DReferredVolumeQuoteQuantums -= removedStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums - stats.Affiliate_30DAttributedVolumeQuoteQuantums -= removedStats.Stats.Affiliate_30DAttributedVolumeQuoteQuantums + // Clamp affiliate_30drevenue at 0 to prevent underflow (must compare before subtracting for uint64) + if stats.Affiliate_30DRevenueGeneratedQuantums > removedStats.Stats.Affiliate_30DRevenueGeneratedQuantums { + stats.Affiliate_30DRevenueGeneratedQuantums -= removedStats.Stats.Affiliate_30DRevenueGeneratedQuantums + } else { + stats.Affiliate_30DRevenueGeneratedQuantums = 0 + } + + // Clamp affiliate fields at 0 to prevent underflow (must compare before subtracting for uint64) + if stats.Affiliate_30DReferredVolumeQuoteQuantums > removedStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums { + stats.Affiliate_30DReferredVolumeQuoteQuantums -= removedStats.Stats.Affiliate_30DReferredVolumeQuoteQuantums + } else { + stats.Affiliate_30DReferredVolumeQuoteQuantums = 0 + } + + if stats.Affiliate_30DAttributedVolumeQuoteQuantums > removedStats.Stats.Affiliate_30DAttributedVolumeQuoteQuantums { + stats.Affiliate_30DAttributedVolumeQuoteQuantums -= removedStats.Stats.Affiliate_30DAttributedVolumeQuoteQuantums + } else { + stats.Affiliate_30DAttributedVolumeQuoteQuantums = 0 + } + k.SetUserStats(ctx, removedStats.User, stats) // Just remove TakerNotional to avoid double counting @@ -439,3 +456,23 @@ func (k Keeper) UnsafeSetCachedStakedBaseTokens(ctx sdk.Context, delegatorAddr s store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.CachedStakeAmountKeyPrefix)) store.Set([]byte(delegatorAddr), k.cdc.MustMarshal(cachedStakedBaseTokens)) } + +// GetAllAddressesWithReferredVolume returns all addresses that have non-zero referred volume. +// This is useful for migrations. +func (k Keeper) GetAllAddressesWithReferredVolume(ctx sdk.Context) []string { + store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.UserStatsKeyPrefix)) + iterator := store.Iterator(nil, nil) + defer iterator.Close() + + addresses := make([]string, 0) + for ; iterator.Valid(); iterator.Next() { + var userStats types.UserStats + k.cdc.MustUnmarshal(iterator.Value(), &userStats) + + if userStats.Affiliate_30DReferredVolumeQuoteQuantums > 0 { + addresses = append(addresses, string(iterator.Key())) + } + } + + return addresses +} diff --git a/protocol/x/stats/keeper/keeper_test.go b/protocol/x/stats/keeper/keeper_test.go index a5477c9431..26b675f3be 100644 --- a/protocol/x/stats/keeper/keeper_test.go +++ b/protocol/x/stats/keeper/keeper_test.go @@ -994,3 +994,228 @@ func TestGetStakedBaseTokens_Cache_Miss(t *testing.T) { receivedCoins := statsKeeper.GetStakedBaseTokens(ctx, constants.AliceAccAddress.String()) require.Equal(t, latestCoinsToStakeQuantums, receivedCoins) } + +// TestExpireOldStats_UnderflowProtection tests that affiliate fields are properly +// clamped to 0 when expiring epochs would cause underflow due to corrupted/inconsistent data. +func TestExpireOldStats_UnderflowProtection(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + + // Epochs start at block height 2 + ctx := tApp.AdvanceToBlock(2, testapp.AdvanceToBlockOptions{ + BlockTime: time.Unix(int64(1), 0).UTC(), + }) + windowDuration := tApp.App.StatsKeeper.GetWindowDuration(ctx) + + // Advance time so epochs can expire + tApp.AdvanceToBlock(3, testapp.AdvanceToBlockOptions{ + BlockTime: time.Unix(0, 0). + Add(windowDuration). + Add((time.Duration(2*epochstypes.StatsEpochDuration) + 1) * time.Second). + UTC(), + }) + ctx = tApp.AdvanceToBlock(100, testapp.AdvanceToBlockOptions{}) + k := tApp.App.StatsKeeper + + // Simulate a scenario where epoch stats have MORE volume than user's current stats + // This could happen after the v9.5 migration or other data inconsistencies + + // Create epoch 0 with large affiliate volumes + k.SetEpochStats(ctx, 0, &types.EpochStats{ + EpochEndTime: time.Unix(0, 0).UTC(), + Stats: []*types.EpochStats_UserWithStats{ + { + User: "alice", + Stats: &types.UserStats{ + TakerNotional: 100, + MakerNotional: 200, + Affiliate_30DReferredVolumeQuoteQuantums: 1_000_000_000_000, // 1M volume in epoch + Affiliate_30DAttributedVolumeQuoteQuantums: 500_000_000_000, // 500k attributed + }, + }, + }, + }) + + // Set user stats with LESS than what's in the epoch + // This simulates corrupted/inconsistent state + k.SetUserStats(ctx, "alice", &types.UserStats{ + TakerNotional: 50, // Less than epoch + MakerNotional: 100, // Less than epoch + Affiliate_30DReferredVolumeQuoteQuantums: 100_000_000_000, // Only 100k, but epoch has 1M + Affiliate_30DAttributedVolumeQuoteQuantums: 50_000_000_000, // Only 50k, but epoch has 500k + }) + + k.SetGlobalStats(ctx, &types.GlobalStats{ + NotionalTraded: 150, + }) + + k.SetStatsMetadata(ctx, &types.StatsMetadata{ + TrailingEpoch: 0, + }) + + // Expire the epoch - this should NOT cause underflow + k.ExpireOldStats(ctx) + + // Verify that affiliate fields are clamped to 0, not wrapped around to huge numbers + aliceStats := k.GetUserStats(ctx, "alice") + + // TakerNotional and MakerNotional can go negative (they wrap around for uint64) + // But we're testing that the affiliate fields with underflow protection work correctly + + // These fields should be clamped to 0, not underflow + require.Equal(t, uint64(0), aliceStats.Affiliate_30DReferredVolumeQuoteQuantums, + "Referred volume should be clamped to 0, not underflow") + require.Equal(t, uint64(0), aliceStats.Affiliate_30DAttributedVolumeQuoteQuantums, + "Attributed volume should be clamped to 0, not underflow") + + // Verify the values aren't huge wrapped-around numbers + // If underflow occurred, these would be close to uint64 max (18446744073709551615) + require.Less(t, aliceStats.Affiliate_30DReferredVolumeQuoteQuantums, uint64(1000), + "Referred volume should not be a wrapped-around huge number") + require.Less(t, aliceStats.Affiliate_30DAttributedVolumeQuoteQuantums, uint64(1000), + "Attributed volume should not be a wrapped-around huge number") +} + +// TestExpireOldStats_UnderflowProtection_EdgeCase tests the exact boundary case +func TestExpireOldStats_UnderflowProtection_EdgeCase(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + + ctx := tApp.AdvanceToBlock(2, testapp.AdvanceToBlockOptions{ + BlockTime: time.Unix(int64(1), 0).UTC(), + }) + windowDuration := tApp.App.StatsKeeper.GetWindowDuration(ctx) + + tApp.AdvanceToBlock(3, testapp.AdvanceToBlockOptions{ + BlockTime: time.Unix(0, 0). + Add(windowDuration). + Add((time.Duration(2*epochstypes.StatsEpochDuration) + 1) * time.Second). + UTC(), + }) + ctx = tApp.AdvanceToBlock(100, testapp.AdvanceToBlockOptions{}) + k := tApp.App.StatsKeeper + + // Test exact boundary: user has exactly the same amount as epoch + k.SetEpochStats(ctx, 0, &types.EpochStats{ + EpochEndTime: time.Unix(0, 0).UTC(), + Stats: []*types.EpochStats_UserWithStats{ + { + User: "bob", + Stats: &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 1000, + Affiliate_30DAttributedVolumeQuoteQuantums: 500, + }, + }, + }, + }) + + k.SetUserStats(ctx, "bob", &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 1000, // Exactly the same + Affiliate_30DAttributedVolumeQuoteQuantums: 500, // Exactly the same + }) + + k.SetStatsMetadata(ctx, &types.StatsMetadata{ + TrailingEpoch: 0, + }) + + k.ExpireOldStats(ctx) + + bobStats := k.GetUserStats(ctx, "bob") + + // Should be exactly 0 after subtracting equal amounts + require.Equal(t, uint64(0), bobStats.Affiliate_30DReferredVolumeQuoteQuantums, + "Should be exactly 0 after subtracting equal amounts") + require.Equal(t, uint64(0), bobStats.Affiliate_30DAttributedVolumeQuoteQuantums, + "Should be exactly 0 after subtracting equal amounts") +} + +// TestExpireOldStats_UnderflowProtection_MultipleUsers tests that underflow +// protection works correctly when multiple users have inconsistent data +func TestExpireOldStats_UnderflowProtection_MultipleUsers(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + + ctx := tApp.AdvanceToBlock(2, testapp.AdvanceToBlockOptions{ + BlockTime: time.Unix(int64(1), 0).UTC(), + }) + windowDuration := tApp.App.StatsKeeper.GetWindowDuration(ctx) + + tApp.AdvanceToBlock(3, testapp.AdvanceToBlockOptions{ + BlockTime: time.Unix(0, 0). + Add(windowDuration). + Add((time.Duration(2*epochstypes.StatsEpochDuration) + 1) * time.Second). + UTC(), + }) + ctx = tApp.AdvanceToBlock(100, testapp.AdvanceToBlockOptions{}) + k := tApp.App.StatsKeeper + + // Create epoch with multiple users having different underflow scenarios + k.SetEpochStats(ctx, 0, &types.EpochStats{ + EpochEndTime: time.Unix(0, 0).UTC(), + Stats: []*types.EpochStats_UserWithStats{ + { + User: "alice", + Stats: &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 5_000_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 3_000_000_000, + }, + }, + { + User: "bob", + Stats: &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 10_000_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 7_000_000_000, + }, + }, + { + User: "carl", + Stats: &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 2_000_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 1_000_000_000, + }, + }, + }, + }) + + // Alice: has more than epoch (normal case) + k.SetUserStats(ctx, "alice", &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 10_000_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 8_000_000_000, + }) + + // Bob: has less than epoch (should clamp to 0) + k.SetUserStats(ctx, "bob", &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 5_000_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 3_000_000_000, + }) + + // Carl: has exactly the same (should become 0) + k.SetUserStats(ctx, "carl", &types.UserStats{ + Affiliate_30DReferredVolumeQuoteQuantums: 2_000_000_000, + Affiliate_30DAttributedVolumeQuoteQuantums: 1_000_000_000, + }) + + k.SetStatsMetadata(ctx, &types.StatsMetadata{ + TrailingEpoch: 0, + }) + + k.ExpireOldStats(ctx) + + // Verify Alice: normal subtraction + aliceStats := k.GetUserStats(ctx, "alice") + require.Equal(t, uint64(5_000_000_000), aliceStats.Affiliate_30DReferredVolumeQuoteQuantums, + "Alice should have 5B remaining (10B - 5B)") + require.Equal(t, uint64(5_000_000_000), aliceStats.Affiliate_30DAttributedVolumeQuoteQuantums, + "Alice should have 5B remaining (8B - 3B)") + + // Verify Bob: clamped to 0 + bobStats := k.GetUserStats(ctx, "bob") + require.Equal(t, uint64(0), bobStats.Affiliate_30DReferredVolumeQuoteQuantums, + "Bob should be clamped to 0 (had 5B, tried to subtract 10B)") + require.Equal(t, uint64(0), bobStats.Affiliate_30DAttributedVolumeQuoteQuantums, + "Bob should be clamped to 0 (had 3B, tried to subtract 7B)") + + // Verify Carl: exactly 0 + carlStats := k.GetUserStats(ctx, "carl") + require.Equal(t, uint64(0), carlStats.Affiliate_30DReferredVolumeQuoteQuantums, + "Carl should be exactly 0 (had 2B, subtracted 2B)") + require.Equal(t, uint64(0), carlStats.Affiliate_30DAttributedVolumeQuoteQuantums, + "Carl should be exactly 0 (had 1B, subtracted 1B)") +} From ed52322cb2a941889d20b7f9d232add9871ff745 Mon Sep 17 00:00:00 2001 From: pthmas <9058370+pthmas@users.noreply.github.com> Date: Mon, 27 Oct 2025 19:19:37 +0100 Subject: [PATCH 06/14] udpate cometbft fork to v0.38.19 --- protocol/go.mod | 52 ++++++++++----------- protocol/go.sum | 117 +++++++++++++++++++++++++----------------------- 2 files changed, 84 insertions(+), 85 deletions(-) diff --git a/protocol/go.mod b/protocol/go.mod index 1bcfea7bf0..1f2002ebdd 100644 --- a/protocol/go.mod +++ b/protocol/go.mod @@ -29,14 +29,14 @@ require ( github.com/pkg/errors v0.9.1 github.com/rakyll/statik v0.1.7 github.com/spf13/cast v1.7.0 - github.com/spf13/cobra v1.8.1 - github.com/spf13/pflag v1.0.5 + github.com/spf13/cobra v1.9.1 + github.com/spf13/pflag v1.0.6 github.com/stretchr/testify v1.10.0 github.com/vektra/mockery/v2 v2.50.0 github.com/zyedidia/generic v1.0.0 golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect - google.golang.org/grpc v1.68.1 + google.golang.org/grpc v1.70.0 gopkg.in/DataDog/dd-trace-go.v1 v1.48.0 gopkg.in/typ.v4 v4.1.0 ) @@ -72,8 +72,8 @@ require ( github.com/shopspring/decimal v1.3.1 github.com/spf13/viper v1.19.0 github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d - google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 - google.golang.org/protobuf v1.35.2 + google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a + google.golang.org/protobuf v1.36.5 gotest.tools/v3 v3.5.1 ) @@ -83,7 +83,7 @@ require ( cloud.google.com/go v0.115.1 // indirect cloud.google.com/go/auth v0.9.4 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect - cloud.google.com/go/compute/metadata v0.5.1 // indirect + cloud.google.com/go/compute/metadata v0.5.2 // indirect cloud.google.com/go/iam v1.2.0 // indirect cloud.google.com/go/storage v1.43.0 // indirect cosmossdk.io/collections v0.4.0 // indirect @@ -107,7 +107,7 @@ require ( github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect github.com/GeertJohan/go.rice v1.0.3 // indirect github.com/IBM/sarama v1.40.1 // indirect - github.com/Masterminds/semver/v3 v3.3.0 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect @@ -171,12 +171,12 @@ require ( github.com/daixiang0/gci v0.13.5 // indirect github.com/danieljoos/wincred v1.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/denis-tingaikin/go-header v0.5.0 // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/dgraph-io/badger/v4 v4.3.0 // indirect github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91 // indirect - github.com/docker/cli v23.0.1+incompatible // indirect + github.com/docker/cli v24.0.7+incompatible // indirect github.com/docker/docker v23.0.1+incompatible // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.5.0 // indirect @@ -261,6 +261,7 @@ require ( github.com/hashicorp/go-safetemp v1.0.0 // indirect github.com/hashicorp/go-uuid v1.0.3 // indirect github.com/hashicorp/golang-lru v1.0.2 // indirect + github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/hdevalence/ed25519consensus v0.1.0 // indirect @@ -299,7 +300,6 @@ require ( github.com/leodido/go-urn v1.2.4 // indirect github.com/leonklingele/grouper v1.1.2 // indirect github.com/lib/pq v1.10.9 // indirect - github.com/libp2p/go-buffer-pool v0.1.0 // indirect github.com/linxGnu/grocksdb v1.9.3 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/lovoo/goka v1.1.9 // indirect @@ -343,9 +343,9 @@ require ( github.com/pierrec/lz4/v4 v4.1.17 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/polyfloyd/go-errorlint v1.7.0 // indirect - github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_golang v1.21.0 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.60.1 // indirect + github.com/prometheus/common v0.62.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 // indirect github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect @@ -416,26 +416,26 @@ require ( go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect - go.opentelemetry.io/otel v1.29.0 // indirect - go.opentelemetry.io/otel/metric v1.29.0 // indirect - go.opentelemetry.io/otel/trace v1.29.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect go.uber.org/automaxprocs v1.6.0 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.31.0 // indirect + golang.org/x/crypto v0.33.0 // indirect golang.org/x/exp/typeparams v0.0.0-20241108190413-2d47ceb2692f // indirect golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.32.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/term v0.27.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/net v0.35.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect + golang.org/x/sync v0.11.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/term v0.29.0 // indirect + golang.org/x/text v0.22.0 // indirect golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.27.0 // indirect google.golang.org/api v0.198.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect @@ -470,11 +470,7 @@ replace ( // Use dYdX fork of Cosmos SDK/store cosmossdk.io/store => github.com/dydxprotocol/cosmos-sdk/store v1.0.3-0.20240326192503-dd116391188d // Use dYdX fork of CometBFT - github.com/cometbft/cometbft => github.com/dydxprotocol/cometbft v0.38.6-0.20251014202517-0235a938b029 - // Fixes the issue that `tx_search` resolves to a single entry, due to an cometbft-db interface - // change in v0.13.0+. - // TODO(CT-1343): Remove and fix properly by backporting upstream fix to cometbft fork. - github.com/cometbft/cometbft-db => github.com/cometbft/cometbft-db v0.12.0 + github.com/cometbft/cometbft => github.com/dydxprotocol/cometbft v0.38.6-0.20251021155510-74157e3aac09 // Use dYdX fork of Cosmos SDK github.com/cosmos/cosmos-sdk => github.com/dydxprotocol/cosmos-sdk v0.50.6-0.20251014211237-3a1ba0aabac3 github.com/cosmos/iavl => github.com/dydxprotocol/iavl v1.1.1-0.20240509161911-1c8b8e787e85 diff --git a/protocol/go.sum b/protocol/go.sum index 04546b9301..dff4f9c4fe 100644 --- a/protocol/go.sum +++ b/protocol/go.sum @@ -188,8 +188,8 @@ cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZ cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/compute/metadata v0.5.1 h1:NM6oZeZNlYjiwYje+sYFjEpP0Q0zCan1bmQW/KmIrGs= -cloud.google.com/go/compute/metadata v0.5.1/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= +cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= +cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= cloud.google.com/go/contactcenterinsights v1.6.0/go.mod h1:IIDlT6CLcDoyv79kDv8iWxMSTZhLxSCofVV5W6YFM/w= @@ -692,8 +692,8 @@ github.com/GeertJohan/go.rice v1.0.3/go.mod h1:XVdrU4pW00M4ikZed5q56tPf1v2KwnIKe github.com/IBM/sarama v1.40.1 h1:lL01NNg/iBeigUbT+wpPysuTYW6roHo6kc1QrffRf0k= github.com/IBM/sarama v1.40.1/go.mod h1:+5OFwA5Du9I6QrznhaMHsuwWdWZNMjaBSIxEWEgKOYE= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= -github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0= -github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= @@ -712,8 +712,8 @@ github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjC github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= -github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= -github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= +github.com/adlio/schema v1.3.6 h1:k1/zc2jNfeiZBA5aFTRy37jlBIuCkXCm0XmvpzCKI9I= +github.com/adlio/schema v1.3.6/go.mod h1:qkxwLgPBd1FgLRHYVCmQT/rrBr3JH38J9LjmVzWNudg= github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -774,8 +774,8 @@ github.com/breml/errchkjson v0.4.0 h1:gftf6uWZMtIa/Is3XJgibewBm2ksAQSY/kABDNFTAd github.com/breml/errchkjson v0.4.0/go.mod h1:AuBOSTHyLSaaAFlWsRSuRBIroCh3eh7ZHh5YeelDIk8= github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U= github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= -github.com/btcsuite/btcd/btcutil v1.1.3 h1:xfbtw8lwpp0G6NwSHb+UE67ryTFHJAiNuipusjXSohQ= -github.com/btcsuite/btcd/btcutil v1.1.3/go.mod h1:UR7dsSJzJUfMmFiiLlIrMq1lS9jh9EdCV7FStZSnpi0= +github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c= +github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28/ZlunY= @@ -863,8 +863,8 @@ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAK github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONNbHU7MuEHboiFuA= github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= -github.com/cometbft/cometbft-db v0.12.0 h1:v77/z0VyfSU7k682IzZeZPFZrQAKiQwkqGN0QzAjMi0= -github.com/cometbft/cometbft-db v0.12.0/go.mod h1:aX2NbCrjNVd2ZajYxt1BsiFf/Z+TQ2MN0VxdicheYuw= +github.com/cometbft/cometbft-db v0.15.0 h1:VLtsRt8udD4jHCyjvrsTBpgz83qne5hnL245AcPJVRk= +github.com/cometbft/cometbft-db v0.15.0/go.mod h1:EBrFs1GDRiTqrWXYi4v90Awf/gcdD5ExzdPbg4X8+mk= github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= @@ -901,8 +901,8 @@ github.com/cosmos/rosetta v0.50.3 h1:LYbtWQ3YxSxVOPvjhixCGJR54VzMJW6XVifml8AzLjU github.com/cosmos/rosetta v0.50.3/go.mod h1:SbRmXwYB8ioHq7ZiNbnkrhuP9B3rr3cIsBTXaZMvjeQ= github.com/cosmos/rosetta-sdk-go v0.10.0 h1:E5RhTruuoA7KTIXUcMicL76cffyeoyvNybzUGSKFTcM= github.com/cosmos/rosetta-sdk-go v0.10.0/go.mod h1:SImAZkb96YbwvoRkzSMQB6noNJXFgWl/ENIznEoYQI4= -github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c h1:uQYC5Z1mdLRPrZhHjHxufI8+2UG/i25QG92j0Er9p6I= github.com/crate-crypto/go-ipa v0.0.0-20240223125850-b1e8a79f509c/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v1.0.0 h1:TsSgHwrkTKecKJ4kadtHi4b3xHW5dCFUDFnUp1TsawI= @@ -928,10 +928,10 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1 github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= -github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= -github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= +github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= +github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8= github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY= github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I= @@ -942,8 +942,8 @@ github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91 h1:Pux6+xANi github.com/dgraph-io/ristretto v0.1.2-0.20240116140435-c67e07994f91/go.mod h1:swkazRqnUf1N62d0Nutz7KIj2UKqsm/H8tD0nBJAXqM= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM= -github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v24.0.7+incompatible h1:wa/nIwYFW7BVTGa7SWPVyyXU9lgORqUb1xfI36MSkFg= +github.com/docker/cli v24.0.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZekyoiVlUZEINcY= github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= @@ -956,8 +956,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= -github.com/dydxprotocol/cometbft v0.38.6-0.20251014202517-0235a938b029 h1:jgRwHeeMpPahMyWUvBT0TIdAo7M9y6CXLzF7ZZzYstg= -github.com/dydxprotocol/cometbft v0.38.6-0.20251014202517-0235a938b029/go.mod h1:XSQX1hQbr54qaJb4/5YNNZGXkAQHHa6bi/KMcN1SQ7w= +github.com/dydxprotocol/cometbft v0.38.6-0.20251021155510-74157e3aac09 h1:b0FQumkBGAMfPs9Q2tw9mR2lPg6fTC/egVzgILyojIA= +github.com/dydxprotocol/cometbft v0.38.6-0.20251021155510-74157e3aac09/go.mod h1:IcJVLogjmvbfWl2bFWJ0gwl8Rv5kgfg4/N+vUA9HARs= github.com/dydxprotocol/cosmos-sdk v0.50.6-0.20251014211237-3a1ba0aabac3 h1:VzjChSIDsDua0WjFoHb+bqodgeAMBPsflNS7ot14TQU= github.com/dydxprotocol/cosmos-sdk v0.50.6-0.20251014211237-3a1ba0aabac3/go.mod h1:PqtaF8C4fKHmDIvrdc7GBpZKsRkjihCJxq0gOlt2k98= github.com/dydxprotocol/cosmos-sdk/store v1.0.3-0.20240326192503-dd116391188d h1:HgLu1FD2oDFzlKW6/+SFXlH5Os8cwNTbplQIrQOWx8w= @@ -1356,6 +1356,8 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= @@ -1488,8 +1490,6 @@ github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84Yrj github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= -github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= github.com/linxGnu/grocksdb v1.9.3 h1:s1cbPcOd0cU2SKXRG1nEqCOWYAELQjdqg3RVI2MH9ik= github.com/linxGnu/grocksdb v1.9.3/go.mod h1:QYiYypR2d4v63Wj1adOOfzglnoII0gLj3PNh4fZkcFA= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= @@ -1762,10 +1762,11 @@ github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w= github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= +github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0= @@ -1918,21 +1919,23 @@ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.5 go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= -go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= -go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= -go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= -go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= -go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= -go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= @@ -1976,8 +1979,8 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0 golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= +golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2115,8 +2118,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2150,8 +2153,8 @@ golang.org/x/oauth2 v0.7.0/go.mod h1:hPLQkd9LyjfXTiRohC/41GhcFqxisoUQ99sCUOHO9x4 golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= golang.org/x/oauth2 v0.12.0/go.mod h1:A74bZ3aGXgCY0qaIC9Ahg6Lglin4AMAco8cIv9baba4= golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2170,8 +2173,8 @@ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= -golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= +golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2278,8 +2281,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2293,8 +2296,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= -golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= +golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= +golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2313,8 +2316,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2637,15 +2640,15 @@ google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go. google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= -google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= +google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a h1:OAiGFfOiA0v9MRYsSidp3ubZaBnteRUyn3xB2ZQ5G/E= +google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY= google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc= google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f h1:cUMEy+8oS78BWIH9OWazBkzbr090Od9tWBNtZHkOhf0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -2688,8 +2691,8 @@ google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g= google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8= -google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= -google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -2710,8 +2713,8 @@ google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/DataDog/dd-trace-go.v1 v1.48.0 h1:AZhmo9zstDWWD7qG7g+2W7x4X7FYuGJcwRIIEjsLiEY= gopkg.in/DataDog/dd-trace-go.v1 v1.48.0/go.mod h1:z+Zm99hL8zep83JLkTbknOxSMJnNvAggmL6mnUtDhWE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From d0704cd3302350005ef14096b2ce23ab61077fc3 Mon Sep 17 00:00:00 2001 From: tac0turtle Date: Tue, 14 Oct 2025 11:01:04 +0200 Subject: [PATCH 07/14] add snapshot cmd --- protocol/cmd/dydxprotocold/cmd/root.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/protocol/cmd/dydxprotocold/cmd/root.go b/protocol/cmd/dydxprotocold/cmd/root.go index 3eda25e32b..d12d8516e3 100644 --- a/protocol/cmd/dydxprotocold/cmd/root.go +++ b/protocol/cmd/dydxprotocold/cmd/root.go @@ -24,6 +24,7 @@ import ( "github.com/cosmos/cosmos-sdk/client/flags" "github.com/cosmos/cosmos-sdk/client/keys" "github.com/cosmos/cosmos-sdk/client/rpc" + "github.com/cosmos/cosmos-sdk/client/snapshot" "github.com/cosmos/cosmos-sdk/codec/address" "github.com/cosmos/cosmos-sdk/crypto/keyring" runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" @@ -209,13 +210,16 @@ func initRootCmd( tmcli.NewCompletionCmd(rootCmd, true), debug.Cmd(), confixcmd.ConfigCommand(), + snapshot.Cmd(newApp), ) server.AddCommands( rootCmd, dydxapp.DefaultNodeHome, func(logger log.Logger, db dbm.DB, writer io.Writer, options servertypes.AppOptions) servertypes.Application { - return appInterceptor(newApp(logger, db, writer, options)) + //TODO: cleanup app appInterceptor + app := newApp(logger, db, writer, options) + return appInterceptor(app.(*dydxapp.App)) }, appExport, func(cmd *cobra.Command) { @@ -351,7 +355,7 @@ func newApp( db dbm.DB, traceStore io.Writer, appOpts servertypes.AppOptions, -) *dydxapp.App { +) servertypes.Application { var cache storetypes.MultiStorePersistentCache if cast.ToBool(appOpts.Get(server.FlagInterBlockCache)) { From e998d384e5160e4c1e14e4e9ae16c4a80c68d4ea Mon Sep 17 00:00:00 2001 From: pthmas <9058370+pthmas@users.noreply.github.com> Date: Wed, 8 Oct 2025 17:18:58 +0200 Subject: [PATCH 08/14] add memiavl --- protocol/app/ante.go | 29 ++++++++++-- protocol/app/app.go | 19 +++++++- protocol/cmd/dydxprotocold/cmd/config.go | 15 +++++- protocol/go.mod | 38 ++++++++++----- protocol/go.sum | 59 ++++++++++++++++-------- 5 files changed, 121 insertions(+), 39 deletions(-) diff --git a/protocol/app/ante.go b/protocol/app/ante.go index 1f849f4bd3..0aae890058 100644 --- a/protocol/app/ante.go +++ b/protocol/app/ante.go @@ -6,7 +6,6 @@ import ( sending "github.com/dydxprotocol/v4-chain/protocol/x/sending/types" errorsmod "cosmossdk.io/errors" - "cosmossdk.io/store/cachemulti" storetypes "cosmossdk.io/store/types" "github.com/cosmos/cosmos-sdk/codec" sdk "github.com/cosmos/cosmos-sdk/types" @@ -249,10 +248,20 @@ func (h *lockingAnteHandler) clobAnteHandle(ctx sdk.Context, tx sdk.Tx, simulate return ctx, err } - cacheMs = ctx.MultiStore().(cachemulti.Store).CacheMultiStoreWithLocking(map[storetypes.StoreKey][][]byte{ + lms, ok := ctx.MultiStore().(interface { + CacheMultiStoreWithLocking(map[storetypes.StoreKey][][]byte) storetypes.CacheMultiStore + }) + if !ok { + return ctx, errorsmod.Wrap(sdkerrors.ErrLogic, "MultiStore does not support CacheMultiStoreWithLocking") + } + cacheMs = lms.CacheMultiStoreWithLocking(map[storetypes.StoreKey][][]byte{ h.authStoreKey: signers, }) - defer cacheMs.(storetypes.LockingStore).Unlock() + ls, ok := cacheMs.(storetypes.LockingStore) + if !ok { + return ctx, errorsmod.Wrap(sdkerrors.ErrLogic, "CacheMultiStore does not implement LockingStore") + } + defer ls.Unlock() ctx = ctx.WithMultiStore(cacheMs) } @@ -407,10 +416,20 @@ func (h *lockingAnteHandler) otherMsgAnteHandle(ctx sdk.Context, tx sdk.Tx, simu return ctx, err } - cacheMs = ctx.MultiStore().(cachemulti.Store).CacheMultiStoreWithLocking(map[storetypes.StoreKey][][]byte{ + lms, ok := ctx.MultiStore().(interface { + CacheMultiStoreWithLocking(map[storetypes.StoreKey][][]byte) storetypes.CacheMultiStore + }) + if !ok { + return ctx, errorsmod.Wrap(sdkerrors.ErrLogic, "MultiStore does not support CacheMultiStoreWithLocking") + } + cacheMs = lms.CacheMultiStoreWithLocking(map[storetypes.StoreKey][][]byte{ h.authStoreKey: signers, }) - defer cacheMs.(storetypes.LockingStore).Unlock() + ls, ok := cacheMs.(storetypes.LockingStore) + if !ok { + return ctx, errorsmod.Wrap(sdkerrors.ErrLogic, "CacheMultiStore does not implement LockingStore") + } + defer ls.Unlock() ctx = ctx.WithMultiStore(cacheMs) h.globalLock.Lock() diff --git a/protocol/app/app.go b/protocol/app/app.go index 7d36544c33..386f3bd334 100644 --- a/protocol/app/app.go +++ b/protocol/app/app.go @@ -3,6 +3,7 @@ package app import ( "context" "encoding/json" + "errors" "fmt" "io" "math/big" @@ -91,6 +92,8 @@ import ( "github.com/cosmos/ibc-go/modules/capability" capabilitykeeper "github.com/cosmos/ibc-go/modules/capability/keeper" capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" + memiavlstore "github.com/crypto-org-chain/cronos/store" + memiavlrootmulti "github.com/crypto-org-chain/cronos/store/rootmulti" antetypes "github.com/dydxprotocol/v4-chain/protocol/app/ante/types" "github.com/gorilla/mux" "github.com/rakyll/statik/fs" @@ -412,6 +415,16 @@ func New( interfaceRegistry := encodingConfig.InterfaceRegistry txConfig := encodingConfig.TxConfig + // Conditionally enable MemIAVL + cacheSize := cast.ToInt(appOpts.Get(memiavlstore.FlagCacheSize)) + homePath := cast.ToString(appOpts.Get(cosmosflags.FlagHome)) + if cast.ToBool(appOpts.Get(memiavlstore.FlagMemIAVL)) { + logger.Info("********************MemIAVL enabled *************************", "cacheSize", cacheSize) + baseAppOptions = memiavlstore.SetupMemIAVL(logger, homePath, appOpts, false, false, cacheSize, baseAppOptions) + } else { + logger.Info("****************** MemIAVL disabled; using standard IAVL") + } + // Enable optimistic block execution. if appFlags.OptimisticExecutionEnabled { logger.Info("optimistic execution is enabled.") @@ -620,7 +633,6 @@ func New( for _, h := range cast.ToIntSlice(appOpts.Get(server.FlagUnsafeSkipUpgrades)) { skipUpgradeHeights[int64(h)] = true } - homePath := cast.ToString(appOpts.Get(cosmosflags.FlagHome)) // set the governance module account as the authority for conducting upgrades app.UpgradeKeeper = upgradekeeper.NewKeeper( skipUpgradeHeights, @@ -2029,10 +2041,13 @@ func (app *App) setAnteHandler(txConfig client.TxConfig) { // Close invokes an ordered shutdown of routines. func (app *App) Close() error { - app.BaseApp.Close() + err := app.BaseApp.Close() if app.oraclePrometheusServer != nil { app.oraclePrometheusServer.Close() } + if cms, ok := app.CommitMultiStore().(*memiavlrootmulti.Store); ok { + return errors.Join(err, cms.Close()) + } return app.closeOnce() } diff --git a/protocol/cmd/dydxprotocold/cmd/config.go b/protocol/cmd/dydxprotocold/cmd/config.go index ff235a8927..1b9ebcc304 100644 --- a/protocol/cmd/dydxprotocold/cmd/config.go +++ b/protocol/cmd/dydxprotocold/cmd/config.go @@ -8,6 +8,9 @@ import ( oracleconfig "github.com/dydxprotocol/slinky/oracle/config" assettypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/types" clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + + // MemIAVL config + memiavlcfg "github.com/crypto-org-chain/cronos/store/config" ) const ( @@ -26,6 +29,10 @@ const ( // DydxAppConfig specifies dYdX app specific config. type DydxAppConfig struct { serverconfig.Config + + // MemIAVL section in app.toml: [memiavl] + MemIAVL memiavlcfg.MemIAVLConfig `mapstructure:"memiavl"` + Oracle oracleconfig.AppConfig `mapstructure:"oracle"` } @@ -34,6 +41,7 @@ type DydxAppConfig struct { // initAppConfig helps to override default appConfig template and configs. // return "", nil if no custom configuration is required for the application. func initAppConfig() (string, *DydxAppConfig) { + // Optionally allow the chain developer to overwrite the SDK's default // server config. srvCfg := serverconfig.DefaultConfig() @@ -54,6 +62,8 @@ func initAppConfig() (string, *DydxAppConfig) { appConfig := DydxAppConfig{ Config: *srvCfg, + // Default MemIAVL config (disabled by default; enable via app.toml) + MemIAVL: memiavlcfg.DefaultMemIAVLConfig(), Oracle: oracleconfig.AppConfig{ Enabled: true, OracleAddress: "localhost:8080", @@ -73,7 +83,10 @@ func initAppConfig() (string, *DydxAppConfig) { // GRPC. appConfig.GRPC.Address = "0.0.0.0:9090" - appTemplate := serverconfig.DefaultConfigTemplate + oracleconfig.DefaultConfigTemplate + // Include MemIAVL config template so it shows up in app.toml + appTemplate := serverconfig.DefaultConfigTemplate + + oracleconfig.DefaultConfigTemplate + + memiavlcfg.DefaultConfigTemplate return appTemplate, &appConfig } diff --git a/protocol/go.mod b/protocol/go.mod index 1f2002ebdd..22ef46a72e 100644 --- a/protocol/go.mod +++ b/protocol/go.mod @@ -8,12 +8,13 @@ require ( cosmossdk.io/api v0.7.6 cosmossdk.io/math v1.4.0 github.com/Shopify/sarama v1.37.2 - github.com/cometbft/cometbft v0.38.15 + github.com/cometbft/cometbft v0.38.17 github.com/cometbft/cometbft-db v0.15.0 // indirect github.com/cosmos/cosmos-proto v1.0.0-beta.5 github.com/cosmos/cosmos-sdk v0.50.11 github.com/cosmos/go-bip39 v1.0.0 github.com/cosmos/gogoproto v1.7.0 + github.com/crypto-org-chain/cronos/store v0.0.5-0.20240716081818-7f581a697f6d github.com/go-playground/validator/v10 v10.14.0 github.com/gofrs/flock v0.12.1 github.com/gogo/protobuf v1.3.2 // indirect @@ -116,6 +117,7 @@ require ( github.com/alexkohler/nakedret/v2 v2.0.5 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect + github.com/alitto/pond v1.8.3 // indirect github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect github.com/ashanbrown/forbidigo v1.6.0 // indirect github.com/ashanbrown/makezero v1.1.1 // indirect @@ -166,6 +168,7 @@ require ( github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect github.com/creachadair/atomicfile v0.3.1 // indirect github.com/creachadair/tomledit v0.0.24 // indirect + github.com/crypto-org-chain/cronos/memiavl v0.0.4 // indirect github.com/curioswitch/go-reassign v0.2.0 // indirect github.com/daaku/go.zipexe v1.0.2 // indirect github.com/daixiang0/gci v0.13.5 // indirect @@ -297,6 +300,7 @@ require ( github.com/lasiar/canonicalheader v1.1.2 // indirect github.com/ldez/gomoddirectives v0.2.4 // indirect github.com/ldez/tagliatelle v0.5.0 // indirect + github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 // indirect github.com/leodido/go-urn v1.2.4 // indirect github.com/leonklingele/grouper v1.1.2 // indirect github.com/lib/pq v1.10.9 // indirect @@ -388,6 +392,11 @@ require ( github.com/tendermint/go-amino v0.16.0 // indirect github.com/tetafro/godot v1.4.18 // indirect github.com/tidwall/btree v1.7.0 // indirect + github.com/tidwall/gjson v1.17.1 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/tidwall/tinylru v1.1.0 // indirect + github.com/tidwall/wal v1.1.7 // indirect github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect github.com/timonwong/loggercheck v0.10.1 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect @@ -406,6 +415,7 @@ require ( github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.3.0 // indirect github.com/ykadowak/zerologlint v0.1.5 // indirect + github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect gitlab.com/bosi/decorder v0.4.2 // indirect @@ -423,15 +433,15 @@ require ( go.uber.org/multierr v1.11.0 // indirect go.uber.org/ratelimit v0.2.0 // indirect go.uber.org/zap v1.27.0 // indirect - golang.org/x/crypto v0.33.0 // indirect + golang.org/x/crypto v0.36.0 // indirect golang.org/x/exp/typeparams v0.0.0-20241108190413-2d47ceb2692f // indirect golang.org/x/mod v0.22.0 // indirect - golang.org/x/net v0.35.0 // indirect + golang.org/x/net v0.38.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect - golang.org/x/sync v0.11.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/term v0.29.0 // indirect - golang.org/x/text v0.22.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/term v0.30.0 // indirect + golang.org/x/text v0.23.0 // indirect golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.27.0 // indirect google.golang.org/api v0.198.0 // indirect @@ -460,6 +470,13 @@ replace ( github.com/prometheus/common => github.com/prometheus/common v0.47.0 ) +replace ( + // memiavl refers to old packages so we need to replace them with the correct naming + github.com/crypto-org-chain/cronos/memiavl => github.com/crypto-org-chain/cronos/memiavl v0.0.5-0.20241028093154-0f94930c27ce + github.com/crypto-org-chain/cronos/store => github.com/01builders/dydx-cronos/store v0.0.5-dydx2 + github.com/tendermint/tendermint => github.com/cometbft/cometbft v0.38.15 +) + replace ( // TODO(DEC-2209): Ideally we rely on a released version (we don't make any changes in our cosmos-sdk fork). // In this case the latest signing mode fixes aren't tagged as a release yet. @@ -467,13 +484,12 @@ replace ( // TODO(https://github.com/cosmos/rosetta/issues/76): Rosetta requires cosmossdk.io/core v0.12.0 erroneously but // should use v0.11.0. The Cosmos build fails with types/context.go:65:29: undefined: comet.BlockInfo otherwise. cosmossdk.io/core => cosmossdk.io/core v0.11.0 - // Use dYdX fork of Cosmos SDK/store - cosmossdk.io/store => github.com/dydxprotocol/cosmos-sdk/store v1.0.3-0.20240326192503-dd116391188d + cosmossdk.io/store => github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx // Use dYdX fork of CometBFT github.com/cometbft/cometbft => github.com/dydxprotocol/cometbft v0.38.6-0.20251021155510-74157e3aac09 // Use dYdX fork of Cosmos SDK - github.com/cosmos/cosmos-sdk => github.com/dydxprotocol/cosmos-sdk v0.50.6-0.20251014211237-3a1ba0aabac3 - github.com/cosmos/iavl => github.com/dydxprotocol/iavl v1.1.1-0.20240509161911-1c8b8e787e85 + github.com/cosmos/cosmos-sdk => github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl +//github.com/cosmos/iavl => github.com/dydxprotocol/iavl v1.1.1-0.20240509161911-1c8b8e787e85 ) replace ( diff --git a/protocol/go.sum b/protocol/go.sum index dff4f9c4fe..e7c13983a1 100644 --- a/protocol/go.sum +++ b/protocol/go.sum @@ -643,6 +643,12 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl h1:GhwXBczji9osPEHCz7SUnzjZvpP+FWtUzKii6PV7AzQ= +github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl/go.mod h1:3o6dUdxFTh0n/kJyl+HWdHfYnzfRr3TemiOXk3gJdqQ= +github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx h1:7zpjRUXs6m4TerknjJB4D7HVsJh8XQO3gTObXAXu6D8= +github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx/go.mod h1:ZW4eIE98wivInyQyhxU2nHqEielc/iZAcr41qNyWSrw= +github.com/01builders/dydx-cronos/store v0.0.5-dydx2 h1:xGn3Vw3aectKOKCIpR+Gv78HL0vj3ad0PgWZjaIp+WA= +github.com/01builders/dydx-cronos/store v0.0.5-dydx2/go.mod h1:lj46YCLx4XWPkzdLl9syauqlFn2SiWOEqL2rjkdaUO4= github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8= github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= @@ -733,6 +739,8 @@ github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pO github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE= github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw= github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I= +github.com/alitto/pond v1.8.3 h1:ydIqygCLVPqIX/USe5EaV/aSRXTRXDEI9JwuDdu+/xs= +github.com/alitto/pond v1.8.3/go.mod h1:CmvIIGd5jKLasGI3D87qDkQxjzChdKMmnXMg3fG6M6Q= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 h1:MzBOUgng9orim59UnfUTLRjMpd09C5uEVQ6RPGeCaVI= github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129/go.mod h1:rFgpPQZYZ8vdbc+48xibu8ALc3yeyd64IhHS+PU6Yyg= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= @@ -887,6 +895,8 @@ github.com/cosmos/gogogateway v1.2.0/go.mod h1:iQpLkGWxYcnCdz5iAdLcRBSw3h7NXeOkZ github.com/cosmos/gogoproto v1.4.2/go.mod h1:cLxOsn1ljAHSV527CHOtaIP91kK6cCrZETRBrkzItWU= github.com/cosmos/gogoproto v1.7.0 h1:79USr0oyXAbxg3rspGh/m4SWNyoz/GLaAh0QlCe2fro= github.com/cosmos/gogoproto v1.7.0/go.mod h1:yWChEv5IUEYURQasfyBW5ffkMHR/90hiHgbNgrtp4j0= +github.com/cosmos/iavl v1.2.2 h1:qHhKW3I70w+04g5KdsdVSHRbFLgt3yY3qTMd4Xa4rC8= +github.com/cosmos/iavl v1.2.2/go.mod h1:GiM43q0pB+uG53mLxLDzimxM9l/5N9UuSY3/D0huuVw= github.com/cosmos/ibc-go/modules/capability v1.0.1 h1:ibwhrpJ3SftEEZRxCRkH0fQZ9svjthrX2+oXdZvzgGI= github.com/cosmos/ibc-go/modules/capability v1.0.1/go.mod h1:rquyOV262nGJplkumH+/LeYs04P3eV8oB7ZM4Ygqk4E= github.com/cosmos/ics23/go v0.11.0 h1:jk5skjT0TqX5e5QJbEnwXIS2yI2vnmLOgpQPeM5RtnU= @@ -914,6 +924,8 @@ github.com/creachadair/tomledit v0.0.24/go.mod h1:9qHbShRWQzSCcn617cMzg4eab1vbLC github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/crypto-org-chain/cronos/memiavl v0.0.5-0.20241028093154-0f94930c27ce h1:yRF9Zsk4fzVBhBQEjkA4oE+Q3Q/Kgaj/UX4gK+xwaAs= +github.com/crypto-org-chain/cronos/memiavl v0.0.5-0.20241028093154-0f94930c27ce/go.mod h1:IyRvgFKOQPC/Qdx543PGl6WgeDOU+hWdv+xLz3stotc= github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= github.com/daaku/go.zipexe v1.0.2 h1:Zg55YLYTr7M9wjKn8SY/WcpuuEi+kR2u4E8RhvpyXmk= @@ -958,12 +970,6 @@ github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= github.com/dydxprotocol/cometbft v0.38.6-0.20251021155510-74157e3aac09 h1:b0FQumkBGAMfPs9Q2tw9mR2lPg6fTC/egVzgILyojIA= github.com/dydxprotocol/cometbft v0.38.6-0.20251021155510-74157e3aac09/go.mod h1:IcJVLogjmvbfWl2bFWJ0gwl8Rv5kgfg4/N+vUA9HARs= -github.com/dydxprotocol/cosmos-sdk v0.50.6-0.20251014211237-3a1ba0aabac3 h1:VzjChSIDsDua0WjFoHb+bqodgeAMBPsflNS7ot14TQU= -github.com/dydxprotocol/cosmos-sdk v0.50.6-0.20251014211237-3a1ba0aabac3/go.mod h1:PqtaF8C4fKHmDIvrdc7GBpZKsRkjihCJxq0gOlt2k98= -github.com/dydxprotocol/cosmos-sdk/store v1.0.3-0.20240326192503-dd116391188d h1:HgLu1FD2oDFzlKW6/+SFXlH5Os8cwNTbplQIrQOWx8w= -github.com/dydxprotocol/cosmos-sdk/store v1.0.3-0.20240326192503-dd116391188d/go.mod h1:zMcD3hfNwd0WMTpdRUhS3QxoCoEtBXWeoKsu3iaLBbQ= -github.com/dydxprotocol/iavl v1.1.1-0.20240509161911-1c8b8e787e85 h1:5B/yGZyTBX/OZASQQMnk6Ms/TZja56MYd8OBaVc0Mho= -github.com/dydxprotocol/iavl v1.1.1-0.20240509161911-1c8b8e787e85/go.mod h1:8xIUkgVvwvVrBu81scdPty+/Dx9GqwHnAvXz4cwF7RY= github.com/dydxprotocol/ibc-go/v8 v8.0.0-rc.0.0.20250312180215-8733b3edf43a h1:aF1rORtUApr+N6dWsNiT/G9H2ysfjyj5DiVRU8CRDaU= github.com/dydxprotocol/ibc-go/v8 v8.0.0-rc.0.0.20250312180215-8733b3edf43a/go.mod h1:C6IiJom0F3cIQCD5fKwVPDrDK9j/xTu563AWuOmXois= github.com/dydxprotocol/slinky v1.3.2 h1:JSMKq37MDlei28ec4o6sB2RaihWPL1xQ8BoH9wGxg9w= @@ -1484,6 +1490,8 @@ github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSio github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4= github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263 h1:LGEzZvf33Y1NhuP5+jI/ni9l1TFS6oYPDilgy74NusM= +github.com/ledgerwatch/erigon-lib v0.0.0-20230210071639-db0e7ed11263/go.mod h1:OXgMDuUo2lZ3NpH29ZvMYbk+LxFd5ffDl2Z2mGMuY/I= github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY= @@ -1818,9 +1826,18 @@ github.com/tetafro/godot v1.4.18 h1:ouX3XGiziKDypbpXqShBfnNLTSjR8r3/HVzrtJ+bHlI= github.com/tetafro/godot v1.4.18/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI= github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY= +github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= +github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/tinylru v1.1.0 h1:XY6IUfzVTU9rpwdhKUF6nQdChgCdGjkMfLzbWyiau6I= +github.com/tidwall/tinylru v1.1.0/go.mod h1:3+bX+TJ2baOLMWTnlyNWHh4QMnFyARg2TLTQ6OFbzw8= +github.com/tidwall/wal v1.1.7 h1:emc1TRjIVsdKKSnpwGBAcsAGg0767SvUk8+ygx7Bb+4= +github.com/tidwall/wal v1.1.7/go.mod h1:r6lR1j27W9EPalgHiB7zLJDYu3mzW5BQP5KrzBpYY/E= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M= github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ= github.com/timonwong/loggercheck v0.10.1 h1:uVZYClxQFpw55eh+PIoqM7uAOHMrhVcDoWDery9R8Lg= @@ -1886,6 +1903,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d h1:XQyeLr7N9iY9mi+TGgsBFkj54+j3fdoo8e2u6zrGP5A= +github.com/zbiljic/go-filelock v0.0.0-20170914061330-1dbf7103ab7d/go.mod h1:hoMeDjlNXTNqVwrCk8YDyaBS2g5vFfEX2ezMi4vb6CY= github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U= @@ -1934,8 +1953,8 @@ go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= @@ -1979,8 +1998,8 @@ golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0 golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -2118,8 +2137,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -2173,8 +2192,8 @@ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2281,8 +2300,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -2296,8 +2315,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2316,8 +2335,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= From 157a6bc2fd537e0eecb8508edce464e7bfc1e703 Mon Sep 17 00:00:00 2001 From: pthmas <9058370+pthmas@users.noreply.github.com> Date: Thu, 9 Oct 2025 11:35:47 +0200 Subject: [PATCH 09/14] update cosmos --- protocol/go.mod | 2 +- protocol/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/go.mod b/protocol/go.mod index 22ef46a72e..c3aa15a1b8 100644 --- a/protocol/go.mod +++ b/protocol/go.mod @@ -488,7 +488,7 @@ replace ( // Use dYdX fork of CometBFT github.com/cometbft/cometbft => github.com/dydxprotocol/cometbft v0.38.6-0.20251021155510-74157e3aac09 // Use dYdX fork of Cosmos SDK - github.com/cosmos/cosmos-sdk => github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl + github.com/cosmos/cosmos-sdk => github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl3 //github.com/cosmos/iavl => github.com/dydxprotocol/iavl v1.1.1-0.20240509161911-1c8b8e787e85 ) diff --git a/protocol/go.sum b/protocol/go.sum index e7c13983a1..6c02e9ca6e 100644 --- a/protocol/go.sum +++ b/protocol/go.sum @@ -643,8 +643,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= -github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl h1:GhwXBczji9osPEHCz7SUnzjZvpP+FWtUzKii6PV7AzQ= -github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl/go.mod h1:3o6dUdxFTh0n/kJyl+HWdHfYnzfRr3TemiOXk3gJdqQ= +github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl3 h1:VC3kuGtK0BWDZz6MdzbAMe7qg3npzRZIwQwfi5UJYbk= +github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl3/go.mod h1:4hp2dWoiVmg2BOXWhc7+a76nTGdui6gnnz2zE+Hly1k= github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx h1:7zpjRUXs6m4TerknjJB4D7HVsJh8XQO3gTObXAXu6D8= github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx/go.mod h1:ZW4eIE98wivInyQyhxU2nHqEielc/iZAcr41qNyWSrw= github.com/01builders/dydx-cronos/store v0.0.5-dydx2 h1:xGn3Vw3aectKOKCIpR+Gv78HL0vj3ad0PgWZjaIp+WA= From 28a2033a7ee0642d2f3d6e8311ff9f6974ef8438 Mon Sep 17 00:00:00 2001 From: pthmas <9058370+pthmas@users.noreply.github.com> Date: Fri, 24 Oct 2025 15:20:23 +0200 Subject: [PATCH 10/14] update cronos store to correct version --- protocol/go.mod | 2 +- protocol/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/go.mod b/protocol/go.mod index c3aa15a1b8..48525eb618 100644 --- a/protocol/go.mod +++ b/protocol/go.mod @@ -473,7 +473,7 @@ replace ( replace ( // memiavl refers to old packages so we need to replace them with the correct naming github.com/crypto-org-chain/cronos/memiavl => github.com/crypto-org-chain/cronos/memiavl v0.0.5-0.20241028093154-0f94930c27ce - github.com/crypto-org-chain/cronos/store => github.com/01builders/dydx-cronos/store v0.0.5-dydx2 + github.com/crypto-org-chain/cronos/store => github.com/01builders/dydx-cronos/store v0.0.5-dydx3 github.com/tendermint/tendermint => github.com/cometbft/cometbft v0.38.15 ) diff --git a/protocol/go.sum b/protocol/go.sum index 6c02e9ca6e..03102e4d88 100644 --- a/protocol/go.sum +++ b/protocol/go.sum @@ -647,8 +647,8 @@ github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl3 h1:VC3kuGtK0BWDZz6Md github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl3/go.mod h1:4hp2dWoiVmg2BOXWhc7+a76nTGdui6gnnz2zE+Hly1k= github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx h1:7zpjRUXs6m4TerknjJB4D7HVsJh8XQO3gTObXAXu6D8= github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx/go.mod h1:ZW4eIE98wivInyQyhxU2nHqEielc/iZAcr41qNyWSrw= -github.com/01builders/dydx-cronos/store v0.0.5-dydx2 h1:xGn3Vw3aectKOKCIpR+Gv78HL0vj3ad0PgWZjaIp+WA= -github.com/01builders/dydx-cronos/store v0.0.5-dydx2/go.mod h1:lj46YCLx4XWPkzdLl9syauqlFn2SiWOEqL2rjkdaUO4= +github.com/01builders/dydx-cronos/store v0.0.5-dydx3 h1:dFrDfQ4IoVzmPDwN8pK4x5xmHzOno1ffMniGpEBg4Mc= +github.com/01builders/dydx-cronos/store v0.0.5-dydx3/go.mod h1:II1a1DGyN3uD1hPS6dUFxI6RnCZz0ZW2v7GMgfjTp6o= github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8= github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= From 0791c404a729f32be4e05ef388f150638a4ca9aa Mon Sep 17 00:00:00 2001 From: pthmas <9058370+pthmas@users.noreply.github.com> Date: Tue, 28 Oct 2025 13:18:41 +0100 Subject: [PATCH 11/14] add versiondb --- protocol/Makefile | 2 +- protocol/app/app.go | 145 +++++++++++------- protocol/app/versiondb.go | 55 +++++++ protocol/app/versiondb_placeholder.go | 19 +++ protocol/cmd/dydxprotocold/cmd/root.go | 6 + protocol/cmd/dydxprotocold/cmd/versiondb.go | 28 ++++ .../cmd/versiondb_placeholder.go | 12 ++ protocol/cmd/dydxprotocold/opendb/opendb.go | 20 +++ .../dydxprotocold/opendb/opendb_rocksdb.go | 99 ++++++++++++ protocol/go.mod | 4 +- protocol/go.sum | 2 + 11 files changed, 337 insertions(+), 55 deletions(-) create mode 100644 protocol/app/versiondb.go create mode 100644 protocol/app/versiondb_placeholder.go create mode 100644 protocol/cmd/dydxprotocold/cmd/versiondb.go create mode 100644 protocol/cmd/dydxprotocold/cmd/versiondb_placeholder.go create mode 100644 protocol/cmd/dydxprotocold/opendb/opendb.go create mode 100644 protocol/cmd/dydxprotocold/opendb/opendb_rocksdb.go diff --git a/protocol/Makefile b/protocol/Makefile index 9aa9e19390..18231cbb66 100644 --- a/protocol/Makefile +++ b/protocol/Makefile @@ -11,7 +11,7 @@ export COMMIT=$(shell git rev-parse HEAD) export GO111MODULE = on # process build tags -build_tags = netgo +build_tags = netgo rocksdb ifeq ($(LEDGER_ENABLED),true) ifeq ($(OS),Windows_NT) diff --git a/protocol/app/app.go b/protocol/app/app.go index 386f3bd334..6286e2bd0c 100644 --- a/protocol/app/app.go +++ b/protocol/app/app.go @@ -268,6 +268,13 @@ func init() { sdk.DefaultPowerReduction = lib.PowerReduction } +type RootMultiStore interface { + storetypes.MultiStore + + // LatestVersion returns the latest version in the store + LatestVersion() int64 +} + // App extends an ABCI application, but with most of its parameters exported. // They are exported for convenience in creating helper functions, as object // capabilities aren't needed for testing. @@ -376,6 +383,8 @@ type App struct { // Slinky oraclePrometheusServer *promserver.PrometheusServer oracleMetrics servicemetrics.Metrics + + qms RootMultiStore } // assertAppPreconditions assert invariants required for an application to start. @@ -447,59 +456,7 @@ func New( bApp.SetInterfaceRegistry(interfaceRegistry) bApp.SetTxEncoder(txConfig.TxEncoder()) - keys := storetypes.NewKVStoreKeys( - authtypes.StoreKey, - authzkeeper.StoreKey, - banktypes.StoreKey, - stakingtypes.StoreKey, - crisistypes.StoreKey, - distrtypes.StoreKey, - slashingtypes.StoreKey, - govtypes.StoreKey, - paramstypes.StoreKey, - consensusparamtypes.StoreKey, - upgradetypes.StoreKey, - feegrant.StoreKey, - ibcexported.StoreKey, - ibctransfertypes.StoreKey, - ratelimitmoduletypes.StoreKey, - icacontrollertypes.StoreKey, - icahosttypes.StoreKey, - evidencetypes.StoreKey, - capabilitytypes.StoreKey, - pricesmoduletypes.StoreKey, - assetsmoduletypes.StoreKey, - blocktimemoduletypes.StoreKey, - bridgemoduletypes.StoreKey, - feetiersmoduletypes.StoreKey, - listingmoduletypes.StoreKey, - perpetualsmoduletypes.StoreKey, - satypes.StoreKey, - statsmoduletypes.StoreKey, - vestmoduletypes.StoreKey, - rewardsmoduletypes.StoreKey, - clobmoduletypes.StoreKey, - sendingmoduletypes.StoreKey, - delaymsgmoduletypes.StoreKey, - epochsmoduletypes.StoreKey, - govplusmoduletypes.StoreKey, - vaultmoduletypes.StoreKey, - revsharemoduletypes.StoreKey, - accountplusmoduletypes.StoreKey, - marketmapmoduletypes.StoreKey, - affiliatesmoduletypes.StoreKey, - ) - keys[authtypes.StoreKey] = keys[authtypes.StoreKey].WithLocking() - tkeys := storetypes.NewTransientStoreKeys( - paramstypes.TStoreKey, - clobmoduletypes.TransientStoreKey, - statsmoduletypes.TransientStoreKey, - rewardsmoduletypes.TransientStoreKey, - indexer_manager.TransientStoreKey, - streaming.StreamingManagerTransientStoreKey, - perpetualsmoduletypes.TransientStoreKey, - ) - memKeys := storetypes.NewMemoryStoreKeys(capabilitytypes.MemStoreKey, clobmoduletypes.MemStoreKey) + keys, memKeys, tkeys := StoreKeys() app := &App{ BaseApp: bApp, @@ -1574,6 +1531,19 @@ func New( app.MountTransientStores(tkeys) app.MountMemoryStores(memKeys) + // wire up the versiondb's `StreamingService` and `MultiStore`. + // we don't support other streaming service, versiondb will override the streaming manager. + if cast.ToBool(appOpts.Get("versiondb.enable")) { + logger.Info("******************** VersionDB enabled *************************") + qms, err := app.setupVersionDB(homePath, keys, tkeys, memKeys) + if err != nil { + panic(err) + } + app.qms = qms.(RootMultiStore) + } else { + logger.Info("****************** VersionDB disabled; using standard store") + } + // initialize BaseApp app.SetInitChainer(app.InitChainer) app.setAnteHandler(encodingConfig.TxConfig) @@ -1613,6 +1583,13 @@ func New( if err := app.LoadLatestVersion(); err != nil { tmos.Exit(err.Error()) } + if app.qms != nil { + v1 := app.qms.LatestVersion() + v2 := app.LastBlockHeight() + if v1 > 0 && v1 != v2 { + tmos.Exit(fmt.Sprintf("versiondb latest version %d don't match iavl latest version %d", v1, v2)) + } + } } app.initializeRateLimiters() @@ -2168,3 +2145,65 @@ func getFullNodeStreamingManagerFromOptions( } return streaming.NewNoopGrpcStreamingManager(), wsServer } + +func StoreKeys() ( + map[string]*storetypes.KVStoreKey, + map[string]*storetypes.MemoryStoreKey, + map[string]*storetypes.TransientStoreKey, +) { + keys := storetypes.NewKVStoreKeys( + authtypes.StoreKey, + authzkeeper.StoreKey, + banktypes.StoreKey, + stakingtypes.StoreKey, + crisistypes.StoreKey, + distrtypes.StoreKey, + slashingtypes.StoreKey, + govtypes.StoreKey, + paramstypes.StoreKey, + consensusparamtypes.StoreKey, + upgradetypes.StoreKey, + feegrant.StoreKey, + ibcexported.StoreKey, + ibctransfertypes.StoreKey, + ratelimitmoduletypes.StoreKey, + icacontrollertypes.StoreKey, + icahosttypes.StoreKey, + evidencetypes.StoreKey, + capabilitytypes.StoreKey, + pricesmoduletypes.StoreKey, + assetsmoduletypes.StoreKey, + blocktimemoduletypes.StoreKey, + bridgemoduletypes.StoreKey, + feetiersmoduletypes.StoreKey, + listingmoduletypes.StoreKey, + perpetualsmoduletypes.StoreKey, + satypes.StoreKey, + statsmoduletypes.StoreKey, + vestmoduletypes.StoreKey, + rewardsmoduletypes.StoreKey, + clobmoduletypes.StoreKey, + sendingmoduletypes.StoreKey, + delaymsgmoduletypes.StoreKey, + epochsmoduletypes.StoreKey, + govplusmoduletypes.StoreKey, + vaultmoduletypes.StoreKey, + revsharemoduletypes.StoreKey, + accountplusmoduletypes.StoreKey, + marketmapmoduletypes.StoreKey, + affiliatesmoduletypes.StoreKey, + ) + keys[authtypes.StoreKey] = keys[authtypes.StoreKey].WithLocking() + tkeys := storetypes.NewTransientStoreKeys( + paramstypes.TStoreKey, + clobmoduletypes.TransientStoreKey, + statsmoduletypes.TransientStoreKey, + rewardsmoduletypes.TransientStoreKey, + indexer_manager.TransientStoreKey, + streaming.StreamingManagerTransientStoreKey, + perpetualsmoduletypes.TransientStoreKey, + ) + memKeys := storetypes.NewMemoryStoreKeys(capabilitytypes.MemStoreKey, clobmoduletypes.MemStoreKey) + + return keys, memKeys, tkeys +} diff --git a/protocol/app/versiondb.go b/protocol/app/versiondb.go new file mode 100644 index 0000000000..2c642fe697 --- /dev/null +++ b/protocol/app/versiondb.go @@ -0,0 +1,55 @@ +//go:build rocksdb +// +build rocksdb + +package app + +import ( + "os" + "path/filepath" + + "github.com/crypto-org-chain/cronos/versiondb" + "github.com/crypto-org-chain/cronos/versiondb/tsrocksdb" + + storetypes "cosmossdk.io/store/types" +) + +func (app *App) setupVersionDB( + homePath string, + keys map[string]*storetypes.KVStoreKey, + tkeys map[string]*storetypes.TransientStoreKey, + memKeys map[string]*storetypes.MemoryStoreKey, +) (storetypes.MultiStore, error) { + dataDir := filepath.Join(homePath, "data", "versiondb") + if err := os.MkdirAll(dataDir, os.ModePerm); err != nil { + return nil, err + } + versionDB, err := tsrocksdb.NewStore(dataDir) + if err != nil { + return nil, err + } + + // always listen for all keys to simplify configuration + exposedKeys := make([]storetypes.StoreKey, 0, len(keys)) + for _, key := range keys { + exposedKeys = append(exposedKeys, key) + } + app.CommitMultiStore().AddListeners(exposedKeys) + + // register in app streaming manager + app.SetStreamingManager(storetypes.StreamingManager{ + ABCIListeners: []storetypes.ABCIListener{versiondb.NewStreamingService(versionDB)}, + StopNodeOnErr: true, + }) + + delegatedStoreKeys := make(map[storetypes.StoreKey]struct{}) + for _, k := range tkeys { + delegatedStoreKeys[k] = struct{}{} + } + for _, k := range memKeys { + delegatedStoreKeys[k] = struct{}{} + } + + verDB := versiondb.NewMultiStore(app.CommitMultiStore(), versionDB, keys, delegatedStoreKeys) + app.SetQueryMultiStore(verDB) + return verDB, nil +} diff --git a/protocol/app/versiondb_placeholder.go b/protocol/app/versiondb_placeholder.go new file mode 100644 index 0000000000..04b05b8ca3 --- /dev/null +++ b/protocol/app/versiondb_placeholder.go @@ -0,0 +1,19 @@ +//go:build !rocksdb +// +build !rocksdb + +package app + +import ( + "errors" + + storetypes "cosmossdk.io/store/types" +) + +func (app *App) setupVersionDB( + homePath string, + keys map[string]*storetypes.KVStoreKey, + tkeys map[string]*storetypes.TransientStoreKey, + memKeys map[string]*storetypes.MemoryStoreKey, +) (storetypes.MultiStore, error) { + return nil, errors.New("versiondb is not supported in this binary") +} diff --git a/protocol/cmd/dydxprotocold/cmd/root.go b/protocol/cmd/dydxprotocold/cmd/root.go index d12d8516e3..c6307d20f6 100644 --- a/protocol/cmd/dydxprotocold/cmd/root.go +++ b/protocol/cmd/dydxprotocold/cmd/root.go @@ -240,6 +240,12 @@ func initRootCmd( ) rootCmd.AddCommand(rosettaCmd.RosettaCommand(tempApp.InterfaceRegistry(), tempApp.AppCodec())) + + // versiondb changeset commands + changeSetCmd := ChangeSetCmd() + if changeSetCmd != nil { + rootCmd.AddCommand(changeSetCmd) + } } // autoCliOpts returns options based upon the modules in the dYdX v4 app. diff --git a/protocol/cmd/dydxprotocold/cmd/versiondb.go b/protocol/cmd/dydxprotocold/cmd/versiondb.go new file mode 100644 index 0000000000..a6ecac0717 --- /dev/null +++ b/protocol/cmd/dydxprotocold/cmd/versiondb.go @@ -0,0 +1,28 @@ +//go:build rocksdb +// +build rocksdb + +package cmd + +import ( + "sort" + + versiondbclient "github.com/crypto-org-chain/cronos/versiondb/client" + "github.com/dydxprotocol/v4-chain/protocol/app" + "github.com/dydxprotocol/v4-chain/protocol/cmd/dydxprotocold/opendb" + "github.com/spf13/cobra" +) + +func ChangeSetCmd() *cobra.Command { + keys, _, _ := app.StoreKeys() + storeNames := make([]string, 0, len(keys)) + for name := range keys { + storeNames = append(storeNames, name) + } + sort.Strings(storeNames) + + return versiondbclient.ChangeSetGroupCmd(versiondbclient.Options{ + DefaultStores: storeNames, + OpenReadOnlyDB: opendb.OpenReadOnlyDB, + AppRocksDBOptions: opendb.NewRocksdbOptions, + }) +} diff --git a/protocol/cmd/dydxprotocold/cmd/versiondb_placeholder.go b/protocol/cmd/dydxprotocold/cmd/versiondb_placeholder.go new file mode 100644 index 0000000000..43e559f8ed --- /dev/null +++ b/protocol/cmd/dydxprotocold/cmd/versiondb_placeholder.go @@ -0,0 +1,12 @@ +//go:build !rocksdb +// +build !rocksdb + +package cmd + +import ( + "github.com/spf13/cobra" +) + +func ChangeSetCmd() *cobra.Command { + return nil +} diff --git a/protocol/cmd/dydxprotocold/opendb/opendb.go b/protocol/cmd/dydxprotocold/opendb/opendb.go new file mode 100644 index 0000000000..8b737f4910 --- /dev/null +++ b/protocol/cmd/dydxprotocold/opendb/opendb.go @@ -0,0 +1,20 @@ +//go:build !rocksdb +// +build !rocksdb + +package opendb + +import ( + "path/filepath" + + dbm "github.com/cosmos/cosmos-db" +) + +func OpenDB(home string, backendType dbm.BackendType) (dbm.DB, error) { + dataDir := filepath.Join(home, "data") + return dbm.NewDB("application", backendType, dataDir) +} + +// OpenReadOnlyDB opens rocksdb backend in read-only mode. +func OpenReadOnlyDB(home string, backendType dbm.BackendType) (dbm.DB, error) { + return OpenDB(home, backendType) +} diff --git a/protocol/cmd/dydxprotocold/opendb/opendb_rocksdb.go b/protocol/cmd/dydxprotocold/opendb/opendb_rocksdb.go new file mode 100644 index 0000000000..149b97c62c --- /dev/null +++ b/protocol/cmd/dydxprotocold/opendb/opendb_rocksdb.go @@ -0,0 +1,99 @@ +//go:build rocksdb +// +build rocksdb + +package opendb + +import ( + "path/filepath" + "runtime" + + dbm "github.com/cosmos/cosmos-db" + "github.com/linxGnu/grocksdb" +) + +func OpenDB(home string, backendType dbm.BackendType) (dbm.DB, error) { + dataDir := filepath.Join(home, "data") + if backendType == dbm.RocksDBBackend { + // customize rocksdb options + db, err := grocksdb.OpenDb(NewRocksdbOptions(false), filepath.Join(dataDir, "application.db")) + if err != nil { + return nil, err + } + ro := grocksdb.NewDefaultReadOptions() + wo := grocksdb.NewDefaultWriteOptions() + woSync := grocksdb.NewDefaultWriteOptions() + woSync.SetSync(true) + return dbm.NewRocksDBWithRawDB(db, ro, wo, woSync), nil + } + + return dbm.NewDB("application", backendType, dataDir) +} + +// OpenReadOnlyDB opens rocksdb backend in read-only mode. +func OpenReadOnlyDB(home string, backendType dbm.BackendType) (dbm.DB, error) { + dataDir := filepath.Join(home, "data") + if backendType == dbm.RocksDBBackend { + // customize rocksdb options + db, err := grocksdb.OpenDbForReadOnly(NewRocksdbOptions(false), filepath.Join(dataDir, "application.db"), false) + if err != nil { + return nil, err + } + + ro := grocksdb.NewDefaultReadOptions() + wo := grocksdb.NewDefaultWriteOptions() + woSync := grocksdb.NewDefaultWriteOptions() + woSync.SetSync(true) + return dbm.NewRocksDBWithRawDB(db, ro, wo, woSync), nil + } + + return dbm.NewDB("application", backendType, dataDir) +} + +func NewRocksdbOptions(sstFileWriter bool) *grocksdb.Options { + opts := grocksdb.NewDefaultOptions() + opts.SetCreateIfMissing(true) + opts.IncreaseParallelism(runtime.NumCPU()) + opts.OptimizeLevelStyleCompaction(512 * 1024 * 1024) + opts.SetTargetFileSizeMultiplier(2) + + // block based table options + bbto := grocksdb.NewDefaultBlockBasedTableOptions() + + // 1G block cache + bbto.SetBlockCache(grocksdb.NewLRUCache(1 << 30)) + + // http://rocksdb.org/blog/2021/12/29/ribbon-filter.html + bbto.SetFilterPolicy(grocksdb.NewRibbonHybridFilterPolicy(9.9, 1)) + + // partition index + // http://rocksdb.org/blog/2017/05/12/partitioned-index-filter.html + bbto.SetIndexType(grocksdb.KTwoLevelIndexSearchIndexType) + bbto.SetPartitionFilters(true) + bbto.SetOptimizeFiltersForMemory(true) + + // reduce memory usage + bbto.SetCacheIndexAndFilterBlocks(true) + bbto.SetPinTopLevelIndexAndFilter(true) + bbto.SetPinL0FilterAndIndexBlocksInCache(true) + + // hash index is better for iavl tree which mostly do point lookup. + bbto.SetDataBlockIndexType(grocksdb.KDataBlockIndexTypeBinarySearchAndHash) + + opts.SetBlockBasedTableFactory(bbto) + + // in iavl tree, we almost always query existing keys + opts.SetOptimizeFiltersForHits(true) + + // heavier compression option at bottommost level, + // 110k dict bytes is default in zstd library, + // train bytes is recommended to be set at 100x dict bytes. + opts.SetBottommostCompression(grocksdb.ZSTDCompression) + compressOpts := grocksdb.NewDefaultCompressionOptions() + compressOpts.Level = 12 + if !sstFileWriter { + compressOpts.MaxDictBytes = 110 * 1024 + opts.SetBottommostCompressionOptionsZstdMaxTrainBytes(compressOpts.MaxDictBytes*100, true) + } + opts.SetBottommostCompressionOptions(compressOpts, true) + return opts +} diff --git a/protocol/go.mod b/protocol/go.mod index 48525eb618..5ca6f2fde6 100644 --- a/protocol/go.mod +++ b/protocol/go.mod @@ -60,6 +60,7 @@ require ( github.com/cosmos/ibc-go/modules/capability v1.0.1 github.com/cosmos/ibc-go/v8 v8.5.1 github.com/cosmos/rosetta v0.50.3 + github.com/crypto-org-chain/cronos/versiondb v0.0.0-00010101000000-000000000000 github.com/deckarep/golang-set/v2 v2.6.0 github.com/dydxprotocol/slinky v1.3.2 github.com/ethereum/go-ethereum v1.14.11 @@ -67,6 +68,7 @@ require ( github.com/gorilla/websocket v1.5.3 github.com/hashicorp/go-metrics v0.5.3 github.com/hashicorp/go-version v1.7.0 + github.com/linxGnu/grocksdb v1.9.3 github.com/ory/dockertest/v3 v3.10.0 github.com/pelletier/go-toml v1.9.5 github.com/rs/zerolog v1.33.0 @@ -304,7 +306,6 @@ require ( github.com/leodido/go-urn v1.2.4 // indirect github.com/leonklingele/grouper v1.1.2 // indirect github.com/lib/pq v1.10.9 // indirect - github.com/linxGnu/grocksdb v1.9.3 // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/lovoo/goka v1.1.9 // indirect github.com/macabu/inamedparam v0.1.3 // indirect @@ -474,6 +475,7 @@ replace ( // memiavl refers to old packages so we need to replace them with the correct naming github.com/crypto-org-chain/cronos/memiavl => github.com/crypto-org-chain/cronos/memiavl v0.0.5-0.20241028093154-0f94930c27ce github.com/crypto-org-chain/cronos/store => github.com/01builders/dydx-cronos/store v0.0.5-dydx3 + github.com/crypto-org-chain/cronos/versiondb => github.com/crypto-org-chain/cronos/versiondb v0.0.0-20250829061541-4b7f16f91ba0 github.com/tendermint/tendermint => github.com/cometbft/cometbft v0.38.15 ) diff --git a/protocol/go.sum b/protocol/go.sum index 03102e4d88..bee2a228fc 100644 --- a/protocol/go.sum +++ b/protocol/go.sum @@ -926,6 +926,8 @@ github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/crypto-org-chain/cronos/memiavl v0.0.5-0.20241028093154-0f94930c27ce h1:yRF9Zsk4fzVBhBQEjkA4oE+Q3Q/Kgaj/UX4gK+xwaAs= github.com/crypto-org-chain/cronos/memiavl v0.0.5-0.20241028093154-0f94930c27ce/go.mod h1:IyRvgFKOQPC/Qdx543PGl6WgeDOU+hWdv+xLz3stotc= +github.com/crypto-org-chain/cronos/versiondb v0.0.0-20250829061541-4b7f16f91ba0 h1:z2r3n4SwXT/5iOORWYbLBBCnSmegluD9GnOO0QOQSbc= +github.com/crypto-org-chain/cronos/versiondb v0.0.0-20250829061541-4b7f16f91ba0/go.mod h1:Y1uyFhZn/8jZkZfr3W/alzrO3DfcfWPuA3UwCM0Ah0g= github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= github.com/daaku/go.zipexe v1.0.2 h1:Zg55YLYTr7M9wjKn8SY/WcpuuEi+kR2u4E8RhvpyXmk= From be6cccc8074adb87829a8ce9e7081aa032362fea Mon Sep 17 00:00:00 2001 From: pthmas <9058370+pthmas@users.noreply.github.com> Date: Wed, 5 Nov 2025 16:10:36 +0100 Subject: [PATCH 12/14] use 01builders fork of versiondb to fix branch nodes overwritting leafs --- protocol/go.mod | 2 +- protocol/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/go.mod b/protocol/go.mod index 5ca6f2fde6..182edbdc62 100644 --- a/protocol/go.mod +++ b/protocol/go.mod @@ -475,7 +475,7 @@ replace ( // memiavl refers to old packages so we need to replace them with the correct naming github.com/crypto-org-chain/cronos/memiavl => github.com/crypto-org-chain/cronos/memiavl v0.0.5-0.20241028093154-0f94930c27ce github.com/crypto-org-chain/cronos/store => github.com/01builders/dydx-cronos/store v0.0.5-dydx3 - github.com/crypto-org-chain/cronos/versiondb => github.com/crypto-org-chain/cronos/versiondb v0.0.0-20250829061541-4b7f16f91ba0 + github.com/crypto-org-chain/cronos/versiondb => github.com/01builders/dydx-cronos/versiondb v0.0.0-20251105153052-e07469b731bb github.com/tendermint/tendermint => github.com/cometbft/cometbft v0.38.15 ) diff --git a/protocol/go.sum b/protocol/go.sum index bee2a228fc..94b4faa16f 100644 --- a/protocol/go.sum +++ b/protocol/go.sum @@ -649,6 +649,8 @@ github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx h1:7zpjRUXs6m4TerknjJB4D github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx/go.mod h1:ZW4eIE98wivInyQyhxU2nHqEielc/iZAcr41qNyWSrw= github.com/01builders/dydx-cronos/store v0.0.5-dydx3 h1:dFrDfQ4IoVzmPDwN8pK4x5xmHzOno1ffMniGpEBg4Mc= github.com/01builders/dydx-cronos/store v0.0.5-dydx3/go.mod h1:II1a1DGyN3uD1hPS6dUFxI6RnCZz0ZW2v7GMgfjTp6o= +github.com/01builders/dydx-cronos/versiondb v0.0.0-20251105153052-e07469b731bb h1:Gx3OKzvcX3dZg36VQp/WZDN7q28yZjTEhCVJi/Y8y9w= +github.com/01builders/dydx-cronos/versiondb v0.0.0-20251105153052-e07469b731bb/go.mod h1:Y1uyFhZn/8jZkZfr3W/alzrO3DfcfWPuA3UwCM0Ah0g= github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8= github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= @@ -926,8 +928,6 @@ github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/crypto-org-chain/cronos/memiavl v0.0.5-0.20241028093154-0f94930c27ce h1:yRF9Zsk4fzVBhBQEjkA4oE+Q3Q/Kgaj/UX4gK+xwaAs= github.com/crypto-org-chain/cronos/memiavl v0.0.5-0.20241028093154-0f94930c27ce/go.mod h1:IyRvgFKOQPC/Qdx543PGl6WgeDOU+hWdv+xLz3stotc= -github.com/crypto-org-chain/cronos/versiondb v0.0.0-20250829061541-4b7f16f91ba0 h1:z2r3n4SwXT/5iOORWYbLBBCnSmegluD9GnOO0QOQSbc= -github.com/crypto-org-chain/cronos/versiondb v0.0.0-20250829061541-4b7f16f91ba0/go.mod h1:Y1uyFhZn/8jZkZfr3W/alzrO3DfcfWPuA3UwCM0Ah0g= github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo= github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc= github.com/daaku/go.zipexe v1.0.2 h1:Zg55YLYTr7M9wjKn8SY/WcpuuEi+kR2u4E8RhvpyXmk= From 3357dbc56921230dbccd53df08ece82868d639fb Mon Sep 17 00:00:00 2001 From: pthmas <9058370+pthmas@users.noreply.github.com> Date: Wed, 5 Nov 2025 17:15:28 +0100 Subject: [PATCH 13/14] update cronos store to use version with sequential snapshot writing --- protocol/go.mod | 2 +- protocol/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/go.mod b/protocol/go.mod index 182edbdc62..ef95f2945c 100644 --- a/protocol/go.mod +++ b/protocol/go.mod @@ -475,7 +475,7 @@ replace ( // memiavl refers to old packages so we need to replace them with the correct naming github.com/crypto-org-chain/cronos/memiavl => github.com/crypto-org-chain/cronos/memiavl v0.0.5-0.20241028093154-0f94930c27ce github.com/crypto-org-chain/cronos/store => github.com/01builders/dydx-cronos/store v0.0.5-dydx3 - github.com/crypto-org-chain/cronos/versiondb => github.com/01builders/dydx-cronos/versiondb v0.0.0-20251105153052-e07469b731bb + github.com/crypto-org-chain/cronos/versiondb => github.com/01builders/dydx-cronos/versiondb v0.0.0-20251105161151-54026f24eea4 github.com/tendermint/tendermint => github.com/cometbft/cometbft v0.38.15 ) diff --git a/protocol/go.sum b/protocol/go.sum index 94b4faa16f..88f7ff1127 100644 --- a/protocol/go.sum +++ b/protocol/go.sum @@ -649,8 +649,8 @@ github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx h1:7zpjRUXs6m4TerknjJB4D github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx/go.mod h1:ZW4eIE98wivInyQyhxU2nHqEielc/iZAcr41qNyWSrw= github.com/01builders/dydx-cronos/store v0.0.5-dydx3 h1:dFrDfQ4IoVzmPDwN8pK4x5xmHzOno1ffMniGpEBg4Mc= github.com/01builders/dydx-cronos/store v0.0.5-dydx3/go.mod h1:II1a1DGyN3uD1hPS6dUFxI6RnCZz0ZW2v7GMgfjTp6o= -github.com/01builders/dydx-cronos/versiondb v0.0.0-20251105153052-e07469b731bb h1:Gx3OKzvcX3dZg36VQp/WZDN7q28yZjTEhCVJi/Y8y9w= -github.com/01builders/dydx-cronos/versiondb v0.0.0-20251105153052-e07469b731bb/go.mod h1:Y1uyFhZn/8jZkZfr3W/alzrO3DfcfWPuA3UwCM0Ah0g= +github.com/01builders/dydx-cronos/versiondb v0.0.0-20251105161151-54026f24eea4 h1:8gllIQvmG5lbUbfCMRsP6f80oWZjz5MThAAuseczOVQ= +github.com/01builders/dydx-cronos/versiondb v0.0.0-20251105161151-54026f24eea4/go.mod h1:Y1uyFhZn/8jZkZfr3W/alzrO3DfcfWPuA3UwCM0Ah0g= github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8= github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0= github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= From cf46a15857fa31f09f894787e2c8c57671e342b0 Mon Sep 17 00:00:00 2001 From: pthmas <9058370+pthmas@users.noreply.github.com> Date: Wed, 3 Dec 2025 11:56:11 +0100 Subject: [PATCH 14/14] use 01builders fork of comet to fix recheck idling --- protocol/go.mod | 2 +- protocol/go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/protocol/go.mod b/protocol/go.mod index ef95f2945c..747653dfbf 100644 --- a/protocol/go.mod +++ b/protocol/go.mod @@ -488,7 +488,7 @@ replace ( cosmossdk.io/core => cosmossdk.io/core v0.11.0 cosmossdk.io/store => github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx // Use dYdX fork of CometBFT - github.com/cometbft/cometbft => github.com/dydxprotocol/cometbft v0.38.6-0.20251021155510-74157e3aac09 + github.com/cometbft/cometbft => github.com/01builders/dydx-cometbft v0.0.0-20251125154254-4bb87b5ef594 // Use dYdX fork of Cosmos SDK github.com/cosmos/cosmos-sdk => github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl3 //github.com/cosmos/iavl => github.com/dydxprotocol/iavl v1.1.1-0.20240509161911-1c8b8e787e85 diff --git a/protocol/go.sum b/protocol/go.sum index 88f7ff1127..ca7046fbc5 100644 --- a/protocol/go.sum +++ b/protocol/go.sum @@ -643,6 +643,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= +github.com/01builders/dydx-cometbft v0.0.0-20251125154254-4bb87b5ef594 h1:nAdIGk2FyC+h9Lux0YhQmSaWMv7C7bpdrqR2EMmqkNE= +github.com/01builders/dydx-cometbft v0.0.0-20251125154254-4bb87b5ef594/go.mod h1:IcJVLogjmvbfWl2bFWJ0gwl8Rv5kgfg4/N+vUA9HARs= github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl3 h1:VC3kuGtK0BWDZz6MdzbAMe7qg3npzRZIwQwfi5UJYbk= github.com/01builders/dydx-cosmos-sdk v0.50.5-dydx-memiavl3/go.mod h1:4hp2dWoiVmg2BOXWhc7+a76nTGdui6gnnz2zE+Hly1k= github.com/01builders/dydx-cosmos-sdk/store v0.5.6-dydx h1:7zpjRUXs6m4TerknjJB4D7HVsJh8XQO3gTObXAXu6D8= @@ -970,8 +972,6 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= -github.com/dydxprotocol/cometbft v0.38.6-0.20251021155510-74157e3aac09 h1:b0FQumkBGAMfPs9Q2tw9mR2lPg6fTC/egVzgILyojIA= -github.com/dydxprotocol/cometbft v0.38.6-0.20251021155510-74157e3aac09/go.mod h1:IcJVLogjmvbfWl2bFWJ0gwl8Rv5kgfg4/N+vUA9HARs= github.com/dydxprotocol/ibc-go/v8 v8.0.0-rc.0.0.20250312180215-8733b3edf43a h1:aF1rORtUApr+N6dWsNiT/G9H2ysfjyj5DiVRU8CRDaU= github.com/dydxprotocol/ibc-go/v8 v8.0.0-rc.0.0.20250312180215-8733b3edf43a/go.mod h1:C6IiJom0F3cIQCD5fKwVPDrDK9j/xTu563AWuOmXois= github.com/dydxprotocol/slinky v1.3.2 h1:JSMKq37MDlei28ec4o6sB2RaihWPL1xQ8BoH9wGxg9w=