diff --git a/indexer/packages/kafka/__tests__/batch-kafka-producer.test.ts b/indexer/packages/kafka/__tests__/batch-kafka-producer.test.ts index f62b28423b..4da777c134 100644 --- a/indexer/packages/kafka/__tests__/batch-kafka-producer.test.ts +++ b/indexer/packages/kafka/__tests__/batch-kafka-producer.test.ts @@ -1,16 +1,18 @@ import { KafkaTopics } from '../src'; import { BatchKafkaProducer, ProducerMessage } from '../src/batch-kafka-producer'; import { producer } from '../src/producer'; +import { IHeaders } from 'kafkajs'; import _ from 'lodash'; interface TestMessage { key?: string, value: string, + headers?: IHeaders, } function testMessage2ProducerMessage(data: TestMessage): ProducerMessage { const key: Buffer | undefined = data.key === undefined ? undefined : Buffer.from(data.key); - return { key, value: Buffer.from(data.value) }; + return { key, value: Buffer.from(data.value), headers: data.headers }; } function testMessage2ProducerMessages(data: TestMessage[]): ProducerMessage[] { @@ -35,9 +37,9 @@ describe('batch-kafka-producer', () => { [ 'will send key if key is not undefined', 5, - [{ key: '1', value: 'a' }, { key: '2', value: 'b' }, { key: '3', value: 'c' }], + [{ key: '1', value: 'a' }, { key: '2', value: 'b' }, { key: '3', value: 'c', headers: { timestamp: 'value' } }], [[{ key: '1', value: 'a' }, { key: '2', value: 'b' }]], - [{ key: '3', value: 'c' }], + [{ key: '3', value: 'c', headers: { timestamp: 'value' } }], ], [ 'will not send message until the batch size is reached', @@ -104,7 +106,9 @@ describe('batch-kafka-producer', () => { for (const msg of messages) { const key: Buffer | undefined = msg.key === undefined ? undefined : Buffer.from(msg.key); - batchProducer.addMessageAndMaybeFlush({ value: Buffer.from(msg.value), key }); + batchProducer.addMessageAndMaybeFlush( + { value: Buffer.from(msg.value), key, headers: msg.headers }, + ); } expect(producerSendMock.mock.calls).toHaveLength(expectedMessagesPerCall.length); diff --git a/indexer/packages/kafka/src/batch-kafka-producer.ts b/indexer/packages/kafka/src/batch-kafka-producer.ts index 6885e29ae9..0ae478680e 100644 --- a/indexer/packages/kafka/src/batch-kafka-producer.ts +++ b/indexer/packages/kafka/src/batch-kafka-producer.ts @@ -1,5 +1,5 @@ import { logger } from '@dydxprotocol-indexer/base'; -import { Producer, RecordMetadata } from 'kafkajs'; +import { IHeaders, Producer, RecordMetadata } from 'kafkajs'; import _ from 'lodash'; import { KafkaTopics } from './types'; @@ -10,6 +10,7 @@ import { KafkaTopics } from './types'; export type ProducerMessage = { key?: Buffer, value: Buffer, + headers?: IHeaders, }; /** @@ -52,7 +53,7 @@ export class BatchKafkaProducer { if (this.currentSize + msgBuffer.byteLength + keyByteLength > this.maxBatchSizeBytes) { this.sendBatch(); } - this.producerMessages.push({ key: message.key, value: msgBuffer }); + this.producerMessages.push({ key: message.key, value: msgBuffer, headers: message.headers }); this.currentSize += msgBuffer.byteLength; this.currentSize += keyByteLength; } diff --git a/indexer/packages/postgres/__tests__/helpers/constants.ts b/indexer/packages/postgres/__tests__/helpers/constants.ts index 5928862fe9..84791623a0 100644 --- a/indexer/packages/postgres/__tests__/helpers/constants.ts +++ b/indexer/packages/postgres/__tests__/helpers/constants.ts @@ -87,6 +87,20 @@ export const defaultSubaccount3: SubaccountCreateObject = { updatedAtHeight: createdHeight, }; +export const isolatedSubaccount: SubaccountCreateObject = { + address: defaultAddress, + subaccountNumber: 128, + updatedAt: createdDateTime.toISO(), + updatedAtHeight: createdHeight, +}; + +export const isolatedSubaccount2: SubaccountCreateObject = { + address: defaultAddress, + subaccountNumber: 256, + updatedAt: createdDateTime.toISO(), + updatedAtHeight: createdHeight, +}; + export const defaultWalletAddress: string = 'defaultWalletAddress'; export const defaultSubaccountId: string = SubaccountTable.uuid( @@ -101,6 +115,14 @@ export const defaultSubaccountId3: string = SubaccountTable.uuid( defaultAddress, defaultSubaccount3.subaccountNumber, ); +export const isolatedSubaccountId: string = SubaccountTable.uuid( + defaultAddress, + isolatedSubaccount.subaccountNumber, +); +export const isolatedSubaccountId2: string = SubaccountTable.uuid( + defaultAddress, + isolatedSubaccount2.subaccountNumber, +); // ============== Wallets ============== export const defaultWallet: WalletCreateObject = { @@ -212,6 +234,42 @@ export const defaultPerpetualMarket3: PerpetualMarketCreateObject = { liquidityTierId: 0, }; +export const isolatedPerpetualMarket: PerpetualMarketCreateObject = { + id: '3', + clobPairId: '4', + ticker: 'ISO-USD', + marketId: 3, + status: PerpetualMarketStatus.ACTIVE, + priceChange24H: '0.000000001', + volume24H: '10000000', + trades24H: 200, + nextFundingRate: '1.2', + openInterest: '40000', + quantumConversionExponent: -16, + atomicResolution: -2, + subticksPerTick: 10, + stepBaseQuantums: 1, + liquidityTierId: 0, +}; + +export const isolatedPerpetualMarket2: PerpetualMarketCreateObject = { + id: '4', + clobPairId: '5', + ticker: 'ISO2-USD', + marketId: 4, + status: PerpetualMarketStatus.ACTIVE, + priceChange24H: '0.000000001', + volume24H: '10000000', + trades24H: 200, + nextFundingRate: '1.2', + openInterest: '40000', + quantumConversionExponent: -16, + atomicResolution: -2, + subticksPerTick: 10, + stepBaseQuantums: 1, + liquidityTierId: 0, +}; + // ============== Orders ============== export const defaultOrder: OrderCreateObject = { @@ -233,6 +291,25 @@ export const defaultOrder: OrderCreateObject = { updatedAtHeight: '1', }; +export const isolatedMarketOrder: OrderCreateObject = { + subaccountId: isolatedSubaccountId, + clientId: '1', + clobPairId: '4', + side: OrderSide.BUY, + size: '25', + totalFilled: '0', + price: '20000', + type: OrderType.LIMIT, + status: OrderStatus.OPEN, + timeInForce: TimeInForce.FOK, + reduceOnly: false, + goodTilBlock: '100', + orderFlags: ORDER_FLAG_SHORT_TERM.toString(), + clientMetadata: '0', + updatedAt: '2023-01-22T00:00:00.000Z', + updatedAtHeight: '1', +}; + export const defaultOrderGoodTilBlockTime: OrderCreateObject = { ...defaultOrder, clientId: '2', @@ -257,6 +334,13 @@ export const defaultOrderId: string = OrderTable.uuid( defaultOrder.orderFlags, ); +export const isolatedMarketOrderId: string = OrderTable.uuid( + isolatedMarketOrder.subaccountId, + isolatedMarketOrder.clientId, + isolatedMarketOrder.clobPairId, + isolatedMarketOrder.orderFlags, +); + export const defaultOrderGoodTilBlockTimeId: string = OrderTable.uuid( defaultOrderGoodTilBlockTime.subaccountId, defaultOrderGoodTilBlockTime.clientId, @@ -381,6 +465,42 @@ export const defaultFill: FillCreateObject = { fee: '1.1', }; +export const isolatedMarketFill: FillCreateObject = { + subaccountId: isolatedSubaccountId, + side: OrderSide.BUY, + liquidity: Liquidity.TAKER, + type: FillType.LIMIT, + clobPairId: '4', + orderId: isolatedMarketOrderId, + size: '10', + price: '20000', + quoteAmount: '200000', + eventId: defaultTendermintEventId2, + transactionHash: '', // TODO: Add a real transaction Hash + createdAt: createdDateTime.toISO(), + createdAtHeight: createdHeight, + clientMetadata: '0', + fee: '1.1', +}; + +export const isolatedMarketFill2: FillCreateObject = { + subaccountId: isolatedSubaccountId2, + side: OrderSide.BUY, + liquidity: Liquidity.TAKER, + type: FillType.LIMIT, + clobPairId: '4', + orderId: isolatedMarketOrderId, + size: '10', + price: '20000', + quoteAmount: '200000', + eventId: defaultTendermintEventId3, + transactionHash: '', // TODO: Add a real transaction Hash + createdAt: createdDateTime.toISO(), + createdAtHeight: createdHeight, + clientMetadata: '0', + fee: '1.1', +}; + // ============== Transfers ============== export const defaultTransfer: TransferCreateObject = { @@ -480,6 +600,22 @@ export const defaultMarket3: MarketCreateObject = { oraclePrice: '0.000000065', }; +export const isolatedMarket: MarketCreateObject = { + id: 3, + pair: 'ISO-USD', + exponent: -12, + minPriceChangePpm: 50, + oraclePrice: '0.000000075', +}; + +export const isolatedMarket2: MarketCreateObject = { + id: 4, + pair: 'ISO2-USD', + exponent: -12, + minPriceChangePpm: 50, + oraclePrice: '0.000000085', +}; + // ============== LiquidityTiers ============== export const defaultLiquidityTier: LiquidityTiersCreateObject = { diff --git a/indexer/packages/postgres/__tests__/helpers/mock-generators.ts b/indexer/packages/postgres/__tests__/helpers/mock-generators.ts index d70d0004e7..86d6f6e3b7 100644 --- a/indexer/packages/postgres/__tests__/helpers/mock-generators.ts +++ b/indexer/packages/postgres/__tests__/helpers/mock-generators.ts @@ -27,17 +27,27 @@ import { defaultTendermintEvent3, defaultTendermintEvent4, defaultWallet, + isolatedMarket, + isolatedMarket2, + isolatedPerpetualMarket, + isolatedPerpetualMarket2, + isolatedSubaccount, + isolatedSubaccount2, } from './constants'; export async function seedData() { await Promise.all([ SubaccountTable.create(defaultSubaccount), SubaccountTable.create(defaultSubaccount2), + SubaccountTable.create(isolatedSubaccount), + SubaccountTable.create(isolatedSubaccount2), ]); await Promise.all([ MarketTable.create(defaultMarket), MarketTable.create(defaultMarket2), MarketTable.create(defaultMarket3), + MarketTable.create(isolatedMarket), + MarketTable.create(isolatedMarket2), ]); await Promise.all([ LiquidityTiersTable.create(defaultLiquidityTier), @@ -47,6 +57,8 @@ export async function seedData() { PerpetualMarketTable.create(defaultPerpetualMarket), PerpetualMarketTable.create(defaultPerpetualMarket2), PerpetualMarketTable.create(defaultPerpetualMarket3), + PerpetualMarketTable.create(isolatedPerpetualMarket), + PerpetualMarketTable.create(isolatedPerpetualMarket2), ]); await Promise.all([ BlockTable.create(defaultBlock), diff --git a/indexer/packages/postgres/__tests__/lib/api-translations.test.ts b/indexer/packages/postgres/__tests__/lib/api-translations.test.ts index c1cda29fed..2ba80bc4a3 100644 --- a/indexer/packages/postgres/__tests__/lib/api-translations.test.ts +++ b/indexer/packages/postgres/__tests__/lib/api-translations.test.ts @@ -1,5 +1,10 @@ import { APITimeInForce, TimeInForce } from '../../src/types'; -import { isOrderTIFPostOnly, orderTIFToAPITIF } from '../../src/lib/api-translations'; +import { + getChildSubaccountNums, + getParentSubaccountNum, + isOrderTIFPostOnly, + orderTIFToAPITIF, +} from '../../src/lib/api-translations'; describe('apiTranslations', () => { describe('orderTIFToAPITIF', () => { @@ -31,4 +36,41 @@ describe('apiTranslations', () => { expect(isOrderTIFPostOnly(orderTimeInForce)).toEqual(expectedPostOnly); }); }); + + describe('getChildSubaccountNums', () => { + it('Gets a list of all possible child subaccount numbers for a parent subaccount 0', () => { + const childSubaccounts = getChildSubaccountNums(0); + expect(childSubaccounts.length).toEqual(1000); + expect(childSubaccounts[0]).toEqual(0); + expect(childSubaccounts[1]).toEqual(128); + expect(childSubaccounts[999]).toEqual(128 * 999); + }); + it('Gets a list of all possible child subaccount numbers for a parent subaccount 127', () => { + const childSubaccounts = getChildSubaccountNums(127); + expect(childSubaccounts.length).toEqual(1000); + expect(childSubaccounts[0]).toEqual(127); + expect(childSubaccounts[1]).toEqual(128 + 127); + expect(childSubaccounts[999]).toEqual(128 * 999 + 127); + }); + }); + + describe('getChildSubaccountNums', () => { + it('Throws an error if the parent subaccount number is greater than or equal to the maximum parent subaccount number', () => { + expect(() => getChildSubaccountNums(128)).toThrowError('Parent subaccount number must be less than 128'); + }); + }); + + describe('getParentSubaccountNum', () => { + it('Gets the parent subaccount number from a child subaccount number', () => { + expect(getParentSubaccountNum(0)).toEqual(0); + expect(getParentSubaccountNum(128)).toEqual(0); + expect(getParentSubaccountNum(128 * 999 - 1)).toEqual(127); + }); + }); + + describe('getParentSubaccountNum', () => { + it('Throws an error if the child subaccount number is greater than the max child subaccount number', () => { + expect(() => getParentSubaccountNum(128001)).toThrowError('Child subaccount number must be less than 128000'); + }); + }); }); diff --git a/indexer/packages/postgres/__tests__/stores/funding-index-updates-table.test.ts b/indexer/packages/postgres/__tests__/stores/funding-index-updates-table.test.ts index b824605b77..d42df73764 100644 --- a/indexer/packages/postgres/__tests__/stores/funding-index-updates-table.test.ts +++ b/indexer/packages/postgres/__tests__/stores/funding-index-updates-table.test.ts @@ -8,7 +8,6 @@ import { defaultFundingIndexUpdateId, defaultPerpetualMarket, defaultPerpetualMarket2, - defaultPerpetualMarket3, defaultTendermintEventId2, defaultTendermintEventId3, } from '../helpers/constants'; @@ -212,11 +211,10 @@ describe('funding index update store', () => { '3', ); - expect(fundingIndexMap).toEqual({ - [defaultFundingIndexUpdate.perpetualId]: Big(defaultFundingIndexUpdate.fundingIndex), - [fundingIndexUpdates3.perpetualId]: Big(fundingIndexUpdates3.fundingIndex), - [defaultPerpetualMarket3.id]: Big(0), - }); + expect(fundingIndexMap[defaultFundingIndexUpdate.perpetualId]) + .toEqual(Big(defaultFundingIndexUpdate.fundingIndex)); + expect(fundingIndexMap[fundingIndexUpdates3.perpetualId]) + .toEqual(Big(fundingIndexUpdates3.fundingIndex)); }); it('Gets default funding index of 0 in funding index map if no funding indexes', async () => { @@ -225,11 +223,8 @@ describe('funding index update store', () => { '3', ); - expect(fundingIndexMap).toEqual({ - [defaultPerpetualMarket.id]: Big(0), - [defaultPerpetualMarket2.id]: Big(0), - [defaultPerpetualMarket3.id]: Big(0), - }); + expect(fundingIndexMap[defaultPerpetualMarket.id]).toEqual(Big(0)); + expect(fundingIndexMap[defaultPerpetualMarket2.id]).toEqual(Big(0)); }); it( @@ -242,11 +237,9 @@ describe('funding index update store', () => { '3', ); - expect(fundingIndexMap).toEqual({ - [defaultPerpetualMarket.id]: Big(defaultFundingIndexUpdate.fundingIndex), - [defaultPerpetualMarket2.id]: Big(0), - [defaultPerpetualMarket3.id]: Big(0), - }); + expect(fundingIndexMap[defaultPerpetualMarket.id]) + .toEqual(Big(defaultFundingIndexUpdate.fundingIndex)); + expect(fundingIndexMap[defaultPerpetualMarket2.id]).toEqual(Big(0)); }, ); }); diff --git a/indexer/packages/postgres/__tests__/stores/perpetual-position-table.test.ts b/indexer/packages/postgres/__tests__/stores/perpetual-position-table.test.ts index 68c2b3153d..da274dfdf4 100644 --- a/indexer/packages/postgres/__tests__/stores/perpetual-position-table.test.ts +++ b/indexer/packages/postgres/__tests__/stores/perpetual-position-table.test.ts @@ -81,13 +81,13 @@ describe('PerpetualPosition store', () => { it('Successfully finds all PerpetualPositions', async () => { await PerpetualMarketTable.create({ ...defaultPerpetualMarket, - id: '3', + id: '100', }); await Promise.all([ PerpetualPositionTable.create(defaultPerpetualPosition), PerpetualPositionTable.create({ ...defaultPerpetualPosition, - perpetualId: '3', + perpetualId: '100', openEventId: defaultTendermintEventId2, }), ]); @@ -107,7 +107,7 @@ describe('PerpetualPosition store', () => { expect(perpetualPositions[0]).toEqual(expect.objectContaining(defaultPerpetualPosition)); expect(perpetualPositions[1]).toEqual(expect.objectContaining({ ...defaultPerpetualPosition, - perpetualId: '3', + perpetualId: '100', openEventId: defaultTendermintEventId2, })); }); @@ -115,13 +115,13 @@ describe('PerpetualPosition store', () => { it('Successfully finds PerpetualPosition with perpetualId', async () => { await PerpetualMarketTable.create({ ...defaultPerpetualMarket, - id: '3', + id: '100', }); await Promise.all([ PerpetualPositionTable.create(defaultPerpetualPosition), PerpetualPositionTable.create({ ...defaultPerpetualPosition, - perpetualId: '3', + perpetualId: '100', openEventId: defaultTendermintEventId2, }), ]); @@ -307,28 +307,28 @@ describe('PerpetualPosition store', () => { await Promise.all([ PerpetualMarketTable.create({ ...defaultPerpetualMarket, - id: '3', + id: '100', }), PerpetualMarketTable.create({ ...defaultPerpetualMarket, - id: '4', + id: '101', }), ]); const perpetualPosition2: PerpetualPositionCreateObject = { ...defaultPerpetualPosition, - perpetualId: '3', + perpetualId: '100', openEventId: defaultTendermintEventId2, }; const perpetualPosition3: PerpetualPositionCreateObject = { ...defaultPerpetualPosition, subaccountId: defaultSubaccountId2, - perpetualId: '4', + perpetualId: '101', openEventId: defaultTendermintEventId2, }; const perpetualPosition4: PerpetualPositionCreateObject = { ...defaultPerpetualPosition, subaccountId: defaultSubaccountId2, - perpetualId: '3', + perpetualId: '100', openEventId: defaultTendermintEventId, status: PerpetualPositionStatus.CLOSED, }; diff --git a/indexer/packages/postgres/src/constants.ts b/indexer/packages/postgres/src/constants.ts index fac6844455..16e36f7e2f 100644 --- a/indexer/packages/postgres/src/constants.ts +++ b/indexer/packages/postgres/src/constants.ts @@ -121,3 +121,7 @@ export const DEFAULT_POSTGRES_OPTIONS : Options = config.USE_READ_REPLICA ? { readReplica: true, } : {}; + +export const MAX_PARENT_SUBACCOUNTS: number = 128; + +export const CHILD_SUBACCOUNT_MULTIPLIER: number = 1000; diff --git a/indexer/packages/postgres/src/lib/api-translations.ts b/indexer/packages/postgres/src/lib/api-translations.ts index 27c776b91e..c6e69e248f 100644 --- a/indexer/packages/postgres/src/lib/api-translations.ts +++ b/indexer/packages/postgres/src/lib/api-translations.ts @@ -1,4 +1,4 @@ -import { TIME_IN_FORCE_TO_API_TIME_IN_FORCE } from '../constants'; +import { TIME_IN_FORCE_TO_API_TIME_IN_FORCE, CHILD_SUBACCOUNT_MULTIPLIER, MAX_PARENT_SUBACCOUNTS } from '../constants'; import { APITimeInForce, TimeInForce } from '../types'; /** @@ -20,3 +20,30 @@ export function isOrderTIFPostOnly(timeInForce: TimeInForce): boolean { export function orderTIFToAPITIF(timeInForce: TimeInForce): APITimeInForce { return TIME_IN_FORCE_TO_API_TIME_IN_FORCE[timeInForce]; } + +/** + * Gets a list of all possible child subaccount numbers for a parent subaccount number + * Child subaccounts = [128*0+parentSubaccount, 128*1+parentSubaccount ... 128*999+parentSubaccount] + * @param parentSubaccount + * @returns + */ +export function getChildSubaccountNums(parentSubaccountNum: number): number[] { + if (parentSubaccountNum >= MAX_PARENT_SUBACCOUNTS) { + throw new Error(`Parent subaccount number must be less than ${MAX_PARENT_SUBACCOUNTS}`); + } + return Array.from({ length: CHILD_SUBACCOUNT_MULTIPLIER }, + (_, i) => MAX_PARENT_SUBACCOUNTS * i + parentSubaccountNum); +} + +/** + * Gets the parent subaccount number from a child subaccount number + * Parent subaccount = childSubaccount % 128 + * @param childSubaccountNum + * @returns + */ +export function getParentSubaccountNum(childSubaccountNum: number): number { + if (childSubaccountNum > MAX_PARENT_SUBACCOUNTS * CHILD_SUBACCOUNT_MULTIPLIER) { + throw new Error(`Child subaccount number must be less than ${MAX_PARENT_SUBACCOUNTS * CHILD_SUBACCOUNT_MULTIPLIER}`); + } + return childSubaccountNum % MAX_PARENT_SUBACCOUNTS; +} diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/bundle.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/bundle.ts index 5d00767e2b..499e146f14 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/bundle.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/bundle.ts @@ -49,114 +49,117 @@ import * as _52 from "./indexer/events/events"; import * as _53 from "./indexer/indexer_manager/event"; import * as _54 from "./indexer/off_chain_updates/off_chain_updates"; import * as _55 from "./indexer/protocol/v1/clob"; -import * as _56 from "./indexer/protocol/v1/subaccount"; -import * as _57 from "./indexer/redis/redis_order"; -import * as _58 from "./indexer/shared/removal_reason"; -import * as _59 from "./indexer/socks/messages"; -import * as _60 from "./perpetuals/genesis"; -import * as _61 from "./perpetuals/params"; -import * as _62 from "./perpetuals/perpetual"; -import * as _63 from "./perpetuals/query"; -import * as _64 from "./perpetuals/tx"; -import * as _65 from "./prices/genesis"; -import * as _66 from "./prices/market_param"; -import * as _67 from "./prices/market_price"; -import * as _68 from "./prices/query"; -import * as _69 from "./prices/tx"; -import * as _70 from "./ratelimit/capacity"; -import * as _71 from "./ratelimit/genesis"; -import * as _72 from "./ratelimit/limit_params"; -import * as _73 from "./ratelimit/query"; -import * as _74 from "./ratelimit/tx"; -import * as _75 from "./rewards/genesis"; -import * as _76 from "./rewards/params"; -import * as _77 from "./rewards/query"; -import * as _78 from "./rewards/reward_share"; -import * as _79 from "./rewards/tx"; -import * as _80 from "./sending/genesis"; -import * as _81 from "./sending/query"; -import * as _82 from "./sending/transfer"; -import * as _83 from "./sending/tx"; -import * as _84 from "./stats/genesis"; -import * as _85 from "./stats/params"; -import * as _86 from "./stats/query"; -import * as _87 from "./stats/stats"; -import * as _88 from "./stats/tx"; -import * as _89 from "./subaccounts/asset_position"; -import * as _90 from "./subaccounts/genesis"; -import * as _91 from "./subaccounts/perpetual_position"; -import * as _92 from "./subaccounts/query"; -import * as _93 from "./subaccounts/subaccount"; -import * as _94 from "./vault/genesis"; -import * as _95 from "./vault/query"; -import * as _96 from "./vault/tx"; -import * as _97 from "./vest/genesis"; -import * as _98 from "./vest/query"; -import * as _99 from "./vest/tx"; -import * as _100 from "./vest/vest_entry"; -import * as _108 from "./assets/query.lcd"; -import * as _109 from "./blocktime/query.lcd"; -import * as _110 from "./bridge/query.lcd"; -import * as _111 from "./clob/query.lcd"; -import * as _112 from "./delaymsg/query.lcd"; -import * as _113 from "./epochs/query.lcd"; -import * as _114 from "./feetiers/query.lcd"; -import * as _115 from "./perpetuals/query.lcd"; -import * as _116 from "./prices/query.lcd"; -import * as _117 from "./ratelimit/query.lcd"; -import * as _118 from "./rewards/query.lcd"; -import * as _119 from "./stats/query.lcd"; -import * as _120 from "./subaccounts/query.lcd"; -import * as _121 from "./vest/query.lcd"; -import * as _122 from "./assets/query.rpc.Query"; -import * as _123 from "./blocktime/query.rpc.Query"; -import * as _124 from "./bridge/query.rpc.Query"; -import * as _125 from "./clob/query.rpc.Query"; -import * as _126 from "./delaymsg/query.rpc.Query"; -import * as _127 from "./epochs/query.rpc.Query"; -import * as _128 from "./feetiers/query.rpc.Query"; -import * as _129 from "./govplus/query.rpc.Query"; -import * as _130 from "./perpetuals/query.rpc.Query"; -import * as _131 from "./prices/query.rpc.Query"; -import * as _132 from "./ratelimit/query.rpc.Query"; -import * as _133 from "./rewards/query.rpc.Query"; -import * as _134 from "./sending/query.rpc.Query"; -import * as _135 from "./stats/query.rpc.Query"; -import * as _136 from "./subaccounts/query.rpc.Query"; -import * as _137 from "./vault/query.rpc.Query"; -import * as _138 from "./vest/query.rpc.Query"; -import * as _139 from "./blocktime/tx.rpc.msg"; -import * as _140 from "./bridge/tx.rpc.msg"; -import * as _141 from "./clob/tx.rpc.msg"; -import * as _142 from "./delaymsg/tx.rpc.msg"; -import * as _143 from "./feetiers/tx.rpc.msg"; -import * as _144 from "./govplus/tx.rpc.msg"; -import * as _145 from "./perpetuals/tx.rpc.msg"; -import * as _146 from "./prices/tx.rpc.msg"; -import * as _147 from "./ratelimit/tx.rpc.msg"; -import * as _148 from "./rewards/tx.rpc.msg"; -import * as _149 from "./sending/tx.rpc.msg"; -import * as _150 from "./stats/tx.rpc.msg"; -import * as _151 from "./vest/tx.rpc.msg"; -import * as _152 from "./lcd"; -import * as _153 from "./rpc.query"; -import * as _154 from "./rpc.tx"; +import * as _56 from "./indexer/protocol/v1/perpetual"; +import * as _57 from "./indexer/protocol/v1/subaccount"; +import * as _58 from "./indexer/redis/redis_order"; +import * as _59 from "./indexer/shared/removal_reason"; +import * as _60 from "./indexer/socks/messages"; +import * as _61 from "./perpetuals/genesis"; +import * as _62 from "./perpetuals/params"; +import * as _63 from "./perpetuals/perpetual"; +import * as _64 from "./perpetuals/query"; +import * as _65 from "./perpetuals/tx"; +import * as _66 from "./prices/genesis"; +import * as _67 from "./prices/market_param"; +import * as _68 from "./prices/market_price"; +import * as _69 from "./prices/query"; +import * as _70 from "./prices/tx"; +import * as _71 from "./ratelimit/capacity"; +import * as _72 from "./ratelimit/genesis"; +import * as _73 from "./ratelimit/limit_params"; +import * as _74 from "./ratelimit/query"; +import * as _75 from "./ratelimit/tx"; +import * as _76 from "./rewards/genesis"; +import * as _77 from "./rewards/params"; +import * as _78 from "./rewards/query"; +import * as _79 from "./rewards/reward_share"; +import * as _80 from "./rewards/tx"; +import * as _81 from "./sending/genesis"; +import * as _82 from "./sending/query"; +import * as _83 from "./sending/transfer"; +import * as _84 from "./sending/tx"; +import * as _85 from "./stats/genesis"; +import * as _86 from "./stats/params"; +import * as _87 from "./stats/query"; +import * as _88 from "./stats/stats"; +import * as _89 from "./stats/tx"; +import * as _90 from "./subaccounts/asset_position"; +import * as _91 from "./subaccounts/genesis"; +import * as _92 from "./subaccounts/perpetual_position"; +import * as _93 from "./subaccounts/query"; +import * as _94 from "./subaccounts/subaccount"; +import * as _95 from "./vault/genesis"; +import * as _96 from "./vault/query"; +import * as _97 from "./vault/tx"; +import * as _98 from "./vault/vault"; +import * as _99 from "./vest/genesis"; +import * as _100 from "./vest/query"; +import * as _101 from "./vest/tx"; +import * as _102 from "./vest/vest_entry"; +import * as _110 from "./assets/query.lcd"; +import * as _111 from "./blocktime/query.lcd"; +import * as _112 from "./bridge/query.lcd"; +import * as _113 from "./clob/query.lcd"; +import * as _114 from "./delaymsg/query.lcd"; +import * as _115 from "./epochs/query.lcd"; +import * as _116 from "./feetiers/query.lcd"; +import * as _117 from "./perpetuals/query.lcd"; +import * as _118 from "./prices/query.lcd"; +import * as _119 from "./ratelimit/query.lcd"; +import * as _120 from "./rewards/query.lcd"; +import * as _121 from "./stats/query.lcd"; +import * as _122 from "./subaccounts/query.lcd"; +import * as _123 from "./vest/query.lcd"; +import * as _124 from "./assets/query.rpc.Query"; +import * as _125 from "./blocktime/query.rpc.Query"; +import * as _126 from "./bridge/query.rpc.Query"; +import * as _127 from "./clob/query.rpc.Query"; +import * as _128 from "./delaymsg/query.rpc.Query"; +import * as _129 from "./epochs/query.rpc.Query"; +import * as _130 from "./feetiers/query.rpc.Query"; +import * as _131 from "./govplus/query.rpc.Query"; +import * as _132 from "./perpetuals/query.rpc.Query"; +import * as _133 from "./prices/query.rpc.Query"; +import * as _134 from "./ratelimit/query.rpc.Query"; +import * as _135 from "./rewards/query.rpc.Query"; +import * as _136 from "./sending/query.rpc.Query"; +import * as _137 from "./stats/query.rpc.Query"; +import * as _138 from "./subaccounts/query.rpc.Query"; +import * as _139 from "./vault/query.rpc.Query"; +import * as _140 from "./vest/query.rpc.Query"; +import * as _141 from "./blocktime/tx.rpc.msg"; +import * as _142 from "./bridge/tx.rpc.msg"; +import * as _143 from "./clob/tx.rpc.msg"; +import * as _144 from "./delaymsg/tx.rpc.msg"; +import * as _145 from "./feetiers/tx.rpc.msg"; +import * as _146 from "./govplus/tx.rpc.msg"; +import * as _147 from "./perpetuals/tx.rpc.msg"; +import * as _148 from "./prices/tx.rpc.msg"; +import * as _149 from "./ratelimit/tx.rpc.msg"; +import * as _150 from "./rewards/tx.rpc.msg"; +import * as _151 from "./sending/tx.rpc.msg"; +import * as _152 from "./stats/tx.rpc.msg"; +import * as _153 from "./vault/tx.rpc.msg"; +import * as _154 from "./vest/tx.rpc.msg"; +import * as _155 from "./lcd"; +import * as _156 from "./rpc.query"; +import * as _157 from "./rpc.tx"; export namespace dydxprotocol { export const assets = { ..._5, ..._6, ..._7, ..._8, - ..._108, - ..._122 + ..._110, + ..._124 }; export const blocktime = { ..._9, ..._10, ..._11, ..._12, ..._13, - ..._109, - ..._123, - ..._139 + ..._111, + ..._125, + ..._141 }; export const bridge = { ..._14, ..._15, @@ -164,9 +167,9 @@ export namespace dydxprotocol { ..._17, ..._18, ..._19, - ..._110, - ..._124, - ..._140 + ..._112, + ..._126, + ..._142 }; export const clob = { ..._20, ..._21, @@ -182,9 +185,9 @@ export namespace dydxprotocol { ..._31, ..._32, ..._33, - ..._111, - ..._125, - ..._141 + ..._113, + ..._127, + ..._143 }; export namespace daemons { export const bridge = { ..._34 @@ -199,29 +202,29 @@ export namespace dydxprotocol { ..._39, ..._40, ..._41, - ..._112, - ..._126, - ..._142 + ..._114, + ..._128, + ..._144 }; export const epochs = { ..._42, ..._43, ..._44, - ..._113, - ..._127 + ..._115, + ..._129 }; export const feetiers = { ..._45, ..._46, ..._47, ..._48, - ..._114, - ..._128, - ..._143 + ..._116, + ..._130, + ..._145 }; export const govplus = { ..._49, ..._50, ..._51, - ..._129, - ..._144 + ..._131, + ..._146 }; export namespace indexer { export const events = { ..._52 @@ -232,91 +235,94 @@ export namespace dydxprotocol { }; export namespace protocol { export const v1 = { ..._55, - ..._56 + ..._56, + ..._57 }; } - export const redis = { ..._57 + export const redis = { ..._58 }; - export const shared = { ..._58 + export const shared = { ..._59 }; - export const socks = { ..._59 + export const socks = { ..._60 }; } - export const perpetuals = { ..._60, - ..._61, + export const perpetuals = { ..._61, ..._62, ..._63, ..._64, - ..._115, - ..._130, - ..._145 + ..._65, + ..._117, + ..._132, + ..._147 }; - export const prices = { ..._65, - ..._66, + export const prices = { ..._66, ..._67, ..._68, ..._69, - ..._116, - ..._131, - ..._146 + ..._70, + ..._118, + ..._133, + ..._148 }; - export const ratelimit = { ..._70, - ..._71, + export const ratelimit = { ..._71, ..._72, ..._73, ..._74, - ..._117, - ..._132, - ..._147 + ..._75, + ..._119, + ..._134, + ..._149 }; - export const rewards = { ..._75, - ..._76, + export const rewards = { ..._76, ..._77, ..._78, ..._79, - ..._118, - ..._133, - ..._148 + ..._80, + ..._120, + ..._135, + ..._150 }; - export const sending = { ..._80, - ..._81, + export const sending = { ..._81, ..._82, ..._83, - ..._134, - ..._149 + ..._84, + ..._136, + ..._151 }; - export const stats = { ..._84, - ..._85, + export const stats = { ..._85, ..._86, ..._87, ..._88, - ..._119, - ..._135, - ..._150 + ..._89, + ..._121, + ..._137, + ..._152 }; - export const subaccounts = { ..._89, - ..._90, + export const subaccounts = { ..._90, ..._91, ..._92, ..._93, - ..._120, - ..._136 + ..._94, + ..._122, + ..._138 }; - export const vault = { ..._94, - ..._95, + export const vault = { ..._95, ..._96, - ..._137 - }; - export const vest = { ..._97, + ..._97, ..._98, - ..._99, - ..._100, - ..._121, - ..._138, - ..._151 + ..._139, + ..._153 }; - export const ClientFactory = { ..._152, - ..._153, + export const vest = { ..._99, + ..._100, + ..._101, + ..._102, + ..._123, + ..._140, ..._154 }; + export const ClientFactory = { ..._155, + ..._156, + ..._157 + }; } \ No newline at end of file diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/block_rate_limit_config.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/block_rate_limit_config.ts index 4db5bf31a3..7da63ae44f 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/block_rate_limit_config.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/clob/block_rate_limit_config.ts @@ -10,7 +10,11 @@ export interface BlockRateLimitConfiguration { * configurations. * * Specifying 0 values disables this rate limit. + * Deprecated in favor of `max_short_term_orders_and_cancels_per_n_blocks` + * for v5.x onwards. */ + + /** @deprecated */ maxShortTermOrdersPerNBlocks: MaxPerNBlocksRateLimit[]; /** * How many stateful order attempts (successful and failed) are allowed for @@ -22,7 +26,10 @@ export interface BlockRateLimitConfiguration { */ maxStatefulOrdersPerNBlocks: MaxPerNBlocksRateLimit[]; + /** @deprecated */ + maxShortTermOrderCancellationsPerNBlocks: MaxPerNBlocksRateLimit[]; + maxShortTermOrdersAndCancelsPerNBlocks: MaxPerNBlocksRateLimit[]; } /** Defines the block rate limits for CLOB specific operations. */ @@ -34,7 +41,11 @@ export interface BlockRateLimitConfigurationSDKType { * configurations. * * Specifying 0 values disables this rate limit. + * Deprecated in favor of `max_short_term_orders_and_cancels_per_n_blocks` + * for v5.x onwards. */ + + /** @deprecated */ max_short_term_orders_per_n_blocks: MaxPerNBlocksRateLimitSDKType[]; /** * How many stateful order attempts (successful and failed) are allowed for @@ -46,7 +57,10 @@ export interface BlockRateLimitConfigurationSDKType { */ max_stateful_orders_per_n_blocks: MaxPerNBlocksRateLimitSDKType[]; + /** @deprecated */ + max_short_term_order_cancellations_per_n_blocks: MaxPerNBlocksRateLimitSDKType[]; + max_short_term_orders_and_cancels_per_n_blocks: MaxPerNBlocksRateLimitSDKType[]; } /** Defines a rate limit over a specific number of blocks. */ @@ -83,7 +97,8 @@ function createBaseBlockRateLimitConfiguration(): BlockRateLimitConfiguration { return { maxShortTermOrdersPerNBlocks: [], maxStatefulOrdersPerNBlocks: [], - maxShortTermOrderCancellationsPerNBlocks: [] + maxShortTermOrderCancellationsPerNBlocks: [], + maxShortTermOrdersAndCancelsPerNBlocks: [] }; } @@ -101,6 +116,10 @@ export const BlockRateLimitConfiguration = { MaxPerNBlocksRateLimit.encode(v!, writer.uint32(26).fork()).ldelim(); } + for (const v of message.maxShortTermOrdersAndCancelsPerNBlocks) { + MaxPerNBlocksRateLimit.encode(v!, writer.uint32(34).fork()).ldelim(); + } + return writer; }, @@ -125,6 +144,10 @@ export const BlockRateLimitConfiguration = { message.maxShortTermOrderCancellationsPerNBlocks.push(MaxPerNBlocksRateLimit.decode(reader, reader.uint32())); break; + case 4: + message.maxShortTermOrdersAndCancelsPerNBlocks.push(MaxPerNBlocksRateLimit.decode(reader, reader.uint32())); + break; + default: reader.skipType(tag & 7); break; @@ -139,6 +162,7 @@ export const BlockRateLimitConfiguration = { message.maxShortTermOrdersPerNBlocks = object.maxShortTermOrdersPerNBlocks?.map(e => MaxPerNBlocksRateLimit.fromPartial(e)) || []; message.maxStatefulOrdersPerNBlocks = object.maxStatefulOrdersPerNBlocks?.map(e => MaxPerNBlocksRateLimit.fromPartial(e)) || []; message.maxShortTermOrderCancellationsPerNBlocks = object.maxShortTermOrderCancellationsPerNBlocks?.map(e => MaxPerNBlocksRateLimit.fromPartial(e)) || []; + message.maxShortTermOrdersAndCancelsPerNBlocks = object.maxShortTermOrdersAndCancelsPerNBlocks?.map(e => MaxPerNBlocksRateLimit.fromPartial(e)) || []; return message; } diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/indexer/events/events.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/indexer/events/events.ts index 2c6482734d..1f8d38d1fd 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/indexer/events/events.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/indexer/events/events.ts @@ -1,6 +1,7 @@ import { IndexerSubaccountId, IndexerSubaccountIdSDKType, IndexerPerpetualPosition, IndexerPerpetualPositionSDKType, IndexerAssetPosition, IndexerAssetPositionSDKType } from "../protocol/v1/subaccount"; import { IndexerOrder, IndexerOrderSDKType, IndexerOrderId, IndexerOrderIdSDKType, ClobPairStatus, ClobPairStatusSDKType } from "../protocol/v1/clob"; import { OrderRemovalReason, OrderRemovalReasonSDKType } from "../shared/removal_reason"; +import { PerpetualMarketType, PerpetualMarketTypeSDKType } from "../protocol/v1/perpetual"; import * as _m0 from "protobufjs/minimal"; import { DeepPartial, Long } from "../../../helpers"; /** Type is the type for funding values. */ @@ -845,6 +846,9 @@ export interface PerpetualMarketCreateEventV1 { */ liquidityTier: number; + /** Market type of the perpetual. */ + + marketType: PerpetualMarketType; } /** * PerpetualMarketCreateEventV1 message contains all the information about a @@ -915,6 +919,9 @@ export interface PerpetualMarketCreateEventV1SDKType { */ liquidity_tier: number; + /** Market type of the perpetual. */ + + market_type: PerpetualMarketTypeSDKType; } /** * LiquidityTierUpsertEventV1 message contains all the information to @@ -2528,7 +2535,8 @@ function createBasePerpetualMarketCreateEventV1(): PerpetualMarketCreateEventV1 atomicResolution: 0, subticksPerTick: 0, stepBaseQuantums: Long.UZERO, - liquidityTier: 0 + liquidityTier: 0, + marketType: 0 }; } @@ -2574,6 +2582,10 @@ export const PerpetualMarketCreateEventV1 = { writer.uint32(80).uint32(message.liquidityTier); } + if (message.marketType !== 0) { + writer.uint32(88).int32(message.marketType); + } + return writer; }, @@ -2626,6 +2638,10 @@ export const PerpetualMarketCreateEventV1 = { message.liquidityTier = reader.uint32(); break; + case 11: + message.marketType = (reader.int32() as any); + break; + default: reader.skipType(tag & 7); break; @@ -2647,6 +2663,7 @@ export const PerpetualMarketCreateEventV1 = { message.subticksPerTick = object.subticksPerTick ?? 0; message.stepBaseQuantums = object.stepBaseQuantums !== undefined && object.stepBaseQuantums !== null ? Long.fromValue(object.stepBaseQuantums) : Long.UZERO; message.liquidityTier = object.liquidityTier ?? 0; + message.marketType = object.marketType ?? 0; return message; } diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/indexer/protocol/v1/perpetual.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/indexer/protocol/v1/perpetual.ts new file mode 100644 index 0000000000..829dced6d8 --- /dev/null +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/indexer/protocol/v1/perpetual.ts @@ -0,0 +1,67 @@ +/** + * Market type of perpetual. + * Defined in perpetual. + */ +export enum PerpetualMarketType { + /** PERPETUAL_MARKET_TYPE_UNSPECIFIED - Unspecified market type. */ + PERPETUAL_MARKET_TYPE_UNSPECIFIED = 0, + + /** PERPETUAL_MARKET_TYPE_CROSS - Market type for cross margin perpetual markets. */ + PERPETUAL_MARKET_TYPE_CROSS = 1, + + /** PERPETUAL_MARKET_TYPE_ISOLATED - Market type for isolated margin perpetual markets. */ + PERPETUAL_MARKET_TYPE_ISOLATED = 2, + UNRECOGNIZED = -1, +} +/** + * Market type of perpetual. + * Defined in perpetual. + */ + +export enum PerpetualMarketTypeSDKType { + /** PERPETUAL_MARKET_TYPE_UNSPECIFIED - Unspecified market type. */ + PERPETUAL_MARKET_TYPE_UNSPECIFIED = 0, + + /** PERPETUAL_MARKET_TYPE_CROSS - Market type for cross margin perpetual markets. */ + PERPETUAL_MARKET_TYPE_CROSS = 1, + + /** PERPETUAL_MARKET_TYPE_ISOLATED - Market type for isolated margin perpetual markets. */ + PERPETUAL_MARKET_TYPE_ISOLATED = 2, + UNRECOGNIZED = -1, +} +export function perpetualMarketTypeFromJSON(object: any): PerpetualMarketType { + switch (object) { + case 0: + case "PERPETUAL_MARKET_TYPE_UNSPECIFIED": + return PerpetualMarketType.PERPETUAL_MARKET_TYPE_UNSPECIFIED; + + case 1: + case "PERPETUAL_MARKET_TYPE_CROSS": + return PerpetualMarketType.PERPETUAL_MARKET_TYPE_CROSS; + + case 2: + case "PERPETUAL_MARKET_TYPE_ISOLATED": + return PerpetualMarketType.PERPETUAL_MARKET_TYPE_ISOLATED; + + case -1: + case "UNRECOGNIZED": + default: + return PerpetualMarketType.UNRECOGNIZED; + } +} +export function perpetualMarketTypeToJSON(object: PerpetualMarketType): string { + switch (object) { + case PerpetualMarketType.PERPETUAL_MARKET_TYPE_UNSPECIFIED: + return "PERPETUAL_MARKET_TYPE_UNSPECIFIED"; + + case PerpetualMarketType.PERPETUAL_MARKET_TYPE_CROSS: + return "PERPETUAL_MARKET_TYPE_CROSS"; + + case PerpetualMarketType.PERPETUAL_MARKET_TYPE_ISOLATED: + return "PERPETUAL_MARKET_TYPE_ISOLATED"; + + case PerpetualMarketType.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} \ No newline at end of file diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/rpc.tx.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/rpc.tx.ts index 1fd6591912..2c23915bc5 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/rpc.tx.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/rpc.tx.ts @@ -17,6 +17,7 @@ export const createRPCMsgClient = async ({ rewards: new (await import("./rewards/tx.rpc.msg")).MsgClientImpl(rpc), sending: new (await import("./sending/tx.rpc.msg")).MsgClientImpl(rpc), stats: new (await import("./stats/tx.rpc.msg")).MsgClientImpl(rpc), + vault: new (await import("./vault/tx.rpc.msg")).MsgClientImpl(rpc), vest: new (await import("./vest/tx.rpc.msg")).MsgClientImpl(rpc) } }); \ No newline at end of file diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/tx.rpc.msg.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/tx.rpc.msg.ts new file mode 100644 index 0000000000..e4028957d6 --- /dev/null +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/tx.rpc.msg.ts @@ -0,0 +1,24 @@ +import { Rpc } from "../../helpers"; +import * as _m0 from "protobufjs/minimal"; +import { MsgDepositToVault, MsgDepositToVaultResponse } from "./tx"; +/** Msg defines the Msg service. */ + +export interface Msg { + /** DepositToVault deposits funds into a vault. */ + depositToVault(request: MsgDepositToVault): Promise; +} +export class MsgClientImpl implements Msg { + private readonly rpc: Rpc; + + constructor(rpc: Rpc) { + this.rpc = rpc; + this.depositToVault = this.depositToVault.bind(this); + } + + depositToVault(request: MsgDepositToVault): Promise { + const data = MsgDepositToVault.encode(request).finish(); + const promise = this.rpc.request("dydxprotocol.vault.Msg", "DepositToVault", data); + return promise.then(data => MsgDepositToVaultResponse.decode(new _m0.Reader(data))); + } + +} \ No newline at end of file diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/tx.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/tx.ts index 693da49fc4..b3587f335b 100644 --- a/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/tx.ts +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/tx.ts @@ -1 +1,133 @@ -export {} \ No newline at end of file +import { VaultId, VaultIdSDKType } from "./vault"; +import { SubaccountId, SubaccountIdSDKType } from "../subaccounts/subaccount"; +import * as _m0 from "protobufjs/minimal"; +import { DeepPartial } from "../../helpers"; +/** MsgDepositToVault is the Msg/DepositToVault request type. */ + +export interface MsgDepositToVault { + /** The vault to deposit into. */ + vaultId?: VaultId; + /** The subaccount to deposit from. */ + + subaccountId?: SubaccountId; + /** Number of quote quantums to deposit. */ + + quoteQuantums: Uint8Array; +} +/** MsgDepositToVault is the Msg/DepositToVault request type. */ + +export interface MsgDepositToVaultSDKType { + /** The vault to deposit into. */ + vault_id?: VaultIdSDKType; + /** The subaccount to deposit from. */ + + subaccount_id?: SubaccountIdSDKType; + /** Number of quote quantums to deposit. */ + + quote_quantums: Uint8Array; +} +/** MsgDepositToVaultResponse is the Msg/DepositToVault response type. */ + +export interface MsgDepositToVaultResponse {} +/** MsgDepositToVaultResponse is the Msg/DepositToVault response type. */ + +export interface MsgDepositToVaultResponseSDKType {} + +function createBaseMsgDepositToVault(): MsgDepositToVault { + return { + vaultId: undefined, + subaccountId: undefined, + quoteQuantums: new Uint8Array() + }; +} + +export const MsgDepositToVault = { + encode(message: MsgDepositToVault, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.vaultId !== undefined) { + VaultId.encode(message.vaultId, writer.uint32(10).fork()).ldelim(); + } + + if (message.subaccountId !== undefined) { + SubaccountId.encode(message.subaccountId, writer.uint32(18).fork()).ldelim(); + } + + if (message.quoteQuantums.length !== 0) { + writer.uint32(26).bytes(message.quoteQuantums); + } + + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MsgDepositToVault { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgDepositToVault(); + + while (reader.pos < end) { + const tag = reader.uint32(); + + switch (tag >>> 3) { + case 1: + message.vaultId = VaultId.decode(reader, reader.uint32()); + break; + + case 2: + message.subaccountId = SubaccountId.decode(reader, reader.uint32()); + break; + + case 3: + message.quoteQuantums = reader.bytes(); + break; + + default: + reader.skipType(tag & 7); + break; + } + } + + return message; + }, + + fromPartial(object: DeepPartial): MsgDepositToVault { + const message = createBaseMsgDepositToVault(); + message.vaultId = object.vaultId !== undefined && object.vaultId !== null ? VaultId.fromPartial(object.vaultId) : undefined; + message.subaccountId = object.subaccountId !== undefined && object.subaccountId !== null ? SubaccountId.fromPartial(object.subaccountId) : undefined; + message.quoteQuantums = object.quoteQuantums ?? new Uint8Array(); + return message; + } + +}; + +function createBaseMsgDepositToVaultResponse(): MsgDepositToVaultResponse { + return {}; +} + +export const MsgDepositToVaultResponse = { + encode(_: MsgDepositToVaultResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): MsgDepositToVaultResponse { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseMsgDepositToVaultResponse(); + + while (reader.pos < end) { + const tag = reader.uint32(); + + switch (tag >>> 3) { + default: + reader.skipType(tag & 7); + break; + } + } + + return message; + }, + + fromPartial(_: DeepPartial): MsgDepositToVaultResponse { + const message = createBaseMsgDepositToVaultResponse(); + return message; + } + +}; \ No newline at end of file diff --git a/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/vault.ts b/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/vault.ts new file mode 100644 index 0000000000..cc93d62a58 --- /dev/null +++ b/indexer/packages/v4-protos/src/codegen/dydxprotocol/vault/vault.ts @@ -0,0 +1,181 @@ +import * as _m0 from "protobufjs/minimal"; +import { DeepPartial } from "../../helpers"; +/** VaultType represents different types of vaults. */ + +export enum VaultType { + /** VAULT_TYPE_UNSPECIFIED - Default value, invalid and unused. */ + VAULT_TYPE_UNSPECIFIED = 0, + + /** VAULT_TYPE_CLOB - Vault is associated with a CLOB pair. */ + VAULT_TYPE_CLOB = 1, + UNRECOGNIZED = -1, +} +/** VaultType represents different types of vaults. */ + +export enum VaultTypeSDKType { + /** VAULT_TYPE_UNSPECIFIED - Default value, invalid and unused. */ + VAULT_TYPE_UNSPECIFIED = 0, + + /** VAULT_TYPE_CLOB - Vault is associated with a CLOB pair. */ + VAULT_TYPE_CLOB = 1, + UNRECOGNIZED = -1, +} +export function vaultTypeFromJSON(object: any): VaultType { + switch (object) { + case 0: + case "VAULT_TYPE_UNSPECIFIED": + return VaultType.VAULT_TYPE_UNSPECIFIED; + + case 1: + case "VAULT_TYPE_CLOB": + return VaultType.VAULT_TYPE_CLOB; + + case -1: + case "UNRECOGNIZED": + default: + return VaultType.UNRECOGNIZED; + } +} +export function vaultTypeToJSON(object: VaultType): string { + switch (object) { + case VaultType.VAULT_TYPE_UNSPECIFIED: + return "VAULT_TYPE_UNSPECIFIED"; + + case VaultType.VAULT_TYPE_CLOB: + return "VAULT_TYPE_CLOB"; + + case VaultType.UNRECOGNIZED: + default: + return "UNRECOGNIZED"; + } +} +/** VaultId uniquely identifies a vault by its type and number. */ + +export interface VaultId { + /** Type of the vault. */ + type: VaultType; + /** Unique ID of the vault within above type. */ + + number: number; +} +/** VaultId uniquely identifies a vault by its type and number. */ + +export interface VaultIdSDKType { + /** Type of the vault. */ + type: VaultTypeSDKType; + /** Unique ID of the vault within above type. */ + + number: number; +} +/** NumShares represents the number of shares in a vault. */ + +export interface NumShares { + /** Number of shares. */ + numShares: Uint8Array; +} +/** NumShares represents the number of shares in a vault. */ + +export interface NumSharesSDKType { + /** Number of shares. */ + num_shares: Uint8Array; +} + +function createBaseVaultId(): VaultId { + return { + type: 0, + number: 0 + }; +} + +export const VaultId = { + encode(message: VaultId, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.type !== 0) { + writer.uint32(8).int32(message.type); + } + + if (message.number !== 0) { + writer.uint32(16).uint32(message.number); + } + + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): VaultId { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseVaultId(); + + while (reader.pos < end) { + const tag = reader.uint32(); + + switch (tag >>> 3) { + case 1: + message.type = (reader.int32() as any); + break; + + case 2: + message.number = reader.uint32(); + break; + + default: + reader.skipType(tag & 7); + break; + } + } + + return message; + }, + + fromPartial(object: DeepPartial): VaultId { + const message = createBaseVaultId(); + message.type = object.type ?? 0; + message.number = object.number ?? 0; + return message; + } + +}; + +function createBaseNumShares(): NumShares { + return { + numShares: new Uint8Array() + }; +} + +export const NumShares = { + encode(message: NumShares, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer { + if (message.numShares.length !== 0) { + writer.uint32(10).bytes(message.numShares); + } + + return writer; + }, + + decode(input: _m0.Reader | Uint8Array, length?: number): NumShares { + const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input); + let end = length === undefined ? reader.len : reader.pos + length; + const message = createBaseNumShares(); + + while (reader.pos < end) { + const tag = reader.uint32(); + + switch (tag >>> 3) { + case 1: + message.numShares = reader.bytes(); + break; + + default: + reader.skipType(tag & 7); + break; + } + } + + return message; + }, + + fromPartial(object: DeepPartial): NumShares { + const message = createBaseNumShares(); + message.numShares = object.numShares ?? new Uint8Array(); + return message; + } + +}; \ No newline at end of file diff --git a/indexer/packages/v4-protos/src/codegen/gogoproto/bundle.ts b/indexer/packages/v4-protos/src/codegen/gogoproto/bundle.ts index ac7a691883..c3b6b38b3f 100644 --- a/indexer/packages/v4-protos/src/codegen/gogoproto/bundle.ts +++ b/indexer/packages/v4-protos/src/codegen/gogoproto/bundle.ts @@ -1,3 +1,3 @@ -import * as _101 from "./gogo"; -export const gogoproto = { ..._101 +import * as _103 from "./gogo"; +export const gogoproto = { ..._103 }; \ No newline at end of file diff --git a/indexer/packages/v4-protos/src/codegen/google/bundle.ts b/indexer/packages/v4-protos/src/codegen/google/bundle.ts index 3312f4c5c1..8878e6797b 100644 --- a/indexer/packages/v4-protos/src/codegen/google/bundle.ts +++ b/indexer/packages/v4-protos/src/codegen/google/bundle.ts @@ -1,16 +1,16 @@ -import * as _102 from "./api/annotations"; -import * as _103 from "./api/http"; -import * as _104 from "./protobuf/descriptor"; -import * as _105 from "./protobuf/duration"; -import * as _106 from "./protobuf/timestamp"; -import * as _107 from "./protobuf/any"; +import * as _104 from "./api/annotations"; +import * as _105 from "./api/http"; +import * as _106 from "./protobuf/descriptor"; +import * as _107 from "./protobuf/duration"; +import * as _108 from "./protobuf/timestamp"; +import * as _109 from "./protobuf/any"; export namespace google { - export const api = { ..._102, - ..._103 + export const api = { ..._104, + ..._105 }; - export const protobuf = { ..._104, - ..._105, - ..._106, - ..._107 + export const protobuf = { ..._106, + ..._107, + ..._108, + ..._109 }; } \ No newline at end of file diff --git a/indexer/services/bazooka/src/vulcan-helpers.ts b/indexer/services/bazooka/src/vulcan-helpers.ts index eacbaedb37..e76ed7cbb1 100644 --- a/indexer/services/bazooka/src/vulcan-helpers.ts +++ b/indexer/services/bazooka/src/vulcan-helpers.ts @@ -22,6 +22,7 @@ import { } from '@dydxprotocol-indexer/v4-protos'; import { Long } from '@dydxprotocol-indexer/v4-protos/build/codegen/helpers'; import Big from 'big.js'; +import { IHeaders } from 'kafkajs'; import _ from 'lodash'; import config from './config'; @@ -30,6 +31,7 @@ import { ZERO } from './constants'; interface VulcanMessage { key: Buffer, value: OffChainUpdateV1, + headers?: IHeaders, } type IndexerOrderIdMap = { [orderUuid: string]: IndexerOrderId }; @@ -129,6 +131,7 @@ export async function sendStatefulOrderMessages() { return { key: message.key, value: Buffer.from(Uint8Array.from(OffChainUpdateV1.encode(message.value).finish())), + headers: message.headers, }; }); diff --git a/indexer/services/comlink/__tests__/controllers/api/v4/addresses-controller.test.ts b/indexer/services/comlink/__tests__/controllers/api/v4/addresses-controller.test.ts index cd5b9894f4..e28feb7e21 100644 --- a/indexer/services/comlink/__tests__/controllers/api/v4/addresses-controller.test.ts +++ b/indexer/services/comlink/__tests__/controllers/api/v4/addresses-controller.test.ts @@ -113,12 +113,14 @@ describe('addresses-controller#V4', () => { size: '9500', side: PositionSide.LONG, assetId: testConstants.defaultAssetPosition.assetId, + subaccountNumber: testConstants.defaultSubaccount.subaccountNumber, }, [testConstants.defaultAsset2.symbol]: { symbol: testConstants.defaultAsset2.symbol, size: testConstants.defaultAssetPosition2.size, side: PositionSide.SHORT, assetId: testConstants.defaultAssetPosition2.assetId, + subaccountNumber: testConstants.defaultSubaccount.subaccountNumber, }, }, }, @@ -162,6 +164,7 @@ describe('addresses-controller#V4', () => { size: testConstants.defaultAssetPosition.size, side: PositionSide.LONG, assetId: testConstants.defaultAssetPosition.assetId, + subaccountNumber: testConstants.defaultSubaccount.subaccountNumber, }, }, }, @@ -270,12 +273,14 @@ describe('addresses-controller#V4', () => { size: '9500', side: PositionSide.LONG, assetId: testConstants.defaultAssetPosition.assetId, + subaccountNumber: testConstants.defaultSubaccount.subaccountNumber, }, [testConstants.defaultAsset2.symbol]: { symbol: testConstants.defaultAsset2.symbol, size: testConstants.defaultAssetPosition2.size, side: PositionSide.SHORT, assetId: testConstants.defaultAssetPosition2.assetId, + subaccountNumber: testConstants.defaultSubaccount.subaccountNumber, }, }, }, @@ -288,6 +293,24 @@ describe('addresses-controller#V4', () => { openPerpetualPositions: {}, assetPositions: {}, }, + { + address: testConstants.defaultAddress, + subaccountNumber: testConstants.isolatedSubaccount.subaccountNumber, + equity: getFixedRepresentation(0), + freeCollateral: getFixedRepresentation(0), + marginEnabled: true, + openPerpetualPositions: {}, + assetPositions: {}, + }, + { + address: testConstants.defaultAddress, + subaccountNumber: testConstants.isolatedSubaccount2.subaccountNumber, + equity: getFixedRepresentation(0), + freeCollateral: getFixedRepresentation(0), + marginEnabled: true, + openPerpetualPositions: {}, + assetPositions: {}, + }, ], totalTradingRewards: testConstants.defaultWallet.totalTradingRewards, }); diff --git a/indexer/services/comlink/__tests__/controllers/api/v4/asset-positions-controller.test.ts b/indexer/services/comlink/__tests__/controllers/api/v4/asset-positions-controller.test.ts index c76c53b7a8..3e9dd4e60e 100644 --- a/indexer/services/comlink/__tests__/controllers/api/v4/asset-positions-controller.test.ts +++ b/indexer/services/comlink/__tests__/controllers/api/v4/asset-positions-controller.test.ts @@ -46,6 +46,7 @@ describe('asset-positions-controller#V4', () => { side: PositionSide.LONG, size, assetId: testConstants.defaultAssetPosition.assetId, + subaccountNumber: testConstants.defaultSubaccount.subaccountNumber, }; expect(response.body.positions).toEqual( @@ -75,6 +76,7 @@ describe('asset-positions-controller#V4', () => { side: PositionSide.SHORT, size: testConstants.defaultAssetPosition.size, assetId: testConstants.defaultAssetPosition.assetId, + subaccountNumber: testConstants.defaultSubaccount.subaccountNumber, }; expect(response.body.positions).toEqual( @@ -110,6 +112,7 @@ describe('asset-positions-controller#V4', () => { size: testConstants.defaultAssetPosition.size, side: PositionSide.LONG, assetId: testConstants.defaultAssetPosition.assetId, + subaccountNumber: testConstants.defaultSubaccount.subaccountNumber, }], ); }); @@ -154,6 +157,7 @@ describe('asset-positions-controller#V4', () => { size: '9500', side: PositionSide.LONG, assetId: testConstants.defaultAssetPosition.assetId, + subaccountNumber: testConstants.defaultSubaccount.subaccountNumber, }], ); }); diff --git a/indexer/services/comlink/__tests__/controllers/api/v4/fills-controller.test.ts b/indexer/services/comlink/__tests__/controllers/api/v4/fills-controller.test.ts index a1a8857009..80cf5e87cb 100644 --- a/indexer/services/comlink/__tests__/controllers/api/v4/fills-controller.test.ts +++ b/indexer/services/comlink/__tests__/controllers/api/v4/fills-controller.test.ts @@ -10,7 +10,11 @@ import { } from '@dydxprotocol-indexer/postgres'; import { FillResponseObject, MarketType, RequestMethod } from '../../../../src/types'; import request from 'supertest'; -import { getQueryString, sendRequest } from '../../../helpers/helpers'; +import { + getQueryString, + sendRequest, + fillResponseObjectFromFillCreateObject, +} from '../../../helpers/helpers'; describe('fills-controller#V4', () => { beforeAll(async () => { @@ -107,6 +111,7 @@ describe('fills-controller#V4', () => { orderId: ethOrder.id, createdAt: ethFill.createdAt, createdAtHeight: ethFill.createdAtHeight, + subaccountNumber: defaultSubaccountNumber, }; // Only the ETH-USD order should be returned @@ -281,5 +286,178 @@ describe('fills-controller#V4', () => { ], }); }); + + it('Get /fills/parentSubaccountNumber gets fills', async () => { + await OrderTable.create(testConstants.defaultOrder); + await FillTable.create(testConstants.defaultFill); + await OrderTable.create(testConstants.isolatedMarketOrder); + await FillTable.create(testConstants.isolatedMarketFill); + await FillTable.create(testConstants.isolatedMarketFill2); + + const parentSubaccountNumber: number = 0; + const response: request.Response = await sendRequest({ + type: RequestMethod.GET, + path: `/v4/fills/parentSubaccountNumber?address=${testConstants.defaultAddress}` + + `&parentSubaccountNumber=${parentSubaccountNumber}`, + }); + + // Use fillResponseObjectFromFillCreateObject to create expectedFills + const expectedFills: Partial[] = [ + fillResponseObjectFromFillCreateObject(testConstants.defaultFill, defaultSubaccountNumber), + fillResponseObjectFromFillCreateObject(testConstants.isolatedMarketFill, + testConstants.isolatedSubaccount.subaccountNumber), + fillResponseObjectFromFillCreateObject(testConstants.isolatedMarketFill2, + testConstants.isolatedSubaccount2.subaccountNumber), + ]; + + expect(response.body.fills).toHaveLength(3); + expect(response.body.fills).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + ...expectedFills[0], + }), + expect.objectContaining({ + ...expectedFills[1], + }), + expect.objectContaining({ + ...expectedFills[2], + }), + ]), + ); + }); + + it('Get /fills/parentSubaccountNumber gets fills for isolated market', async () => { + await OrderTable.create(testConstants.defaultOrder); + await FillTable.create(testConstants.defaultFill); + await OrderTable.create(testConstants.isolatedMarketOrder); + await FillTable.create(testConstants.isolatedMarketFill); + await FillTable.create(testConstants.isolatedMarketFill2); + + const parentSubaccountNumber: number = 0; + const response: request.Response = await sendRequest({ + type: RequestMethod.GET, + path: `/v4/fills/parentSubaccountNumber?address=${testConstants.defaultAddress}` + + `&parentSubaccountNumber=${parentSubaccountNumber}` + + `&market=${testConstants.isolatedPerpetualMarket.ticker}&marketType=${MarketType.PERPETUAL}`, + }); + + // Use fillResponseObjectFromFillCreateObject to create expectedFills + const expectedFills: Partial[] = [ + fillResponseObjectFromFillCreateObject(testConstants.isolatedMarketFill, + testConstants.isolatedSubaccount.subaccountNumber), + fillResponseObjectFromFillCreateObject(testConstants.isolatedMarketFill2, + testConstants.isolatedSubaccount2.subaccountNumber), + ]; + + expect(response.body.fills).toHaveLength(2); + expect(response.body.fills).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + ...expectedFills[0], + }), + expect.objectContaining({ + ...expectedFills[1], + }), + ]), + ); + }); + + it('Get /fills/parentSubaccountNumber with market with no fills', async () => { + await OrderTable.create(testConstants.defaultOrder); + await FillTable.create(testConstants.defaultFill); + await OrderTable.create(testConstants.isolatedMarketOrder); + await FillTable.create(testConstants.isolatedMarketFill); + + const parentSubaccountNumber: number = 0; + const response: request.Response = await sendRequest({ + type: RequestMethod.GET, + path: `/v4/fills/parentSubaccountNumber?address=${testConstants.defaultAddress}` + + `&parentSubaccountNumber=${parentSubaccountNumber}` + + `&market=${testConstants.isolatedPerpetualMarket2.ticker}&marketType=${MarketType.PERPETUAL}`, + }); + + expect(response.body.fills).toEqual([]); + }); + + it.each([ + [ + 'market passed in without marketType', + { + address: defaultAddress, + subaccountNumber: defaultSubaccountNumber, + market: defaultMarket, + }, + 'marketType', + 'marketType must be provided if market is provided', + ], + [ + 'marketType passed in without market', + { + address: defaultAddress, + subaccountNumber: defaultSubaccountNumber, + marketType: MarketType.PERPETUAL, + }, + 'market', + 'market must be provided if marketType is provided', + ], + [ + 'invalid marketType', + { + address: defaultAddress, + subaccountNumber: defaultSubaccountNumber, + marketType: 'INVALID', + market: defaultMarket, + }, + 'marketType', + 'marketType must be a valid market type (PERPETUAL/SPOT)', + ], + ])('Returns 400 when validation fails for parentSubaccount endpoint: %s', async ( + _reason: string, + queryParams: { + address?: string, + parentSubaccountNumber?: number, + market?: string, + marketType?: string, + createdBeforeOrAt?: string, + createdBeforeOrAtHeight?: number, + }, + fieldWithError: string, + expectedErrorMsg: string, + ) => { + const response: request.Response = await sendRequest({ + type: RequestMethod.GET, + path: `/v4/fills/parentSubaccountNumber?${getQueryString(queryParams)}`, + expectedStatus: 400, + }); + + expect(response.body).toEqual(expect.objectContaining({ + errors: expect.arrayContaining([ + expect.objectContaining({ + param: fieldWithError, + msg: expectedErrorMsg, + }), + ]), + })); + }); + + it('Returns 404 with unknown market and type on parentSubaccount endpt', async () => { + const parentSubaccountNumber: number = 0; + const response: request.Response = await sendRequest({ + type: RequestMethod.GET, + path: `/v4/fills/parentSubaccountNumber?address=${testConstants.defaultAddress}` + + `&parentSubaccountNumber=${parentSubaccountNumber}` + + `&market=${invalidMarket}&marketType=${MarketType.PERPETUAL}`, + expectedStatus: 404, + }); + + expect(response.body).toEqual({ + errors: [ + { + msg: `${invalidMarket} not found in markets of type ${MarketType.PERPETUAL}`, + }, + ], + }); + }); + }); }); diff --git a/indexer/services/comlink/__tests__/controllers/api/v4/sparklines-controller.test.ts b/indexer/services/comlink/__tests__/controllers/api/v4/sparklines-controller.test.ts index f06741a8ac..07863f32fc 100644 --- a/indexer/services/comlink/__tests__/controllers/api/v4/sparklines-controller.test.ts +++ b/indexer/services/comlink/__tests__/controllers/api/v4/sparklines-controller.test.ts @@ -6,7 +6,6 @@ import { PerpetualMarketFromDatabase, perpetualMarketRefresher, testConstants, - testMocks, } from '@dydxprotocol-indexer/postgres'; import _ from 'lodash'; import { DateTime } from 'luxon'; @@ -16,6 +15,44 @@ import { SPARKLINE_TIME_PERIOD_TO_LIMIT_MAP, SPARKLINE_TIME_PERIOD_TO_RESOLUTION import { RequestMethod, SparklineTimePeriod } from '../../../../src/types'; import { sendRequest } from '../../../helpers/helpers'; import Big from 'big.js'; +import * as SubaccountTable from '@dydxprotocol-indexer/postgres/build/src/stores/subaccount-table'; +import { + defaultLiquidityTier, + defaultLiquidityTier2, + defaultMarket, + defaultMarket2, + defaultMarket3, + defaultPerpetualMarket, + defaultPerpetualMarket2, + defaultPerpetualMarket3, + defaultSubaccount, + defaultSubaccount2, +} from '@dydxprotocol-indexer/postgres/build/__tests__/helpers/constants'; +import * as MarketTable from '@dydxprotocol-indexer/postgres/build/src/stores/market-table'; +import * as LiquidityTiersTable from '@dydxprotocol-indexer/postgres/build/src/stores/liquidity-tiers-table'; +import * as PerpetualMarketTable from '@dydxprotocol-indexer/postgres/build/src/stores/perpetual-market-table'; + +// helper function to seed data +async function seedData() { + await Promise.all([ + SubaccountTable.create(defaultSubaccount), + SubaccountTable.create(defaultSubaccount2), + ]); + await Promise.all([ + MarketTable.create(defaultMarket), + MarketTable.create(defaultMarket2), + MarketTable.create(defaultMarket3), + ]); + await Promise.all([ + LiquidityTiersTable.create(defaultLiquidityTier), + LiquidityTiersTable.create(defaultLiquidityTier2), + ]); + await Promise.all([ + PerpetualMarketTable.create(defaultPerpetualMarket), + PerpetualMarketTable.create(defaultPerpetualMarket2), + PerpetualMarketTable.create(defaultPerpetualMarket3), + ]); +} describe('sparklines-controller#V4', () => { beforeAll(async () => { @@ -23,7 +60,7 @@ describe('sparklines-controller#V4', () => { }); beforeEach(async () => { - await testMocks.seedData(); + await seedData(); await perpetualMarketRefresher.updatePerpetualMarkets(); }); diff --git a/indexer/services/comlink/__tests__/helpers/helpers.ts b/indexer/services/comlink/__tests__/helpers/helpers.ts index d5fe46f5f6..7d9d6e6cea 100644 --- a/indexer/services/comlink/__tests__/helpers/helpers.ts +++ b/indexer/services/comlink/__tests__/helpers/helpers.ts @@ -15,7 +15,7 @@ import request from 'supertest'; import IndexV4 from '../../src/controllers/api/index-v4'; import Server from '../../src/request-helpers/server'; -import { RequestMethod } from '../../src/types'; +import { RequestMethod, FillResponseObject, MarketType } from '../../src/types'; const app: e.Express = Server(IndexV4); @@ -145,6 +145,26 @@ export async function createMakerTakerOrderAndFill( return { makerFill, takerFill }; } +export function fillResponseObjectFromFillCreateObject( + fill: FillCreateObject, + subaccountNumber: number, +): Partial { + const fillResponse: Partial = { + side: fill.side, + liquidity: fill.liquidity, + marketType: MarketType.PERPETUAL, + price: fill.price, + size: fill.size, + fee: fill.fee, + type: fill.type, + orderId: fill.orderId, + createdAt: fill.createdAt, + createdAtHeight: fill.createdAtHeight, + subaccountNumber, + }; + return fillResponse; +} + function randomInt(range: number = 1000): number { return Math.floor(Math.random() * range); } diff --git a/indexer/services/comlink/__tests__/lib/helpers.test.ts b/indexer/services/comlink/__tests__/lib/helpers.test.ts index 032c2e3ceb..815d1382cc 100644 --- a/indexer/services/comlink/__tests__/lib/helpers.test.ts +++ b/indexer/services/comlink/__tests__/lib/helpers.test.ts @@ -307,7 +307,6 @@ describe('helpers', () => { latestBlock!, ); - expect(Object.keys(lastUpdatedFundingIndexMap)).toHaveLength(3); expect( lastUpdatedFundingIndexMap[testConstants.defaultFundingIndexUpdate.perpetualId] .toString(), @@ -320,7 +319,6 @@ describe('helpers', () => { lastUpdatedFundingIndexMap[testConstants.defaultPerpetualMarket3.id] .toString(), ).toEqual(ZERO.toString()); - expect(Object.keys(latestFundingIndexMap)).toHaveLength(3); expect(latestFundingIndexMap[fundingIndexUpdate3.perpetualId].toString()) .toEqual(fundingIndexUpdate3.fundingIndex); expect(latestFundingIndexMap[testConstants.defaultPerpetualMarket2.id].toString()) @@ -392,6 +390,7 @@ describe('helpers', () => { side: PositionSide.LONG, assetId: '0', size: '1', + subaccountNumber: 0, }, }; const unsettledFunding: Big = Big('300'); @@ -416,6 +415,7 @@ describe('helpers', () => { side: PositionSide.LONG, assetId: '0', size: '1', + subaccountNumber: 0, }, }); expect(assetPositionsMap).toEqual({ @@ -429,6 +429,7 @@ describe('helpers', () => { side: PositionSide.LONG, assetId: '0', size: '1', + subaccountNumber: 0, }, }); expect(adjustedUSDCAssetPositionSize).toEqual(expectedAdjustedPositionSize); @@ -458,6 +459,7 @@ describe('helpers', () => { side: PositionSide.LONG, assetId: '0', size: '1', + subaccountNumber: 0, }, }; @@ -481,6 +483,7 @@ describe('helpers', () => { side: PositionSide.LONG, assetId: '0', size: '1', + subaccountNumber: 0, }, }); expect(assetPositionsMap).toEqual({ @@ -494,6 +497,7 @@ describe('helpers', () => { side: PositionSide.LONG, assetId: '0', size: '1', + subaccountNumber: 0, }, }); expect(adjustedUSDCAssetPositionSize).toEqual(expectedAdjustedPositionSize); @@ -513,6 +517,7 @@ describe('helpers', () => { side: PositionSide.LONG, assetId: '0', size: '1', + subaccountNumber: 0, }, }; @@ -531,6 +536,7 @@ describe('helpers', () => { side: PositionSide.LONG, assetId: '0', size: '1', + subaccountNumber: 0, }, }); expect(assetPositionsMap).toEqual({ @@ -544,6 +550,7 @@ describe('helpers', () => { side: PositionSide.LONG, assetId: '0', size: '1', + subaccountNumber: 0, }, }); expect(adjustedUSDCAssetPositionSize).toEqual(funding); @@ -569,6 +576,7 @@ describe('helpers', () => { side: PositionSide.LONG, assetId: '0', size: '1', + subaccountNumber: 0, }, }; @@ -592,6 +600,7 @@ describe('helpers', () => { side: PositionSide.LONG, assetId: '0', size: '1', + subaccountNumber: 0, }, }); expect(assetPositionsMap).toEqual({ @@ -600,6 +609,7 @@ describe('helpers', () => { side: PositionSide.LONG, assetId: '0', size: '1', + subaccountNumber: 0, }, }); expect(adjustedUSDCAssetPositionSize).toEqual(ZERO.toString()); diff --git a/indexer/services/comlink/public/api-documentation.md b/indexer/services/comlink/public/api-documentation.md index f1c932f218..50a9a43c87 100644 --- a/indexer/services/comlink/public/api-documentation.md +++ b/indexer/services/comlink/public/api-documentation.md @@ -110,13 +110,15 @@ fetch('https://dydx-testnet.imperator.co/v4/addresses/{address}', "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 }, "property2": { "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 } }, "marginEnabled": true @@ -234,13 +236,15 @@ fetch('https://dydx-testnet.imperator.co/v4/addresses/{address}/subaccountNumber "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 }, "property2": { "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 } }, "marginEnabled": true @@ -317,7 +321,8 @@ fetch('https://dydx-testnet.imperator.co/v4/assetPositions?address=string&subacc "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 } ] } @@ -580,7 +585,106 @@ fetch('https://dydx-testnet.imperator.co/v4/fills?address=string&subaccountNumbe "createdAt": "string", "createdAtHeight": "string", "orderId": "string", - "clientMetadata": "string" + "clientMetadata": "string", + "subaccountNumber": 0 + } + ] +} +``` + +### Responses + +|Status|Meaning|Description|Schema| +|---|---|---|---| +|200|[OK](https://tools.ietf.org/html/rfc7231#section-6.3.1)|Ok|[FillResponse](#schemafillresponse)| + + + +## GetFillsForParentSubaccount + + + +> Code samples + +```python +import requests +headers = { + 'Accept': 'application/json' +} + +r = requests.get('https://dydx-testnet.imperator.co/v4/fills/parentSubaccount', params={ + 'address': 'string', 'parentSubaccountNumber': '0' +}, headers = headers) + +print(r.json()) + +``` + +```javascript + +const headers = { + 'Accept':'application/json' +}; + +fetch('https://dydx-testnet.imperator.co/v4/fills/parentSubaccount?address=string&parentSubaccountNumber=0', +{ + method: 'GET', + + headers: headers +}) +.then(function(res) { + return res.json(); +}).then(function(body) { + console.log(body); +}); + +``` + +`GET /fills/parentSubaccount` + +### Parameters + +|Name|In|Type|Required|Description| +|---|---|---|---|---| +|address|query|string|true|none| +|parentSubaccountNumber|query|number(double)|true|none| +|market|query|string|false|none| +|marketType|query|[MarketType](#schemamarkettype)|false|none| +|limit|query|number(double)|false|none| +|createdBeforeOrAtHeight|query|number(double)|false|none| +|createdBeforeOrAt|query|[IsoString](#schemaisostring)|false|none| + +#### Enumerated Values + +|Parameter|Value| +|---|---| +|marketType|PERPETUAL| +|marketType|SPOT| + +> Example responses + +> 200 Response + +```json +{ + "fills": [ + { + "id": "string", + "side": "BUY", + "liquidity": "TAKER", + "type": "LIMIT", + "market": "string", + "marketType": "PERPETUAL", + "price": "string", + "size": "string", + "fee": "string", + "createdAt": "string", + "createdAtHeight": "string", + "orderId": "string", + "clientMetadata": "string", + "subaccountNumber": 0 } ] } @@ -2036,7 +2140,8 @@ This operation does not require authentication "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 } ``` @@ -2049,6 +2154,7 @@ This operation does not require authentication |side|[PositionSide](#schemapositionside)|true|none|none| |size|string|true|none|none| |assetId|string|true|none|none| +|subaccountNumber|number(double)|true|none|none| ## AssetPositionsMap @@ -2063,13 +2169,15 @@ This operation does not require authentication "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 }, "property2": { "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 } } @@ -2135,13 +2243,15 @@ This operation does not require authentication "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 }, "property2": { "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 } }, "marginEnabled": true @@ -2217,13 +2327,15 @@ This operation does not require authentication "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 }, "property2": { "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 } }, "marginEnabled": true @@ -2255,7 +2367,8 @@ This operation does not require authentication "symbol": "string", "side": "LONG", "size": "string", - "assetId": "string" + "assetId": "string", + "subaccountNumber": 0 } ] } @@ -2598,7 +2711,8 @@ This operation does not require authentication "createdAt": "string", "createdAtHeight": "string", "orderId": "string", - "clientMetadata": "string" + "clientMetadata": "string", + "subaccountNumber": 0 } ``` @@ -2620,6 +2734,7 @@ This operation does not require authentication |createdAtHeight|string|true|none|none| |orderId|string|false|none|none| |clientMetadata|string|false|none|none| +|subaccountNumber|number(double)|true|none|none| ## FillResponse @@ -2644,7 +2759,8 @@ This operation does not require authentication "createdAt": "string", "createdAtHeight": "string", "orderId": "string", - "clientMetadata": "string" + "clientMetadata": "string", + "subaccountNumber": 0 } ] } diff --git a/indexer/services/comlink/public/swagger.json b/indexer/services/comlink/public/swagger.json index a3ae9dbf22..ad36977d51 100644 --- a/indexer/services/comlink/public/swagger.json +++ b/indexer/services/comlink/public/swagger.json @@ -116,13 +116,18 @@ }, "assetId": { "type": "string" + }, + "subaccountNumber": { + "type": "number", + "format": "double" } }, "required": [ "symbol", "side", "size", - "assetId" + "assetId", + "subaccountNumber" ], "type": "object", "additionalProperties": false @@ -409,6 +414,10 @@ }, "clientMetadata": { "type": "string" + }, + "subaccountNumber": { + "type": "number", + "format": "double" } }, "required": [ @@ -422,7 +431,8 @@ "size", "fee", "createdAt", - "createdAtHeight" + "createdAtHeight", + "subaccountNumber" ], "type": "object", "additionalProperties": false @@ -1436,6 +1446,85 @@ ] } }, + "/fills/parentSubaccount": { + "get": { + "operationId": "GetFillsForParentSubaccount", + "responses": { + "200": { + "description": "Ok", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FillResponse" + } + } + } + } + }, + "security": [], + "parameters": [ + { + "in": "query", + "name": "address", + "required": true, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "parentSubaccountNumber", + "required": true, + "schema": { + "format": "double", + "type": "number" + } + }, + { + "in": "query", + "name": "market", + "required": false, + "schema": { + "type": "string" + } + }, + { + "in": "query", + "name": "marketType", + "required": false, + "schema": { + "$ref": "#/components/schemas/MarketType" + } + }, + { + "in": "query", + "name": "limit", + "required": false, + "schema": { + "format": "double", + "type": "number" + } + }, + { + "in": "query", + "name": "createdBeforeOrAtHeight", + "required": false, + "schema": { + "format": "double", + "type": "number" + } + }, + { + "in": "query", + "name": "createdBeforeOrAt", + "required": false, + "schema": { + "$ref": "#/components/schemas/IsoString" + } + } + ] + } + }, "/height": { "get": { "operationId": "GetHeight", diff --git a/indexer/services/comlink/src/controllers/api/v4/addresses-controller.ts b/indexer/services/comlink/src/controllers/api/v4/addresses-controller.ts index c2f4964fc0..1539fea52c 100644 --- a/indexer/services/comlink/src/controllers/api/v4/addresses-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/addresses-controller.ts @@ -378,6 +378,7 @@ async function getSubaccountResponse( return assetPositionToResponseObject( assetPosition, assetIdToAsset, + subaccount.subaccountNumber, ); }, ); diff --git a/indexer/services/comlink/src/controllers/api/v4/asset-positions-controller.ts b/indexer/services/comlink/src/controllers/api/v4/asset-positions-controller.ts index 3aca9f0796..7bf0ae96c2 100644 --- a/indexer/services/comlink/src/controllers/api/v4/asset-positions-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/asset-positions-controller.ts @@ -52,7 +52,7 @@ const router: express.Router = express.Router(); const controllerName: string = 'asset-positions-controller'; @Route('assetPositions') -class AddressesController extends Controller { +class AssetPositionsController extends Controller { @Get('/') async getAssetPositions( @Query() address: string, @@ -108,7 +108,9 @@ class AddressesController extends Controller { let assetPositionsMap: AssetPositionsMap = _.chain(sortedAssetPositions) .map( - (position: AssetPositionFromDatabase) => assetPositionToResponseObject(position, idToAsset), + (position: AssetPositionFromDatabase) => assetPositionToResponseObject(position, + idToAsset, + subaccountNumber), ).keyBy( (positionResponse: AssetPositionResponseObject) => positionResponse.symbol, ).value(); @@ -159,11 +161,14 @@ router.get( subaccountNumber, }: AssetPositionRequest = matchedData(req) as AssetPositionRequest; + // The schema checks allow subaccountNumber to be a string, but we know it's a number here. + const subaccountNum : number = +subaccountNumber; + try { - const controller: AddressesController = new AddressesController(); + const controller: AssetPositionsController = new AssetPositionsController(); const response: AssetPositionResponse = await controller.getAssetPositions( address, - subaccountNumber, + subaccountNum, ); return res.send(response); diff --git a/indexer/services/comlink/src/controllers/api/v4/fills-controller.ts b/indexer/services/comlink/src/controllers/api/v4/fills-controller.ts index eed5255103..aff2ed3f51 100644 --- a/indexer/services/comlink/src/controllers/api/v4/fills-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/fills-controller.ts @@ -8,6 +8,7 @@ import { FillFromDatabase, QueryableField, } from '@dydxprotocol-indexer/postgres'; +import { getChildSubaccountNums } from '@dydxprotocol-indexer/postgres/build/src/lib/api-translations'; import express from 'express'; import { checkSchema, @@ -27,12 +28,17 @@ import { getClobPairId, handleControllerError, isDefined, } from '../../../lib/helpers'; import { rateLimiterMiddleware } from '../../../lib/rate-limit'; -import { CheckLimitAndCreatedBeforeOrAtSchema, CheckSubaccountSchema } from '../../../lib/validation/schemas'; +import { CheckLimitAndCreatedBeforeOrAtSchema, CheckSubaccountSchema, CheckParentSubaccountSchema } from '../../../lib/validation/schemas'; import { handleValidationErrors } from '../../../request-helpers/error-handler'; import ExportResponseCodeStats from '../../../request-helpers/export-response-code-stats'; import { fillToResponseObject } from '../../../request-helpers/request-transformer'; import { - FillRequest, FillResponse, FillResponseObject, MarketAndTypeByClobPairId, MarketType, + FillRequest, + FillResponse, + FillResponseObject, + MarketAndTypeByClobPairId, + MarketType, + ParentSubaccountFillRequest, } from '../../../types'; const router: express.Router = express.Router(); @@ -90,7 +96,72 @@ class FillsController extends Controller { return { fills: fills.map((fill: FillFromDatabase): FillResponseObject => { - return fillToResponseObject(fill, clobPairIdToMarket); + return fillToResponseObject(fill, clobPairIdToMarket, subaccountNumber); + }), + }; + } + + @Get('/parentSubaccount') + async getFillsForParentSubaccount( + @Query() address: string, + @Query() parentSubaccountNumber: number, + @Query() market?: string, + @Query() marketType?: MarketType, + @Query() limit?: number, + @Query() createdBeforeOrAtHeight?: number, + @Query() createdBeforeOrAt?: IsoString, + ): Promise { + // TODO(DEC-656): Change to using a cache of markets in Redis similar to Librarian instead of + // querying the DB. + let clobPairId: string | undefined; + if (isDefined(market) && isDefined(marketType)) { + clobPairId = await getClobPairId(market!, marketType!); + + if (clobPairId === undefined) { + throw new NotFoundError(`${market} not found in markets of type ${marketType}`); + } + } + + // Get subaccountIds for all child subaccounts of the parent subaccount + // Create a record of subaccountId to subaccount number + const childIdtoSubaccountNumber: Record = {}; + getChildSubaccountNums(parentSubaccountNumber).forEach( + (subaccountNum: number) => { + childIdtoSubaccountNumber[SubaccountTable.uuid(address, subaccountNum)] = subaccountNum; + }, + ); + const subaccountIds: string[] = Object.keys(childIdtoSubaccountNumber); + + const fills: FillFromDatabase[] = await FillTable.findAll( + { + subaccountId: subaccountIds, + clobPairId, + limit, + createdBeforeOrAtHeight: createdBeforeOrAtHeight + ? createdBeforeOrAtHeight.toString() + : undefined, + createdBeforeOrAt, + }, + [QueryableField.LIMIT], + ); + + const clobPairIdToPerpetualMarket: Record< + string, + PerpetualMarketFromDatabase> = perpetualMarketRefresher.getClobPairIdToPerpetualMarket(); + const clobPairIdToMarket: MarketAndTypeByClobPairId = _.mapValues( + clobPairIdToPerpetualMarket, + (perpetualMarket: PerpetualMarketFromDatabase) => { + return { + marketType: MarketType.PERPETUAL, + market: perpetualMarket.ticker, + }; + }, + ); + + return { + fills: fills.map((fill: FillFromDatabase): FillResponseObject => { + return fillToResponseObject(fill, clobPairIdToMarket, + childIdtoSubaccountNumber[fill.subaccountId]); }), }; } @@ -139,13 +210,16 @@ router.get( createdBeforeOrAt, }: FillRequest = matchedData(req) as FillRequest; + // The schema checks allow subaccountNumber to be a string, but we know it's a number here. + const subaccountNum : number = +subaccountNumber; + // TODO(DEC-656): Change to using a cache of markets in Redis similar to Librarian instead of // querying the DB. try { const controller: FillsController = new FillsController(); const response: FillResponse = await controller.getFills( address, - subaccountNumber, + subaccountNum, market, marketType, limit, @@ -171,4 +245,82 @@ router.get( }, ); +router.get( + '/parentSubaccountNumber', + rateLimiterMiddleware(getReqRateLimiter), + ...CheckParentSubaccountSchema, + ...CheckLimitAndCreatedBeforeOrAtSchema, + // Use conditional validations such that market is required if marketType is in the query + // parameters and vice-versa. + // Reference https://express-validator.github.io/docs/validation-chain-api.html#ifcondition + query('market').if(query('marketType').exists()).notEmpty() + .withMessage('market must be provided if marketType is provided'), + query('marketType').if(query('market').exists()).notEmpty() + .withMessage('marketType must be provided if market is provided'), + // TODO(DEC-656): Validate market/marketType against cached markets. + ...checkSchema({ + market: { + in: ['query'], + isString: true, + optional: true, + }, + marketType: { + in: ['query'], + isIn: { + options: [Object.values(MarketType)], + }, + optional: true, + errorMessage: 'marketType must be a valid market type (PERPETUAL/SPOT)', + }, + }), + handleValidationErrors, + complianceAndGeoCheck, + ExportResponseCodeStats({ controllerName }), + async (req: express.Request, res: express.Response) => { + const start: number = Date.now(); + const { + address, + parentSubaccountNumber, + market, + marketType, + limit, + createdBeforeOrAtHeight, + createdBeforeOrAt, + }: ParentSubaccountFillRequest = matchedData(req) as ParentSubaccountFillRequest; + + // The schema checks allow subaccountNumber to be a string, but we know it's a number here. + const parentSubaccountNum : number = +parentSubaccountNumber; + + // TODO(DEC-656): Change to using a cache of markets in Redis similar to Librarian instead of + // querying the DB. + try { + const controller: FillsController = new FillsController(); + const response: FillResponse = await controller.getFillsForParentSubaccount( + address, + parentSubaccountNum, + market, + marketType, + limit, + createdBeforeOrAtHeight, + createdBeforeOrAt, + ); + + return res.send(response); + } catch (error) { + return handleControllerError( + 'FillsController GET /parentSubaccountNumber', + 'Fills error', + error, + req, + res, + ); + } finally { + stats.timing( + `${config.SERVICE_NAME}.${controllerName}.get_fills.timing`, + Date.now() - start, + ); + } + }, +); + export default router; diff --git a/indexer/services/comlink/src/controllers/api/v4/historical-block-trading-rewards-controller.ts b/indexer/services/comlink/src/controllers/api/v4/historical-block-trading-rewards-controller.ts index ff82c3b97e..8b57ffd3d6 100644 --- a/indexer/services/comlink/src/controllers/api/v4/historical-block-trading-rewards-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/historical-block-trading-rewards-controller.ts @@ -24,7 +24,7 @@ import { tradingRewardToResponse } from '../../../request-helpers/request-transf import { HistoricalBlockTradingRewardRequest as HistoricalBlockTradingRewardsRequest, HistoricalBlockTradingRewardsResponse } from '../../../types'; const router: express.Router = express.Router(); -const controllerName: string = 'height-controller'; +const controllerName: string = 'historical-block-trading-rewards-controller'; @Route('historicalBlockTradingRewards') class HistoricalBlockTradingRewardsController extends Controller { diff --git a/indexer/services/comlink/src/controllers/api/v4/historical-trading-reward-aggregations-controller.ts b/indexer/services/comlink/src/controllers/api/v4/historical-trading-reward-aggregations-controller.ts index 878c96d288..777a3ff78d 100644 --- a/indexer/services/comlink/src/controllers/api/v4/historical-trading-reward-aggregations-controller.ts +++ b/indexer/services/comlink/src/controllers/api/v4/historical-trading-reward-aggregations-controller.ts @@ -25,7 +25,7 @@ import { tradingRewardAggregationToResponse } from '../../../request-helpers/req import { HistoricalTradingRewardAggregationRequest, HistoricalTradingRewardAggregationsResponse } from '../../../types'; const router: express.Router = express.Router(); -const controllerName: string = 'height-controller'; +const controllerName: string = 'historical-trading-reward-aggregations-controller'; @Route('historicalTradingRewardAggregations') class HistoricalTradingRewardAggregationsController extends Controller { diff --git a/indexer/services/comlink/src/lib/constants.ts b/indexer/services/comlink/src/lib/constants.ts index ce8c65aed0..5be1cbf8d5 100644 --- a/indexer/services/comlink/src/lib/constants.ts +++ b/indexer/services/comlink/src/lib/constants.ts @@ -13,6 +13,7 @@ export const ZERO_USDC_POSITION: AssetPositionResponseObject = { symbol: USDC_SYMBOL, side: PositionSide.LONG, assetId: USDC_ASSET_ID, + subaccountNumber: 0, }; export const SPARKLINE_TIME_PERIOD_TO_LIMIT_MAP: diff --git a/indexer/services/comlink/src/lib/validation/schemas.ts b/indexer/services/comlink/src/lib/validation/schemas.ts index c13a17ed83..5d43d0efb5 100644 --- a/indexer/services/comlink/src/lib/validation/schemas.ts +++ b/indexer/services/comlink/src/lib/validation/schemas.ts @@ -18,6 +18,20 @@ export const CheckSubaccountSchema = checkSchema({ }, }); +export const CheckParentSubaccountSchema = checkSchema({ + address: { + in: ['params', 'query'], + isString: true, + }, + parentSubaccountNumber: { + in: ['params', 'query'], + isInt: { + options: { gt: -1, lt: MAX_SUBACCOUNT_NUMBER + 1 }, + }, + errorMessage: 'parentSubaccountNumber must be a non-negative integer less than 128', + }, +}); + export const checkAddressSchemaRecord: Record = { address: { in: ['params'], diff --git a/indexer/services/comlink/src/request-helpers/export-response-code-stats.ts b/indexer/services/comlink/src/request-helpers/export-response-code-stats.ts index c2565b8d4b..999cc83b19 100644 --- a/indexer/services/comlink/src/request-helpers/export-response-code-stats.ts +++ b/indexer/services/comlink/src/request-helpers/export-response-code-stats.ts @@ -15,6 +15,8 @@ export default (options?: ResponseStatsOptions) => ( response.on('finish', () => { stats.increment(`${config.SERVICE_NAME}.${options?.controllerName}.response_status_code.${response.statusCode}`, 1, { path: request.route.path, method: request.method }); + stats.increment(`${config.SERVICE_NAME}.${options?.controllerName}.response_status_code`, + 1, { path: request.route.path, method: request.method }); stats.increment(`${config.SERVICE_NAME}.response_status_code.${response.statusCode}`, 1, { path: request.route.path, method: request.method }); }); diff --git a/indexer/services/comlink/src/request-helpers/request-transformer.ts b/indexer/services/comlink/src/request-helpers/request-transformer.ts index 1fe32aa721..b516e900f9 100644 --- a/indexer/services/comlink/src/request-helpers/request-transformer.ts +++ b/indexer/services/comlink/src/request-helpers/request-transformer.ts @@ -115,6 +115,7 @@ export function perpetualPositionToResponseObject( export function assetPositionToResponseObject( position: AssetPositionFromDatabase, assetMap: AssetById, + subaccountNumber: number, ): AssetPositionResponseObject { return { @@ -122,6 +123,7 @@ export function assetPositionToResponseObject( side: position.isLong ? PositionSide.LONG : PositionSide.SHORT, size: position.size, assetId: position.assetId, + subaccountNumber, }; } @@ -134,6 +136,7 @@ export function assetPositionToResponseObject( export function fillToResponseObject( fill: FillFromDatabase, marketsByClobPairId: MarketAndTypeByClobPairId, + subaccountNumber: number, ): FillResponseObject { return { id: fill.id, @@ -149,6 +152,7 @@ export function fillToResponseObject( createdAtHeight: fill.createdAtHeight, orderId: fill.orderId, clientMetadata: fill.clientMetadata, + subaccountNumber, }; } diff --git a/indexer/services/comlink/src/types.ts b/indexer/services/comlink/src/types.ts index c61fdd55fc..3a1ba4402e 100644 --- a/indexer/services/comlink/src/types.ts +++ b/indexer/services/comlink/src/types.ts @@ -105,6 +105,7 @@ export interface AssetPositionResponseObject { side: PositionSide; size: string; assetId: string; + subaccountNumber: number; } export type AssetPositionsMap = { [symbol: string]: AssetPositionResponseObject }; @@ -129,6 +130,7 @@ export interface FillResponseObject { createdAtHeight: string, orderId?: string, clientMetadata?: string, + subaccountNumber: number, } /* ------- TRANSFER TYPES ------- */ @@ -308,6 +310,10 @@ export interface SubaccountRequest extends AddressRequest { subaccountNumber: number, } +export interface ParentSubaccountRequest extends AddressRequest { + parentSubaccountNumber: number, +} + export interface LimitRequest { limit: number, } @@ -344,6 +350,12 @@ export interface FillRequest extends SubaccountRequest, LimitAndCreatedBeforeReq marketType: MarketType, } +export interface ParentSubaccountFillRequest + extends ParentSubaccountRequest, LimitAndCreatedBeforeRequest { + market: string, + marketType: MarketType, +} + export interface TradeRequest extends LimitAndCreatedBeforeRequest { ticker: string, } diff --git a/indexer/services/ender/__tests__/caches/candle-cache.test.ts b/indexer/services/ender/__tests__/caches/candle-cache.test.ts index d17d3a1a05..c058dd2974 100644 --- a/indexer/services/ender/__tests__/caches/candle-cache.test.ts +++ b/indexer/services/ender/__tests__/caches/candle-cache.test.ts @@ -76,7 +76,6 @@ describe('candleCache', () => { await startCandleCache(); const map: CandlesMap = getCandlesMap(); - expect(_.size(map)).toEqual(3); expect(_.size(map[testConstants.defaultPerpetualMarket.ticker])).toEqual(1); expect(_.size(map[testConstants.defaultPerpetualMarket2.ticker])).toEqual(0); expect(_.size(map[testConstants.defaultPerpetualMarket3.ticker])).toEqual(0); diff --git a/indexer/services/ender/__tests__/handlers/markets/market-create-handler.test.ts b/indexer/services/ender/__tests__/handlers/markets/market-create-handler.test.ts index 5ce1a88233..eb89b010e3 100644 --- a/indexer/services/ender/__tests__/handlers/markets/market-create-handler.test.ts +++ b/indexer/services/ender/__tests__/handlers/markets/market-create-handler.test.ts @@ -90,7 +90,7 @@ describe('marketCreateHandler', () => { const transactionIndex: number = 0; const marketCreate: MarketEventV1 = { - marketId: 3, + marketId: 100, marketCreate: { base: { pair: 'DYDX-USD', diff --git a/indexer/services/ender/__tests__/handlers/stateful-order/conditional-order-triggered-handler.test.ts b/indexer/services/ender/__tests__/handlers/stateful-order/conditional-order-triggered-handler.test.ts index e9a02dc164..d01e01def6 100644 --- a/indexer/services/ender/__tests__/handlers/stateful-order/conditional-order-triggered-handler.test.ts +++ b/indexer/services/ender/__tests__/handlers/stateful-order/conditional-order-triggered-handler.test.ts @@ -143,6 +143,7 @@ describe('conditionalOrderTriggeredHandler', () => { producerSendMock, orderId: conditionalOrderId, offchainUpdate: expectedOffchainUpdate, + headers: { message_received_timestamp: kafkaMessage.timestamp, event_type: 'ConditionalOrderTriggered' }, }); }); diff --git a/indexer/services/ender/__tests__/handlers/stateful-order/stateful-order-placement-handler.test.ts b/indexer/services/ender/__tests__/handlers/stateful-order/stateful-order-placement-handler.test.ts index dd0ca482c4..52e0056dc4 100644 --- a/indexer/services/ender/__tests__/handlers/stateful-order/stateful-order-placement-handler.test.ts +++ b/indexer/services/ender/__tests__/handlers/stateful-order/stateful-order-placement-handler.test.ts @@ -180,6 +180,7 @@ describe('statefulOrderPlacementHandler', () => { producerSendMock, orderId: defaultOrder.orderId!, offchainUpdate: expectedOffchainUpdate, + headers: { message_received_timestamp: kafkaMessage.timestamp, event_type: 'StatefulOrderPlacement' }, }); }); diff --git a/indexer/services/ender/__tests__/handlers/stateful-order/stateful-order-removal-handler.test.ts b/indexer/services/ender/__tests__/handlers/stateful-order/stateful-order-removal-handler.test.ts index 68aa0b226b..353808dba0 100644 --- a/indexer/services/ender/__tests__/handlers/stateful-order/stateful-order-removal-handler.test.ts +++ b/indexer/services/ender/__tests__/handlers/stateful-order/stateful-order-removal-handler.test.ts @@ -21,7 +21,10 @@ import { DydxIndexerSubtypes } from '../../../src/lib/types'; import { defaultDateTime, defaultHeight, - defaultOrderId, defaultPreviousHeight, defaultTime, defaultTxHash, + defaultOrderId, + defaultPreviousHeight, + defaultTime, + defaultTxHash, } from '../../helpers/constants'; import { createKafkaMessageFromStatefulOrderEvent } from '../../helpers/kafka-helpers'; import { updateBlockCache } from '../../../src/caches/block-cache'; @@ -130,6 +133,7 @@ describe('statefulOrderRemovalHandler', () => { producerSendMock, orderId: defaultOrderId, offchainUpdate: expectedOffchainUpdate, + headers: { message_received_timestamp: kafkaMessage.timestamp, event_type: 'StatefulOrderRemoval' }, }); }); diff --git a/indexer/services/ender/__tests__/helpers/constants.ts b/indexer/services/ender/__tests__/helpers/constants.ts index beec5c47f8..e058d9dcec 100644 --- a/indexer/services/ender/__tests__/helpers/constants.ts +++ b/indexer/services/ender/__tests__/helpers/constants.ts @@ -24,6 +24,7 @@ import { OrderFillEventV1, OrderRemovalReason, PerpetualMarketCreateEventV1, + PerpetualMarketType, StatefulOrderEventV1, SubaccountMessage, SubaccountUpdateEventV1, @@ -138,6 +139,7 @@ export const defaultPerpetualMarketCreateEvent: PerpetualMarketCreateEventV1 = { subticksPerTick: 100, stepBaseQuantums: Long.fromValue(10, true), liquidityTier: 0, + marketType: PerpetualMarketType.PERPETUAL_MARKET_TYPE_CROSS, }; export const defaultLiquidityTierUpsertEvent: LiquidityTierUpsertEventV1 = { diff --git a/indexer/services/ender/__tests__/helpers/indexer-proto-helpers.ts b/indexer/services/ender/__tests__/helpers/indexer-proto-helpers.ts index 6dc900c143..c19b4424ec 100644 --- a/indexer/services/ender/__tests__/helpers/indexer-proto-helpers.ts +++ b/indexer/services/ender/__tests__/helpers/indexer-proto-helpers.ts @@ -51,7 +51,7 @@ import { PerpetualMarketCreateEventV1, DeleveragingEventV1, } from '@dydxprotocol-indexer/v4-protos'; -import { Message, ProducerRecord } from 'kafkajs'; +import { IHeaders, Message, ProducerRecord } from 'kafkajs'; import _ from 'lodash'; import { @@ -318,10 +318,12 @@ export function expectVulcanKafkaMessage({ producerSendMock, orderId, offchainUpdate, + headers, }: { producerSendMock: jest.SpyInstance, orderId: IndexerOrderId, offchainUpdate: OffChainUpdateV1, + headers?: IHeaders, }): void { expect(producerSendMock.mock.calls.length).toBeGreaterThanOrEqual(1); expect(producerSendMock.mock.calls[0].length).toBeGreaterThanOrEqual(1); @@ -339,7 +341,6 @@ export function expectVulcanKafkaMessage({ vulcanProducerRecord.messages, (message: Message): VulcanMessage => { expect(Buffer.isBuffer(message.value)); - const messageValueBinary: Uint8Array = new Uint8Array( // Can assume Buffer, since we check above that it is a buffer message.value as Buffer, @@ -348,6 +349,7 @@ export function expectVulcanKafkaMessage({ return { key: message.key as Buffer, value: OffChainUpdateV1.decode(messageValueBinary), + headers: message.headers, }; }, ); @@ -355,6 +357,7 @@ export function expectVulcanKafkaMessage({ expect(vulcanMessages).toContainEqual({ key: getOrderIdHash(orderId), value: offchainUpdate, + headers, }); } diff --git a/indexer/services/ender/__tests__/lib/block-processor.test.ts b/indexer/services/ender/__tests__/lib/block-processor.test.ts index d0ea660cdb..6f8c18a6f3 100644 --- a/indexer/services/ender/__tests__/lib/block-processor.test.ts +++ b/indexer/services/ender/__tests__/lib/block-processor.test.ts @@ -121,6 +121,7 @@ describe('block-processor', () => { const blockProcessor: BlockProcessor = new BlockProcessor( block, txId, + defaultDateTime.toString(), ); blockProcessor.batchedHandlers = batchedHandlers; blockProcessor.syncHandlers = syncHandlers; @@ -149,6 +150,7 @@ describe('block-processor', () => { const blockProcessor: BlockProcessor = new BlockProcessor( block, txId, + defaultDateTime.toString(), ); blockProcessor.batchedHandlers = batchedHandlers; blockProcessor.syncHandlers = syncHandlers; diff --git a/indexer/services/ender/__tests__/lib/on-message.test.ts b/indexer/services/ender/__tests__/lib/on-message.test.ts index 37c9f49263..58aef89a4a 100644 --- a/indexer/services/ender/__tests__/lib/on-message.test.ts +++ b/indexer/services/ender/__tests__/lib/on-message.test.ts @@ -759,9 +759,9 @@ describe('on-message', () => { await onMessage(kafkaMessage); - expect(stats.increment).toHaveBeenCalledWith(`${config.SERVICE_NAME}.block_already_parsed.failure`, 1); + expect(stats.increment).toHaveBeenCalledWith(`${config.SERVICE_NAME}.block_already_parsed`, 1); expect(stats.increment).toHaveBeenCalledWith('ender.received_kafka_message', 1); - expect(stats.timing).toHaveBeenCalledWith( + expect(stats.timing).not.toHaveBeenCalledWith( 'ender.message_time_in_queue', expect.any(Number), 1, { topic: KafkaTopics.TO_ENDER }); }); diff --git a/indexer/services/ender/src/caches/block-cache.ts b/indexer/services/ender/src/caches/block-cache.ts index e4f359500f..1b7af459f6 100644 --- a/indexer/services/ender/src/caches/block-cache.ts +++ b/indexer/services/ender/src/caches/block-cache.ts @@ -51,7 +51,7 @@ export async function shouldSkipBlock( canRefreshCache: boolean = true, ): Promise { if (blockAlreadyProcessed(blockHeight)) { - stats.increment(`${config.SERVICE_NAME}.block_already_parsed.failure`, 1); + stats.increment(`${config.SERVICE_NAME}.block_already_parsed`, 1); logger.info({ at: 'onMessage#onMessage', message: `Already processed block with block height: ${blockHeight}, so skipping`, diff --git a/indexer/services/ender/src/handlers/handler.ts b/indexer/services/ender/src/handlers/handler.ts index 51362c9965..664a6f52c5 100644 --- a/indexer/services/ender/src/handlers/handler.ts +++ b/indexer/services/ender/src/handlers/handler.ts @@ -18,6 +18,7 @@ import { OffChainUpdateV1, SubaccountId, } from '@dydxprotocol-indexer/v4-protos'; +import { IHeaders } from 'kafkajs'; import { DateTime } from 'luxon'; import * as pg from 'pg'; @@ -33,6 +34,7 @@ export type HandlerInitializer = new ( indexerTendermintEvent: IndexerTendermintEvent, txId: number, event: EventMessage, + messageReceivedTimestamp?: string, ) => Handler; /** @@ -49,6 +51,7 @@ export abstract class Handler { blockEventIndex: number; event: T; abstract eventType: string; + messageReceivedTimestamp?: string; constructor( block: IndexerTendermintBlock, @@ -56,6 +59,7 @@ export abstract class Handler { indexerTendermintEvent: IndexerTendermintEvent, txId: number, event: T, + messageReceivedTimestamp?: string, ) { this.block = block; this.blockEventIndex = blockEventIndex; @@ -63,6 +67,7 @@ export abstract class Handler { this.timestamp = DateTime.fromJSDate(block.time!); this.txId = txId; this.event = event; + this.messageReceivedTimestamp = messageReceivedTimestamp; } /** @@ -178,6 +183,7 @@ export abstract class Handler { protected generateConsolidatedVulcanKafkaEvent( key: Buffer, offChainUpdate: OffChainUpdateV1, + headers?: IHeaders, ): ConsolidatedKafkaEvent { stats.increment(`${config.SERVICE_NAME}.create_vulcan_kafka_event`, 1); @@ -186,6 +192,7 @@ export abstract class Handler { message: { key, value: offChainUpdate, + headers, }, }; } diff --git a/indexer/services/ender/src/handlers/stateful-order/conditional-order-triggered-handler.ts b/indexer/services/ender/src/handlers/stateful-order/conditional-order-triggered-handler.ts index 0cab1392da..f6969c7f0f 100644 --- a/indexer/services/ender/src/handlers/stateful-order/conditional-order-triggered-handler.ts +++ b/indexer/services/ender/src/handlers/stateful-order/conditional-order-triggered-handler.ts @@ -54,6 +54,10 @@ export class ConditionalOrderTriggeredHandler extends this.generateConsolidatedVulcanKafkaEvent( getOrderIdHash(order.orderId!), offChainUpdate, + { + message_received_timestamp: this.messageReceivedTimestamp, + event_type: 'ConditionalOrderTriggered', + }, ), ]; } diff --git a/indexer/services/ender/src/handlers/stateful-order/stateful-order-placement-handler.ts b/indexer/services/ender/src/handlers/stateful-order/stateful-order-placement-handler.ts index a2ea67f578..31a5104123 100644 --- a/indexer/services/ender/src/handlers/stateful-order/stateful-order-placement-handler.ts +++ b/indexer/services/ender/src/handlers/stateful-order/stateful-order-placement-handler.ts @@ -54,6 +54,10 @@ export class StatefulOrderPlacementHandler extends kafakEvents.push(this.generateConsolidatedVulcanKafkaEvent( getOrderIdHash(order.orderId!), offChainUpdate, + { + message_received_timestamp: this.messageReceivedTimestamp, + event_type: 'StatefulOrderPlacement', + }, )); return kafakEvents; diff --git a/indexer/services/ender/src/handlers/stateful-order/stateful-order-removal-handler.ts b/indexer/services/ender/src/handlers/stateful-order/stateful-order-removal-handler.ts index 5a162c850a..02715d1021 100644 --- a/indexer/services/ender/src/handlers/stateful-order/stateful-order-removal-handler.ts +++ b/indexer/services/ender/src/handlers/stateful-order/stateful-order-removal-handler.ts @@ -42,6 +42,10 @@ export class StatefulOrderRemovalHandler extends this.generateConsolidatedVulcanKafkaEvent( getOrderIdHash(orderIdProto), offChainUpdate, + { + message_received_timestamp: this.messageReceivedTimestamp, + event_type: 'StatefulOrderRemoval', + }, ), ]; } diff --git a/indexer/services/ender/src/lib/block-processor.ts b/indexer/services/ender/src/lib/block-processor.ts index 655e85d6b2..5a6e4cb7e6 100644 --- a/indexer/services/ender/src/lib/block-processor.ts +++ b/indexer/services/ender/src/lib/block-processor.ts @@ -77,13 +77,16 @@ export class BlockProcessor { txId: number; batchedHandlers: BatchedHandlers; syncHandlers: SyncHandlers; + messageReceivedTimestamp: string; constructor( block: IndexerTendermintBlock, txId: number, + messageReceivedTimestamp: string, ) { this.block = block; this.txId = txId; + this.messageReceivedTimestamp = messageReceivedTimestamp; this.sqlBlock = { ...this.block, events: new Array(this.block.events.length), @@ -205,6 +208,7 @@ export class BlockProcessor { const handlers: Handler[] = validator.createHandlers( eventProto.indexerTendermintEvent, this.txId, + this.messageReceivedTimestamp, ); _.map(handlers, (handler: Handler) => { diff --git a/indexer/services/ender/src/lib/kafka-publisher.ts b/indexer/services/ender/src/lib/kafka-publisher.ts index e1d7b018f4..8be583c0cc 100644 --- a/indexer/services/ender/src/lib/kafka-publisher.ts +++ b/indexer/services/ender/src/lib/kafka-publisher.ts @@ -247,6 +247,7 @@ export class KafkaPublisher { return { key: message.key, value: Buffer.from(Uint8Array.from(OffChainUpdateV1.encode(message.value).finish())), + headers: message.headers, }; }), }); diff --git a/indexer/services/ender/src/lib/on-message.ts b/indexer/services/ender/src/lib/on-message.ts index c33be095d7..99eec93054 100644 --- a/indexer/services/ender/src/lib/on-message.ts +++ b/indexer/services/ender/src/lib/on-message.ts @@ -49,7 +49,6 @@ export async function onMessage(message: KafkaMessage): Promise { const start: number = Date.now(); const indexerTendermintBlock: IndexerTendermintBlock | undefined = getIndexerTendermintBlock( message, - start, ); if (indexerTendermintBlock === undefined) { return; @@ -66,6 +65,15 @@ export async function onMessage(message: KafkaMessage): Promise { return; } + stats.timing( + `${config.SERVICE_NAME}.message_time_in_queue`, + start - Number(message.timestamp), + STATS_NO_SAMPLING, + { + topic: KafkaTopics.TO_ENDER, + }, + ); + let success: boolean = false; const txId: number = await Transaction.start(); await Transaction.setIsolationLevel(txId, IsolationLevel.READ_UNCOMMITTED); @@ -75,6 +83,7 @@ export async function onMessage(message: KafkaMessage): Promise { const blockProcessor: BlockProcessor = new BlockProcessor( indexerTendermintBlock, txId, + message.timestamp, ); const kafkaPublisher: KafkaPublisher = await blockProcessor.process(); @@ -141,7 +150,6 @@ export async function onMessage(message: KafkaMessage): Promise { */ function getIndexerTendermintBlock( message: KafkaMessage, - start: number, ): IndexerTendermintBlock | undefined { if (!message || !message.value || !message.timestamp) { stats.increment(`${config.SERVICE_NAME}.empty_kafka_message`, 1); @@ -151,15 +159,6 @@ function getIndexerTendermintBlock( }); return undefined; } - - stats.timing( - `${config.SERVICE_NAME}.message_time_in_queue`, - start - Number(message.timestamp), - STATS_NO_SAMPLING, - { - topic: KafkaTopics.TO_ENDER, - }, - ); try { const messageValueBinary: Uint8Array = new Uint8Array(message.value); logger.info({ diff --git a/indexer/services/ender/src/lib/types.ts b/indexer/services/ender/src/lib/types.ts index b0c8857332..be228f14ce 100644 --- a/indexer/services/ender/src/lib/types.ts +++ b/indexer/services/ender/src/lib/types.ts @@ -33,6 +33,7 @@ import { DeleveragingEventV1, TradingRewardsEventV1, } from '@dydxprotocol-indexer/v4-protos'; +import { IHeaders } from 'kafkajs'; import Long from 'long'; // Type sourced from protocol: @@ -217,6 +218,7 @@ export interface AnnotatedSubaccountMessage extends SubaccountMessage { export interface VulcanMessage { key: Buffer, value: OffChainUpdateV1, + headers?: IHeaders, } export type ConsolidatedKafkaEvent = { diff --git a/indexer/services/ender/src/validators/asset-validator.ts b/indexer/services/ender/src/validators/asset-validator.ts index 0bf140e322..cf2186c0e6 100644 --- a/indexer/services/ender/src/validators/asset-validator.ts +++ b/indexer/services/ender/src/validators/asset-validator.ts @@ -10,6 +10,7 @@ export class AssetValidator extends Validator { public createHandlers( indexerTendermintEvent: IndexerTendermintEvent, txId: number, + _: string, ): Handler[] { const handler: Handler = new AssetCreationHandler( this.block, diff --git a/indexer/services/ender/src/validators/deleveraging-validator.ts b/indexer/services/ender/src/validators/deleveraging-validator.ts index 931e7e153c..6336f76310 100644 --- a/indexer/services/ender/src/validators/deleveraging-validator.ts +++ b/indexer/services/ender/src/validators/deleveraging-validator.ts @@ -38,6 +38,7 @@ export class DeleveragingValidator extends Validator { public createHandlers( indexerTendermintEvent: IndexerTendermintEvent, txId: number, + _: string, ): Handler[] { return [ new DeleveragingHandler( diff --git a/indexer/services/ender/src/validators/funding-validator.ts b/indexer/services/ender/src/validators/funding-validator.ts index 3de03e61e1..4e18df610a 100644 --- a/indexer/services/ender/src/validators/funding-validator.ts +++ b/indexer/services/ender/src/validators/funding-validator.ts @@ -42,6 +42,7 @@ export class FundingValidator extends Validator { public createHandlers( indexerTendermintEvent: IndexerTendermintEvent, txId: number, + _: string, ): Handler[] { const handler: Handler = new FundingHandler( this.block, diff --git a/indexer/services/ender/src/validators/liquidity-tier-validator.ts b/indexer/services/ender/src/validators/liquidity-tier-validator.ts index a41d53df71..33256bd425 100644 --- a/indexer/services/ender/src/validators/liquidity-tier-validator.ts +++ b/indexer/services/ender/src/validators/liquidity-tier-validator.ts @@ -34,6 +34,7 @@ export class LiquidityTierValidator extends Validator[] { const handler: Handler = new LiquidityTierHandler( this.block, diff --git a/indexer/services/ender/src/validators/market-validator.ts b/indexer/services/ender/src/validators/market-validator.ts index fef4dbd787..ff5218ad90 100644 --- a/indexer/services/ender/src/validators/market-validator.ts +++ b/indexer/services/ender/src/validators/market-validator.ts @@ -104,6 +104,7 @@ export class MarketValidator extends Validator { public createHandlers( indexerTendermintEvent: IndexerTendermintEvent, txId: number, + __: string, ): Handler[] { const Initializer: HandlerInitializer | undefined = this.getHandlerInitializer(); diff --git a/indexer/services/ender/src/validators/order-fill-validator.ts b/indexer/services/ender/src/validators/order-fill-validator.ts index d25eb6421d..c8a42b5bf7 100644 --- a/indexer/services/ender/src/validators/order-fill-validator.ts +++ b/indexer/services/ender/src/validators/order-fill-validator.ts @@ -93,6 +93,7 @@ export class OrderFillValidator extends Validator { public createHandlers( indexerTendermintEvent: IndexerTendermintEvent, txId: number, + __: string, ): Handler[] { const orderFillEventsWithLiquidity: OrderFillEventWithLiquidity[] = [ { diff --git a/indexer/services/ender/src/validators/perpetual-market-validator.ts b/indexer/services/ender/src/validators/perpetual-market-validator.ts index 93531c835b..b599aae739 100644 --- a/indexer/services/ender/src/validators/perpetual-market-validator.ts +++ b/indexer/services/ender/src/validators/perpetual-market-validator.ts @@ -38,6 +38,7 @@ export class PerpetualMarketValidator extends Validator[] { const handler: Handler = new PerpetualMarketCreationHandler( this.block, diff --git a/indexer/services/ender/src/validators/stateful-order-validator.ts b/indexer/services/ender/src/validators/stateful-order-validator.ts index 066b7fbb20..8507fb6ca7 100644 --- a/indexer/services/ender/src/validators/stateful-order-validator.ts +++ b/indexer/services/ender/src/validators/stateful-order-validator.ts @@ -211,6 +211,7 @@ export class StatefulOrderValidator extends Validator { public createHandlers( indexerTendermintEvent: IndexerTendermintEvent, txId: number, + messageReceivedTimestamp: string, ): Handler[] { const Initializer: HandlerInitializer | undefined = this.getHandlerInitializer(); @@ -227,6 +228,7 @@ export class StatefulOrderValidator extends Validator { indexerTendermintEvent, txId, this.event, + messageReceivedTimestamp, ); return [handler]; diff --git a/indexer/services/ender/src/validators/subaccount-update-validator.ts b/indexer/services/ender/src/validators/subaccount-update-validator.ts index a747a43716..df37b60c37 100644 --- a/indexer/services/ender/src/validators/subaccount-update-validator.ts +++ b/indexer/services/ender/src/validators/subaccount-update-validator.ts @@ -19,6 +19,7 @@ export class SubaccountUpdateValidator extends Validator[] { return [ new SubaccountUpdateHandler( diff --git a/indexer/services/ender/src/validators/trading-rewards-validator.ts b/indexer/services/ender/src/validators/trading-rewards-validator.ts index 96ce7b2433..fe51832c0f 100644 --- a/indexer/services/ender/src/validators/trading-rewards-validator.ts +++ b/indexer/services/ender/src/validators/trading-rewards-validator.ts @@ -36,6 +36,7 @@ export class TradingRewardsValidator extends Validator { public createHandlers( indexerTendermintEvent: IndexerTendermintEvent, txId: number, + __: string, ): Handler[] { return [ new TradingRewardsHandler( diff --git a/indexer/services/ender/src/validators/transfer-validator.ts b/indexer/services/ender/src/validators/transfer-validator.ts index 1529403579..330cab294e 100644 --- a/indexer/services/ender/src/validators/transfer-validator.ts +++ b/indexer/services/ender/src/validators/transfer-validator.ts @@ -46,6 +46,7 @@ export class TransferValidator extends Validator { public createHandlers( indexerTendermintEvent: IndexerTendermintEvent, txId: number, + _: string, ): Handler[] { return [ new TransferHandler( diff --git a/indexer/services/ender/src/validators/update-clob-pair-validator.ts b/indexer/services/ender/src/validators/update-clob-pair-validator.ts index 315075da3f..7c7761bb13 100644 --- a/indexer/services/ender/src/validators/update-clob-pair-validator.ts +++ b/indexer/services/ender/src/validators/update-clob-pair-validator.ts @@ -20,6 +20,7 @@ export class UpdateClobPairValidator extends Validator { public createHandlers( indexerTendermintEvent: IndexerTendermintEvent, txId: number, + _: string, ): Handler[] { const handler: Handler = new UpdateClobPairHandler( this.block, diff --git a/indexer/services/ender/src/validators/update-perpetual-validator.ts b/indexer/services/ender/src/validators/update-perpetual-validator.ts index b0511d7e48..25caf65123 100644 --- a/indexer/services/ender/src/validators/update-perpetual-validator.ts +++ b/indexer/services/ender/src/validators/update-perpetual-validator.ts @@ -18,6 +18,7 @@ export class UpdatePerpetualValidator extends Validator public createHandlers( indexerTendermintEvent: IndexerTendermintEvent, txId: number, + _: string, ): Handler[] { const handler: Handler = new UpdatePerpetualHandler( this.block, diff --git a/indexer/services/ender/src/validators/validator.ts b/indexer/services/ender/src/validators/validator.ts index 3db9fe9b5a..f9c0a53662 100644 --- a/indexer/services/ender/src/validators/validator.ts +++ b/indexer/services/ender/src/validators/validator.ts @@ -56,5 +56,6 @@ export abstract class Validator { public abstract createHandlers( indexerTendermintEvent: IndexerTendermintEvent, txId: number, + messageReceivedTimestamp: string, ): Handler[]; } diff --git a/indexer/services/roundtable/__tests__/helpers/pnl-ticks-helper.test.ts b/indexer/services/roundtable/__tests__/helpers/pnl-ticks-helper.test.ts index 02f6c4c3e9..ef7487db4c 100644 --- a/indexer/services/roundtable/__tests__/helpers/pnl-ticks-helper.test.ts +++ b/indexer/services/roundtable/__tests__/helpers/pnl-ticks-helper.test.ts @@ -154,26 +154,18 @@ describe('pnl-ticks-helper', () => { _.Dictionary = await getBlockHeightToFundingIndexMap( subaccountsWithTransfers, accountsToUpdate1, ); - expect(heightToFundingIndices1).toEqual({ - 1: { - [testConstants.defaultPerpetualMarket.id]: Big('100'), - [testConstants.defaultPerpetualMarket2.id]: Big('2'), - [testConstants.defaultPerpetualMarket3.id]: Big('0'), - }, - }); + + expect(heightToFundingIndices1[1][testConstants.defaultPerpetualMarket.id]).toEqual(Big('100')); + expect(heightToFundingIndices1[1][testConstants.defaultPerpetualMarket2.id]).toEqual(Big('2')); const accountsToUpdate2: string[] = [testConstants.defaultSubaccountId2]; const heightToFundingIndices2: _.Dictionary = await getBlockHeightToFundingIndexMap( subaccountsWithTransfers, accountsToUpdate2, ); - expect(heightToFundingIndices2).toEqual({ - 2: { - [testConstants.defaultPerpetualMarket.id]: Big('10050'), - [testConstants.defaultPerpetualMarket2.id]: Big('5'), - [testConstants.defaultPerpetualMarket3.id]: Big('0'), - }, - }); + + expect(heightToFundingIndices2[2][testConstants.defaultPerpetualMarket.id]).toEqual(Big('10050')); + expect(heightToFundingIndices2[2][testConstants.defaultPerpetualMarket2.id]).toEqual(Big('5')); const accountsToUpdate3: string[] = [ testConstants.defaultSubaccountId, testConstants.defaultSubaccountId2, @@ -183,23 +175,13 @@ describe('pnl-ticks-helper', () => { _.Dictionary = await getBlockHeightToFundingIndexMap( subaccountsWithTransfers, accountsToUpdate3, ); - expect(heightToFundingIndices3).toEqual({ - 1: { - [testConstants.defaultPerpetualMarket.id]: Big('100'), - [testConstants.defaultPerpetualMarket2.id]: Big('2'), - [testConstants.defaultPerpetualMarket3.id]: Big('0'), - }, - 2: { - [testConstants.defaultPerpetualMarket.id]: Big('10050'), - [testConstants.defaultPerpetualMarket2.id]: Big('5'), - [testConstants.defaultPerpetualMarket3.id]: Big('0'), - }, - 3: { - [testConstants.defaultPerpetualMarket.id]: Big('10050'), - [testConstants.defaultPerpetualMarket2.id]: Big('5'), - [testConstants.defaultPerpetualMarket3.id]: Big('0'), - }, - }); + + expect(heightToFundingIndices3[1][testConstants.defaultPerpetualMarket.id]).toEqual(Big('100')); + expect(heightToFundingIndices3[1][testConstants.defaultPerpetualMarket2.id]).toEqual(Big('2')); + expect(heightToFundingIndices3[2][testConstants.defaultPerpetualMarket.id]).toEqual(Big('10050')); + expect(heightToFundingIndices3[2][testConstants.defaultPerpetualMarket2.id]).toEqual(Big('5')); + expect(heightToFundingIndices3[3][testConstants.defaultPerpetualMarket.id]).toEqual(Big('10050')); + expect(heightToFundingIndices3[3][testConstants.defaultPerpetualMarket2.id]).toEqual(Big('5')); }); it('getUsdcTransfersSinceLastPnlTick with transfers', async () => { diff --git a/indexer/services/roundtable/__tests__/tasks/market-updater.test.ts b/indexer/services/roundtable/__tests__/tasks/market-updater.test.ts index efeb3b5c1f..b142b8b3d5 100644 --- a/indexer/services/roundtable/__tests__/tasks/market-updater.test.ts +++ b/indexer/services/roundtable/__tests__/tasks/market-updater.test.ts @@ -10,7 +10,6 @@ import { PerpetualPositionTable, testConstants, OraclePriceTable, - testMocks, PriceMap, BlockTable, LiquidityTiersFromDatabase, @@ -31,12 +30,78 @@ import { NextFundingCache, redis } from '@dydxprotocol-indexer/redis'; import { redisClient } from '../../src/helpers/redis'; import Big from 'big.js'; import { DateTime } from 'luxon'; +import * as SubaccountTable from '@dydxprotocol-indexer/postgres/build/src/stores/subaccount-table'; +import { + defaultAsset, + defaultAsset2, + defaultAsset3, + defaultBlock, + defaultBlock2, + defaultLiquidityTier, + defaultLiquidityTier2, + defaultMarket, + defaultMarket2, + defaultMarket3, + defaultPerpetualMarket, + defaultPerpetualMarket2, + defaultPerpetualMarket3, + defaultSubaccount, + defaultSubaccount2, + defaultTendermintEvent, + defaultTendermintEvent2, + defaultTendermintEvent3, + defaultTendermintEvent4, + defaultWallet, +} from '@dydxprotocol-indexer/postgres/build/__tests__/helpers/constants'; +import * as MarketTable from '@dydxprotocol-indexer/postgres/build/src/stores/market-table'; +import * as TendermintEventTable from '@dydxprotocol-indexer/postgres/build/src/stores/tendermint-event-table'; +import * as AssetTable from '@dydxprotocol-indexer/postgres/build/src/stores/asset-table'; +import * as WalletTable from '@dydxprotocol-indexer/postgres/build/src/stores/wallet-table'; jest.mock('@dydxprotocol-indexer/base', () => ({ ...jest.requireActual('@dydxprotocol-indexer/base'), wrapBackgroundTask: jest.fn(), })); +async function seedData() { + await Promise.all([ + SubaccountTable.create(defaultSubaccount), + SubaccountTable.create(defaultSubaccount2), + ]); + await Promise.all([ + MarketTable.create(defaultMarket), + MarketTable.create(defaultMarket2), + MarketTable.create(defaultMarket3), + ]); + await Promise.all([ + LiquidityTiersTable.create(defaultLiquidityTier), + LiquidityTiersTable.create(defaultLiquidityTier2), + ]); + await Promise.all([ + PerpetualMarketTable.create(defaultPerpetualMarket), + PerpetualMarketTable.create(defaultPerpetualMarket2), + PerpetualMarketTable.create(defaultPerpetualMarket3), + ]); + await Promise.all([ + BlockTable.create(defaultBlock), + BlockTable.create(defaultBlock2), + ]); + await Promise.all([ + TendermintEventTable.create(defaultTendermintEvent), + TendermintEventTable.create(defaultTendermintEvent2), + TendermintEventTable.create(defaultTendermintEvent3), + TendermintEventTable.create(defaultTendermintEvent4), + ]); + await Promise.all([ + AssetTable.create(defaultAsset), + AssetTable.create(defaultAsset2), + AssetTable.create(defaultAsset3), + ]); + await Promise.all([ + WalletTable.create(defaultWallet), + ]); +} + describe('market-updater', () => { const perpMarketUpdate1: PerpetualMarketUpdateObject = { @@ -65,7 +130,7 @@ describe('market-updater', () => { }); beforeEach(async () => { - await testMocks.seedData(); + await seedData(); }); afterAll(async () => { diff --git a/indexer/services/roundtable/src/tasks/aggregate-trading-rewards.ts b/indexer/services/roundtable/src/tasks/aggregate-trading-rewards.ts index f0beb0a83c..7bad1e8c1a 100644 --- a/indexer/services/roundtable/src/tasks/aggregate-trading-rewards.ts +++ b/indexer/services/roundtable/src/tasks/aggregate-trading-rewards.ts @@ -384,7 +384,7 @@ export class AggregateTradingReward { const blocks: BlockFromDatabase[] = await BlockTable.findAll({ createdOnOrAfter: time, limit: 1, - }, []); + }, [], { readReplica: true }); if (blocks.length === 0) { logger.error({ diff --git a/proto/dydxprotocol/clob/block_rate_limit_config.proto b/proto/dydxprotocol/clob/block_rate_limit_config.proto index 8f212132e7..273c2d536e 100644 --- a/proto/dydxprotocol/clob/block_rate_limit_config.proto +++ b/proto/dydxprotocol/clob/block_rate_limit_config.proto @@ -13,8 +13,10 @@ message BlockRateLimitConfiguration { // configurations. // // Specifying 0 values disables this rate limit. + // Deprecated in favor of `max_short_term_orders_and_cancels_per_n_blocks` + // for v5.x onwards. repeated MaxPerNBlocksRateLimit max_short_term_orders_per_n_blocks = 1 - [ (gogoproto.nullable) = false ]; + [ (gogoproto.nullable) = false, deprecated = true ]; // How many stateful order attempts (successful and failed) are allowed for // an account per N blocks. Note that the rate limits are applied @@ -31,8 +33,20 @@ message BlockRateLimitConfiguration { // rate limit configurations. // // Specifying 0 values disables this rate limit. + // Deprecated in favor of `max_short_term_orders_and_cancels_per_n_blocks` + // for v5.x onwards. repeated MaxPerNBlocksRateLimit max_short_term_order_cancellations_per_n_blocks = 3 + [ (gogoproto.nullable) = false, deprecated = true ]; + + // How many short term order place and cancel attempts (successful and failed) + // are allowed for an account per N blocks. Note that the rate limits are + // applied in an AND fashion such that an order placement must pass all rate + // limit configurations. + // + // Specifying 0 values disables this rate limit. + repeated MaxPerNBlocksRateLimit + max_short_term_orders_and_cancels_per_n_blocks = 4 [ (gogoproto.nullable) = false ]; } diff --git a/proto/dydxprotocol/indexer/events/events.proto b/proto/dydxprotocol/indexer/events/events.proto index c8d187f915..affeaefea8 100644 --- a/proto/dydxprotocol/indexer/events/events.proto +++ b/proto/dydxprotocol/indexer/events/events.proto @@ -5,6 +5,7 @@ import "cosmos_proto/cosmos.proto"; import "gogoproto/gogo.proto"; import "dydxprotocol/indexer/shared/removal_reason.proto"; import "dydxprotocol/indexer/protocol/v1/clob.proto"; +import "dydxprotocol/indexer/protocol/v1/perpetual.proto"; import "dydxprotocol/indexer/protocol/v1/subaccount.proto"; option go_package = "github.com/dydxprotocol/v4-chain/protocol/indexer/events"; @@ -352,6 +353,9 @@ message PerpetualMarketCreateEventV1 { // The liquidity_tier that this perpetual is associated with. // Defined in perpetuals.perpetual uint32 liquidity_tier = 10; + + // Market type of the perpetual. + dydxprotocol.indexer.protocol.v1.PerpetualMarketType market_type = 11; } // LiquidityTierUpsertEventV1 message contains all the information to diff --git a/proto/dydxprotocol/indexer/protocol/v1/perpetual.proto b/proto/dydxprotocol/indexer/protocol/v1/perpetual.proto new file mode 100644 index 0000000000..5becdf8b2f --- /dev/null +++ b/proto/dydxprotocol/indexer/protocol/v1/perpetual.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package dydxprotocol.indexer.protocol.v1; + +option go_package = "github.com/dydxprotocol/v4-chain/protocol/indexer/protocol/v1/types"; + +// Market type of perpetual. +// Defined in perpetual. +enum PerpetualMarketType { + // Unspecified market type. + PERPETUAL_MARKET_TYPE_UNSPECIFIED = 0; + // Market type for cross margin perpetual markets. + PERPETUAL_MARKET_TYPE_CROSS = 1; + // Market type for isolated margin perpetual markets. + PERPETUAL_MARKET_TYPE_ISOLATED = 2; +} diff --git a/proto/dydxprotocol/vault/tx.proto b/proto/dydxprotocol/vault/tx.proto index d6afd1bdbd..90da68e40f 100644 --- a/proto/dydxprotocol/vault/tx.proto +++ b/proto/dydxprotocol/vault/tx.proto @@ -1,7 +1,33 @@ syntax = "proto3"; package dydxprotocol.vault; +import "dydxprotocol/subaccounts/subaccount.proto"; +import "dydxprotocol/vault/vault.proto"; +import "gogoproto/gogo.proto"; + option go_package = "github.com/dydxprotocol/v4-chain/protocol/x/vault/types"; // Msg defines the Msg service. -service Msg {} +service Msg { + // DepositToVault deposits funds into a vault. + rpc DepositToVault(MsgDepositToVault) returns (MsgDepositToVaultResponse); +} + +// MsgDepositToVault is the Msg/DepositToVault request type. +message MsgDepositToVault { + // The vault to deposit into. + VaultId vault_id = 1; + + // The subaccount to deposit from. + dydxprotocol.subaccounts.SubaccountId subaccount_id = 2; + + // Number of quote quantums to deposit. + bytes quote_quantums = 3 [ + (gogoproto.customtype) = + "github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt", + (gogoproto.nullable) = false + ]; +} + +// MsgDepositToVaultResponse is the Msg/DepositToVault response type. +message MsgDepositToVaultResponse {} diff --git a/proto/dydxprotocol/vault/vault.proto b/proto/dydxprotocol/vault/vault.proto new file mode 100644 index 0000000000..87b8c9dd5a --- /dev/null +++ b/proto/dydxprotocol/vault/vault.proto @@ -0,0 +1,34 @@ +syntax = "proto3"; +package dydxprotocol.vault; + +import "gogoproto/gogo.proto"; + +option go_package = "github.com/dydxprotocol/v4-chain/protocol/x/vault/types"; + +// VaultType represents different types of vaults. +enum VaultType { + // Default value, invalid and unused. + VAULT_TYPE_UNSPECIFIED = 0; + + // Vault is associated with a CLOB pair. + VAULT_TYPE_CLOB = 1; +} + +// VaultId uniquely identifies a vault by its type and number. +message VaultId { + // Type of the vault. + VaultType type = 1; + + // Unique ID of the vault within above type. + uint32 number = 2; +} + +// NumShares represents the number of shares in a vault. +message NumShares { + // Number of shares. + bytes num_shares = 1 [ + (gogoproto.customtype) = + "github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt", + (gogoproto.nullable) = false + ]; +} diff --git a/protocol/Makefile b/protocol/Makefile index b2f4f753a7..39b66779cd 100644 --- a/protocol/Makefile +++ b/protocol/Makefile @@ -35,10 +35,6 @@ ifeq ($(LEDGER_ENABLED),true) endif endif -# libsecp256k1_sdk is disabled by default as both ethereum/go-ethereum and cosmos/cosmos-sdk will define duplicate symbols -ifeq (secp,$(findstring secp,$(COSMOS_BUILD_OPTIONS))) - build_tags += libsecp256k1_sdk # Requires CGO_ENABLED=1 -endif ifeq (cleveldb,$(findstring cleveldb,$(COSMOS_BUILD_OPTIONS))) build_tags += gcc endif diff --git a/protocol/app/app.go b/protocol/app/app.go index c631ef93a5..3af306366b 100644 --- a/protocol/app/app.go +++ b/protocol/app/app.go @@ -89,10 +89,10 @@ 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" - "github.com/dydxprotocol/v4-chain/protocol/daemons/configs" "github.com/gorilla/mux" "github.com/rakyll/statik/fs" "github.com/spf13/cast" + "go.uber.org/zap" "google.golang.org/grpc" // App @@ -100,8 +100,10 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/app/flags" "github.com/dydxprotocol/v4-chain/protocol/app/middleware" "github.com/dydxprotocol/v4-chain/protocol/app/prepare" + "github.com/dydxprotocol/v4-chain/protocol/app/prepare/prices" "github.com/dydxprotocol/v4-chain/protocol/app/process" + "github.com/dydxprotocol/v4-chain/protocol/app/vote_extensions" "github.com/dydxprotocol/v4-chain/protocol/lib" "github.com/dydxprotocol/v4-chain/protocol/lib/metrics" timelib "github.com/dydxprotocol/v4-chain/protocol/lib/time" @@ -112,6 +114,7 @@ import ( // Daemons bridgeclient "github.com/dydxprotocol/v4-chain/protocol/daemons/bridge/client" + "github.com/dydxprotocol/v4-chain/protocol/daemons/configs" daemonflags "github.com/dydxprotocol/v4-chain/protocol/daemons/flags" liquidationclient "github.com/dydxprotocol/v4-chain/protocol/daemons/liquidation/client" metricsclient "github.com/dydxprotocol/v4-chain/protocol/daemons/metrics/client" @@ -204,9 +207,16 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/indexer/msgsender" // Slinky + slinkyproposals "github.com/skip-mev/slinky/abci/proposals" + "github.com/skip-mev/slinky/abci/strategies/aggregator" + compression "github.com/skip-mev/slinky/abci/strategies/codec" + "github.com/skip-mev/slinky/abci/strategies/currencypair" + "github.com/skip-mev/slinky/abci/ve" oracleconfig "github.com/skip-mev/slinky/oracle/config" + "github.com/skip-mev/slinky/pkg/math/voteweighted" oracleclient "github.com/skip-mev/slinky/service/clients/oracle" servicemetrics "github.com/skip-mev/slinky/service/metrics" + promserver "github.com/skip-mev/slinky/service/servers/prometheus" // Grpc Streaming streaming "github.com/dydxprotocol/v4-chain/protocol/streaming/grpc" @@ -326,11 +336,13 @@ type App struct { PriceFeedClient *pricefeedclient.Client LiquidationsClient *liquidationclient.Client BridgeClient *bridgeclient.Client + SlinkyClient *slinkyclient.Client DaemonHealthMonitor *daemonservertypes.HealthMonitor // Slinky - SlinkyClient *slinkyclient.Client + oraclePrometheusServer *promserver.PrometheusServer + oracleMetrics servicemetrics.Metrics } // assertAppPreconditions assert invariants required for an application to start. @@ -447,6 +459,7 @@ func New( return nil }, ) + app.initOracleMetrics(appOpts) app.ParamsKeeper = initParamsKeeper(appCodec, cdc, keys[paramstypes.StoreKey], tkeys[paramstypes.TStoreKey]) @@ -863,7 +876,6 @@ func New( appCodec, keys[pricesmoduletypes.StoreKey], indexPriceCache, - pricesmoduletypes.NewMarketToSmoothedPrices(pricesmoduletypes.SmoothedPriceTrackingBlockHistoryLength), timeProvider, app.IndexerEventManager, // set the governance and delaymsg module accounts as the authority for conducting upgrades @@ -993,9 +1005,8 @@ func New( clobFlags := clobflags.GetClobFlagValuesFromOptions(appOpts) logger.Info("Parsed CLOB flags", "Flags", clobFlags) - memClob := clobmodulememclob.NewMemClobPriceTimePriority( - app.IndexerEventManager.Enabled() || app.GrpcStreamingManager.Enabled(), - ) + memClob := clobmodulememclob.NewMemClobPriceTimePriority(app.IndexerEventManager.Enabled()) + memClob.SetGenerateOrderbookUpdates(app.GrpcStreamingManager.Enabled()) app.ClobKeeper = clobmodulekeeper.NewKeeper( appCodec, @@ -1021,8 +1032,7 @@ func New( app.GrpcStreamingManager, txConfig.TxDecoder(), clobFlags, - rate_limit.NewPanicRateLimiter[*clobmoduletypes.MsgPlaceOrder](), - rate_limit.NewPanicRateLimiter[*clobmoduletypes.MsgCancelOrder](), + rate_limit.NewPanicRateLimiter[sdk.Msg](), daemonLiquidationInfo, ) clobModule := clobmodule.NewAppModule( @@ -1069,6 +1079,10 @@ func New( app.VaultKeeper = *vaultmodulekeeper.NewKeeper( appCodec, keys[vaultmoduletypes.StoreKey], + app.ClobKeeper, + app.PerpetualsKeeper, + app.PricesKeeper, + app.SubaccountsKeeper, []string{ lib.GovModuleAddress.String(), delaymsgmoduletypes.ModuleAddress.String(), @@ -1355,48 +1369,10 @@ func New( app.SetPrecommiter(app.Precommitter) app.SetPrepareCheckStater(app.PrepareCheckStater) - // PrepareProposal setup. - if appFlags.NonValidatingFullNode { - app.SetPrepareProposal(prepare.FullNodePrepareProposalHandler()) - } else { - app.SetPrepareProposal( - prepare.PrepareProposalHandler( - txConfig, - app.BridgeKeeper, - app.ClobKeeper, - app.PricesKeeper, - app.PerpetualsKeeper, - ), - ) - } - - // ProcessProposal setup. - if appFlags.NonValidatingFullNode { - // Note: If the command-line flag `--non-validating-full-node` is enabled, this node will use - // an implementation of `ProcessProposal` which always returns `abci.ResponseProcessProposal_ACCEPT`. - // Full-nodes do not participate in consensus, and therefore should not participate in voting / `ProcessProposal`. - app.SetProcessProposal( - process.FullNodeProcessProposalHandler( - txConfig, - app.BridgeKeeper, - app.ClobKeeper, - app.StakingKeeper, - app.PerpetualsKeeper, - app.PricesKeeper, - ), - ) - } else { - app.SetProcessProposal( - process.ProcessProposalHandler( - txConfig, - app.BridgeKeeper, - app.ClobKeeper, - app.StakingKeeper, - app.PerpetualsKeeper, - app.PricesKeeper, - ), - ) - } + // ProposalHandler setup. + prepareProposalHandler, processProposalHandler := app.createProposalHandlers(appFlags, txConfig, appOpts) + app.SetPrepareProposal(prepareProposalHandler) + app.SetProcessProposal(processProposalHandler) // Note that panics from out of gas errors won't get logged, since the `OutOfGasMiddleware` is added in front of this, // so error will get handled by that middleware and subsequent middlewares won't get executed. @@ -1447,20 +1423,15 @@ func New( } func (app *App) initSlinkySidecarClient(appOpts servertypes.AppOptions) oracleclient.OracleClient { - // Slinky setup + // Create the oracle service. cfg, err := oracleconfig.ReadConfigFromAppOpts(appOpts) if err != nil { panic(err) } - oracleMetrics, err := servicemetrics.NewMetricsFromConfig(cfg, app.ChainID()) - if err != nil { - panic(err) - } - // Create the oracle service. slinkyClient, err := oracleclient.NewClientFromConfig( cfg, app.Logger().With("client", "oracle"), - oracleMetrics, + app.oracleMetrics, ) if err != nil { panic(err) @@ -1468,6 +1439,152 @@ func (app *App) initSlinkySidecarClient(appOpts servertypes.AppOptions) oraclecl return slinkyClient } +func (app *App) createProposalHandlers( + appFlags flags.Flags, + txConfig client.TxConfig, + appOpts servertypes.AppOptions, +) (sdk.PrepareProposalHandler, sdk.ProcessProposalHandler) { + var priceUpdateDecoder process.UpdateMarketPriceTxDecoder = process.NewDefaultUpdateMarketPriceTxDecoder( + app.PricesKeeper, app.txConfig.TxDecoder()) + // If the node is a NonValidatingFullNode, we don't need to run any oracle code + // Note: If the command-line flag `--non-validating-full-node` is enabled, this node will use + // an implementation of `ProcessProposal` which always returns `abci.ResponseProcessProposal_ACCEPT`. + // Full-nodes do not participate in consensus, and therefore should not participate in voting / `ProcessProposal`. + if appFlags.NonValidatingFullNode { + if app.oracleMetrics == nil { + app.oracleMetrics = servicemetrics.NewNopMetrics() + } + return prepare.FullNodePrepareProposalHandler(), process.FullNodeProcessProposalHandler( + txConfig, + app.BridgeKeeper, + app.ClobKeeper, + app.StakingKeeper, + app.PerpetualsKeeper, + priceUpdateDecoder, + ) + } + strategy := currencypair.NewDefaultCurrencyPairStrategy(app.PricesKeeper) + var priceUpdateGenerator prices.PriceUpdateGenerator = prices.NewDefaultPriceUpdateGenerator(app.PricesKeeper) + + veCodec := compression.NewCompressionVoteExtensionCodec( + compression.NewDefaultVoteExtensionCodec(), + compression.NewZLibCompressor(), + ) + extCommitCodec := compression.NewCompressionExtendedCommitCodec( + compression.NewDefaultExtendedCommitCodec(), + compression.NewZLibCompressor(), + ) + + // Set Price Update Generators/Decoders for Slinky + if appFlags.VEOracleEnabled { + priceUpdateGenerator = prices.NewSlinkyPriceUpdateGenerator( + aggregator.NewDefaultVoteAggregator( + app.Logger(), + voteweighted.MedianFromContext( + app.Logger(), + app.StakingKeeper, + voteweighted.DefaultPowerThreshold, + ), + strategy, + ), + extCommitCodec, + veCodec, + strategy, + ) + priceUpdateDecoder = process.NewSlinkyMarketPriceDecoder( + priceUpdateDecoder, + priceUpdateGenerator, + ) + } + // Generate the dydx handlers + dydxPrepareProposalHandler := prepare.PrepareProposalHandler( + txConfig, + app.BridgeKeeper, + app.ClobKeeper, + app.PerpetualsKeeper, + priceUpdateGenerator, + ) + + // ProcessProposal setup. + var dydxProcessProposalHandler = process.ProcessProposalHandler( + txConfig, + app.BridgeKeeper, + app.ClobKeeper, + app.StakingKeeper, + app.PerpetualsKeeper, + app.PricesKeeper, + priceUpdateDecoder, + ) + + // Wrap dydx handlers with slinky handlers + if appFlags.VEOracleEnabled { + app.initOracle(priceUpdateDecoder) + proposalHandler := slinkyproposals.NewProposalHandler( + app.Logger(), + dydxPrepareProposalHandler, + dydxProcessProposalHandler, + ve.NewDefaultValidateVoteExtensionsFn(app.ChainID(), app.StakingKeeper), + veCodec, + extCommitCodec, + strategy, + app.oracleMetrics, + slinkyproposals.RetainOracleDataInWrappedProposalHandler(), + ) + return proposalHandler.PrepareProposalHandler(), proposalHandler.ProcessProposalHandler() + } + return dydxPrepareProposalHandler, dydxProcessProposalHandler +} + +func (app *App) initOracle(pricesTxDecoder process.UpdateMarketPriceTxDecoder) { + // Vote Extension setup. + slinkyVoteExtensionsHandler := ve.NewVoteExtensionHandler( + app.Logger(), + vote_extensions.NewOraclePrices(app.PricesKeeper), + time.Second, + currencypair.NewDefaultCurrencyPairStrategy(app.PricesKeeper), + compression.NewCompressionVoteExtensionCodec( + compression.NewDefaultVoteExtensionCodec(), + compression.NewZLibCompressor(), + ), + app.PreBlocker, + app.oracleMetrics, + ) + + dydxExtendVoteHandler := vote_extensions.ExtendVoteHandler{ + SlinkyExtendVoteHandler: slinkyVoteExtensionsHandler.ExtendVoteHandler(), + PricesTxDecoder: pricesTxDecoder, + PricesKeeper: app.PricesKeeper, + } + + app.SetExtendVoteHandler(dydxExtendVoteHandler.ExtendVoteHandler()) + app.SetVerifyVoteExtensionHandler(slinkyVoteExtensionsHandler.VerifyVoteExtensionHandler()) +} + +func (app *App) initOracleMetrics(appOpts servertypes.AppOptions) { + cfg, err := oracleconfig.ReadConfigFromAppOpts(appOpts) + if err != nil { + panic(err) + } + oracleMetrics, err := servicemetrics.NewMetricsFromConfig(cfg, app.ChainID()) + if err != nil { + panic(err) + } + // run prometheus metrics + if cfg.MetricsEnabled { + promLogger, err := zap.NewProduction() + if err != nil { + panic(err) + } + app.oraclePrometheusServer, err = promserver.NewPrometheusServer(cfg.PrometheusServerAddress, promLogger) + if err != nil { + panic(err) + } + // start the prometheus server + go app.oraclePrometheusServer.Start() + } + app.oracleMetrics = oracleMetrics +} + // RegisterDaemonWithHealthMonitor registers a daemon service with the update monitor, which will commence monitoring // the health of the daemon. If the daemon does not register, the method will panic. func (app *App) RegisterDaemonWithHealthMonitor( @@ -1742,6 +1859,9 @@ func (app *App) setAnteHandler(txConfig client.TxConfig) { // Close invokes an ordered shutdown of routines. func (app *App) Close() error { app.BaseApp.Close() + if app.oraclePrometheusServer != nil { + app.oraclePrometheusServer.Close() + } return app.closeOnce() } diff --git a/protocol/app/app_test.go b/protocol/app/app_test.go index 1a55241e79..44748c78b5 100644 --- a/protocol/app/app_test.go +++ b/protocol/app/app_test.go @@ -106,6 +106,7 @@ func TestAppIsFullyInitialized(t *testing.T) { "LiquidationsClient", "BridgeClient", "SlinkyClient", + "oraclePrometheusServer", // Any default constructed type can be considered initialized if the default is what is // expected. getUninitializedStructFields relies on fields being the non-default and diff --git a/protocol/app/basic_manager/basic_manager.go b/protocol/app/basic_manager/basic_manager.go index 8368247bdf..f1cec344bc 100644 --- a/protocol/app/basic_manager/basic_manager.go +++ b/protocol/app/basic_manager/basic_manager.go @@ -36,6 +36,7 @@ import ( sendingmodule "github.com/dydxprotocol/v4-chain/protocol/x/sending" statsmodule "github.com/dydxprotocol/v4-chain/protocol/x/stats" subaccountsmodule "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts" + vaultmodule "github.com/dydxprotocol/v4-chain/protocol/x/vault" vestmodule "github.com/dydxprotocol/v4-chain/protocol/x/vest" ica "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts" @@ -91,5 +92,6 @@ var ( epochsmodule.AppModuleBasic{}, ratelimitmodule.AppModuleBasic{}, govplusmodule.AppModuleBasic{}, + vaultmodule.AppModuleBasic{}, ) ) diff --git a/protocol/app/constants/constants.go b/protocol/app/constants/constants.go index 52e30be6d6..09c6a5b6e7 100644 --- a/protocol/app/constants/constants.go +++ b/protocol/app/constants/constants.go @@ -5,3 +5,12 @@ const ( AppDaemonName = AppName + "d" ServiceName = "validator" ) + +// Slinky Constants + +const ( + // OracleInfoIndex is the index at which slinky will inject VE data + OracleInfoIndex = 0 + // OracleVEInjectedTxs is the number of transactions Slinky injects into the block (for VE data) + OracleVEInjectedTxs = 1 +) diff --git a/protocol/app/e2e/ante_test.go b/protocol/app/e2e/ante_test.go index 3cb7ec9fa7..815952064d 100644 --- a/protocol/app/e2e/ante_test.go +++ b/protocol/app/e2e/ante_test.go @@ -137,7 +137,7 @@ func TestParallelAnteHandler_ClobAndOther(t *testing.T) { }, }, constants.TestFeeCoins_5Cents, - 100_000, + 110_000, ctx.ChainID(), []uint64{account.GetAccountNumber()}, []uint64{sequenceNumber}, diff --git a/protocol/app/flags/flags.go b/protocol/app/flags/flags.go index 18fa78a19b..0be74ac6d8 100644 --- a/protocol/app/flags/flags.go +++ b/protocol/app/flags/flags.go @@ -22,6 +22,7 @@ type Flags struct { // Grpc Streaming GrpcStreamingEnabled bool + VEOracleEnabled bool // Slinky Vote Extensions } // List of CLI flags. @@ -37,6 +38,9 @@ const ( // Grpc Streaming GrpcStreamingEnabled = "grpc-streaming-enabled" + + // Slinky VEs enabled + VEOracleEnabled = "slinky-vote-extension-oracle-enabled" ) // Default values. @@ -47,6 +51,7 @@ const ( DefaultDdErrorTrackingFormat = false DefaultGrpcStreamingEnabled = false + DefaultVEOracleEnabled = true ) // AddFlagsToCmd adds flags to app initialization. @@ -80,6 +85,11 @@ func AddFlagsToCmd(cmd *cobra.Command) { DefaultGrpcStreamingEnabled, "Whether to enable grpc streaming for full nodes", ) + cmd.Flags().Bool( + VEOracleEnabled, + DefaultVEOracleEnabled, + "Whether to run on-chain oracle via slinky vote extensions", + ) } // Validate checks that the flags are valid. @@ -119,6 +129,7 @@ func GetFlagValuesFromOptions( GrpcEnable: true, GrpcStreamingEnabled: DefaultGrpcStreamingEnabled, + VEOracleEnabled: true, } // Populate the flags if they exist. @@ -164,5 +175,11 @@ func GetFlagValuesFromOptions( } } + if option := appOpts.Get(VEOracleEnabled); option != nil { + if v, err := cast.ToBoolE(option); err == nil { + result.VEOracleEnabled = v + } + } + return result } diff --git a/protocol/app/msgs/all_msgs.go b/protocol/app/msgs/all_msgs.go index 15abfff329..dc4715319f 100644 --- a/protocol/app/msgs/all_msgs.go +++ b/protocol/app/msgs/all_msgs.go @@ -234,6 +234,10 @@ var ( "/dydxprotocol.stats.MsgUpdateParams": {}, "/dydxprotocol.stats.MsgUpdateParamsResponse": {}, + // vault + "/dydxprotocol.vault.MsgDepositToVault": {}, + "/dydxprotocol.vault.MsgDepositToVaultResponse": {}, + // vest "/dydxprotocol.vest.MsgSetVestEntry": {}, "/dydxprotocol.vest.MsgSetVestEntryResponse": {}, diff --git a/protocol/app/msgs/normal_msgs.go b/protocol/app/msgs/normal_msgs.go index 8bc4818191..7808cf1c8e 100644 --- a/protocol/app/msgs/normal_msgs.go +++ b/protocol/app/msgs/normal_msgs.go @@ -19,6 +19,7 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/lib" clob "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" sending "github.com/dydxprotocol/v4-chain/protocol/x/sending/types" + vault "github.com/dydxprotocol/v4-chain/protocol/x/vault/types" ) var ( @@ -233,5 +234,9 @@ var ( "/dydxprotocol.sending.MsgDepositToSubaccountResponse": nil, "/dydxprotocol.sending.MsgWithdrawFromSubaccount": &sending.MsgWithdrawFromSubaccount{}, "/dydxprotocol.sending.MsgWithdrawFromSubaccountResponse": nil, + + // vault + "/dydxprotocol.vault.MsgDepositToVault": &vault.MsgDepositToVault{}, + "/dydxprotocol.vault.MsgDepositToVaultResponse": nil, } ) diff --git a/protocol/app/msgs/normal_msgs_test.go b/protocol/app/msgs/normal_msgs_test.go index 6eb1c3aec2..c5bba587c3 100644 --- a/protocol/app/msgs/normal_msgs_test.go +++ b/protocol/app/msgs/normal_msgs_test.go @@ -138,6 +138,10 @@ func TestNormalMsgs_Key(t *testing.T) { "/dydxprotocol.sending.MsgWithdrawFromSubaccount", "/dydxprotocol.sending.MsgWithdrawFromSubaccountResponse", + // vault + "/dydxprotocol.vault.MsgDepositToVault", + "/dydxprotocol.vault.MsgDepositToVaultResponse", + // ibc application module: ICA "/ibc.applications.interchain_accounts.v1.InterchainAccount", diff --git a/protocol/app/prepare/expected_keepers.go b/protocol/app/prepare/expected_keepers.go index f360a05520..608121eca5 100644 --- a/protocol/app/prepare/expected_keepers.go +++ b/protocol/app/prepare/expected_keepers.go @@ -7,7 +7,6 @@ import ( bridgetypes "github.com/dydxprotocol/v4-chain/protocol/x/bridge/types" clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" perpstypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" - pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" ) // PrepareClobKeeper defines the expected CLOB keeper used for `PrepareProposal`. @@ -20,11 +19,6 @@ type PreparePerpetualsKeeper interface { GetAddPremiumVotes(ctx sdk.Context) *perpstypes.MsgAddPremiumVotes } -// PreparePricesKeeper defines the expected Prices keeper used for `PrepareProposal`. -type PreparePricesKeeper interface { - GetValidMarketPriceUpdates(ctx sdk.Context) *pricestypes.MsgUpdateMarketPrices -} - // PrepareBridgeKeeper defines the expected Bridge keeper used for `PrepareProposal`. type PrepareBridgeKeeper interface { GetAcknowledgeBridges(ctx sdk.Context, blockTimestamp time.Time) *bridgetypes.MsgAcknowledgeBridges diff --git a/protocol/app/prepare/full_node_prepare_proposal.go b/protocol/app/prepare/full_node_prepare_proposal.go index 34fb8d6107..a394ffdcfc 100644 --- a/protocol/app/prepare/full_node_prepare_proposal.go +++ b/protocol/app/prepare/full_node_prepare_proposal.go @@ -20,6 +20,6 @@ func FullNodePrepareProposalHandler() sdk.PrepareProposalHandler { recordErrorMetricsWithLabel(metrics.PrepareProposalTxs) // Return an empty response if the node is running in full-node mode so that the proposal fails. - return &EmptyResponse, nil + return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil } } diff --git a/protocol/app/prepare/prepare_proposal.go b/protocol/app/prepare/prepare_proposal.go index ca376f45f2..ad7d3b3e59 100644 --- a/protocol/app/prepare/prepare_proposal.go +++ b/protocol/app/prepare/prepare_proposal.go @@ -10,7 +10,10 @@ import ( "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dydxprotocol/v4-chain/protocol/app/constants" + "github.com/dydxprotocol/v4-chain/protocol/app/prepare/prices" "github.com/dydxprotocol/v4-chain/protocol/lib/metrics" + pricetypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" ) var ( @@ -52,8 +55,8 @@ func PrepareProposalHandler( txConfig client.TxConfig, bridgeKeeper PrepareBridgeKeeper, clobKeeper PrepareClobKeeper, - pricesKeeper PreparePricesKeeper, perpetualKeeper PreparePerpetualsKeeper, + priceUpdateGenerator prices.PriceUpdateGenerator, ) sdk.PrepareProposalHandler { return func(ctx sdk.Context, req *abci.RequestPrepareProposal) (*abci.ResponsePrepareProposal, error) { defer telemetry.ModuleMeasureSince( @@ -71,38 +74,53 @@ func PrepareProposalHandler( return &EmptyResponse, nil } + // Grab the injected VEs from the previous block. + // If VEs are not enabled, no tx will have been injected. + var extCommitBzTx []byte + if len(req.Txs) >= constants.OracleVEInjectedTxs { + extCommitBzTx = req.Txs[constants.OracleInfoIndex] + } + + // get the update market prices tx + msg, err := priceUpdateGenerator.GetValidMarketPriceUpdates(ctx, extCommitBzTx) + if err != nil { + ctx.Logger().Error(fmt.Sprintf("GetValidMarketPriceUpdates error: %v", err)) + recordErrorMetricsWithLabel(metrics.PricesTx) + return &EmptyResponse, nil + } + // Gather "FixedSize" group messages. - pricesTxResp, err := GetUpdateMarketPricesTx(ctx, txConfig, pricesKeeper) + pricesTxResp, err := EncodeMarketPriceUpdates(txConfig, msg) if err != nil { ctx.Logger().Error(fmt.Sprintf("GetUpdateMarketPricesTx error: %v", err)) recordErrorMetricsWithLabel(metrics.PricesTx) - return &EmptyResponse, nil + return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil } err = txs.SetUpdateMarketPricesTx(pricesTxResp.Tx) if err != nil { ctx.Logger().Error(fmt.Sprintf("SetUpdateMarketPricesTx error: %v", err)) recordErrorMetricsWithLabel(metrics.PricesTx) - return &EmptyResponse, nil + return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil } fundingTxResp, err := GetAddPremiumVotesTx(ctx, txConfig, perpetualKeeper) if err != nil { ctx.Logger().Error(fmt.Sprintf("GetAddPremiumVotesTx error: %v", err)) recordErrorMetricsWithLabel(metrics.FundingTx) - return &EmptyResponse, nil + return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil } err = txs.SetAddPremiumVotesTx(fundingTxResp.Tx) if err != nil { ctx.Logger().Error(fmt.Sprintf("SetAddPremiumVotesTx error: %v", err)) recordErrorMetricsWithLabel(metrics.FundingTx) - return &EmptyResponse, nil + return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil } acknowledgeBridgesTxResp, err := GetAcknowledgeBridgesTx(ctx, txConfig, bridgeKeeper) if err != nil { ctx.Logger().Error(fmt.Sprintf("GetAcknowledgeBridgesTx error: %v", err)) recordErrorMetricsWithLabel(metrics.AcknowledgeBridgesTx) - return &EmptyResponse, nil + return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil } // Set AcknowledgeBridgesTx whether there are bridge events or not to ensure // consistent ordering of txs received by ProcessProposal. @@ -110,7 +128,7 @@ func PrepareProposalHandler( if err != nil { ctx.Logger().Error(fmt.Sprintf("SetAcknowledgeBridgesTx error: %v", err)) recordErrorMetricsWithLabel(metrics.AcknowledgeBridgesTx) - return &EmptyResponse, nil + return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil } // Gather "Other" group messages. @@ -123,7 +141,7 @@ func PrepareProposalHandler( if err != nil { ctx.Logger().Error(fmt.Sprintf("AddOtherTxs error: %v", err)) recordErrorMetricsWithLabel(metrics.OtherTxs) - return &EmptyResponse, nil + return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil } } @@ -133,13 +151,13 @@ func PrepareProposalHandler( if err != nil { ctx.Logger().Error(fmt.Sprintf("GetProposedOperationsTx error: %v", err)) recordErrorMetricsWithLabel(metrics.OperationsTx) - return &EmptyResponse, nil + return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil } err = txs.SetProposedOperationsTx(operationsTxResp.Tx) if err != nil { ctx.Logger().Error(fmt.Sprintf("SetProposedOperationsTx error: %v", err)) recordErrorMetricsWithLabel(metrics.OperationsTx) - return &EmptyResponse, nil + return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil } // Try to pack in more "Other" txs. @@ -151,7 +169,7 @@ func PrepareProposalHandler( if err != nil { ctx.Logger().Error(fmt.Sprintf("AddOtherTxs (additional) error: %v", err)) recordErrorMetricsWithLabel(metrics.OtherTxs) - return &EmptyResponse, nil + return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil } } } @@ -160,7 +178,7 @@ func PrepareProposalHandler( if err != nil { ctx.Logger().Error(fmt.Sprintf("GetTxsInOrder error: %v", err)) recordErrorMetricsWithLabel(metrics.GetTxsInOrder) - return &EmptyResponse, nil + return &abci.ResponsePrepareProposal{Txs: [][]byte{}}, nil } // Record a success metric. @@ -180,19 +198,12 @@ func PrepareProposalHandler( } } -// GetUpdateMarketPricesTx returns a tx containing `MsgUpdateMarketPrices`. -func GetUpdateMarketPricesTx( - ctx sdk.Context, +// EncodeMarketPriceUpdates returns a tx containing `MsgUpdateMarketPrices`. +func EncodeMarketPriceUpdates( txConfig client.TxConfig, - pricesKeeper PreparePricesKeeper, + msg *pricetypes.MsgUpdateMarketPrices, ) (PricesTxResponse, error) { - // Get prices to update. - msgUpdateMarketPrices := pricesKeeper.GetValidMarketPriceUpdates(ctx) - if msgUpdateMarketPrices == nil { - return PricesTxResponse{}, fmt.Errorf("MsgUpdateMarketPrices cannot be nil") - } - - tx, err := EncodeMsgsIntoTxBytes(txConfig, msgUpdateMarketPrices) + tx, err := EncodeMsgsIntoTxBytes(txConfig, msg) if err != nil { return PricesTxResponse{}, err } @@ -202,7 +213,7 @@ func GetUpdateMarketPricesTx( return PricesTxResponse{ Tx: tx, - NumMarkets: len(msgUpdateMarketPrices.MarketPriceUpdates), + NumMarkets: len(msg.MarketPriceUpdates), }, nil } diff --git a/protocol/app/prepare/prepare_proposal_test.go b/protocol/app/prepare/prepare_proposal_test.go index c82b1597df..19536bc5af 100644 --- a/protocol/app/prepare/prepare_proposal_test.go +++ b/protocol/app/prepare/prepare_proposal_test.go @@ -5,9 +5,12 @@ import ( "fmt" "testing" + "cosmossdk.io/log" abci "github.com/cometbft/cometbft/abci/types" - sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/client" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/dydxprotocol/v4-chain/protocol/app/prepare" + "github.com/dydxprotocol/v4-chain/protocol/app/prepare/prices" "github.com/dydxprotocol/v4-chain/protocol/mocks" "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" "github.com/dydxprotocol/v4-chain/protocol/testutil/encoding" @@ -16,26 +19,34 @@ import ( clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" perpetualtypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" + "github.com/skip-mev/slinky/abci/strategies/aggregator" + aggregatormock "github.com/skip-mev/slinky/abci/strategies/aggregator/mocks" + "github.com/skip-mev/slinky/abci/strategies/codec" + strategymock "github.com/skip-mev/slinky/abci/strategies/currencypair/mocks" + slinkytestutils "github.com/skip-mev/slinky/abci/testutils" + vetypes "github.com/skip-mev/slinky/abci/ve/types" + oracletypes "github.com/skip-mev/slinky/pkg/types" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" + "math/big" ) var ( - ctx = sdktypes.Context{} + ctx = sdk.Context{} - failingTxEncoder = func(tx sdktypes.Tx) ([]byte, error) { + failingTxEncoder = func(tx sdk.Tx) ([]byte, error) { return nil, errors.New("encoder failed") } - emptyTxEncoder = func(tx sdktypes.Tx) ([]byte, error) { + emptyTxEncoder = func(tx sdk.Tx) ([]byte, error) { return []byte{}, nil } - passingTxEncoderOne = func(tx sdktypes.Tx) ([]byte, error) { + passingTxEncoderOne = func(tx sdk.Tx) ([]byte, error) { return []byte{1}, nil } - passingTxEncoderTwo = func(tx sdktypes.Tx) ([]byte, error) { + passingTxEncoderTwo = func(tx sdk.Tx) ([]byte, error) { return []byte{1, 2}, nil } - passingTxEncoderFour = func(tx sdktypes.Tx) ([]byte, error) { + passingTxEncoderFour = func(tx sdk.Tx) ([]byte, error) { return []byte{1, 2, 3, 4}, nil } ) @@ -49,16 +60,16 @@ func TestPrepareProposalHandler(t *testing.T) { maxBytes int64 pricesResp *pricestypes.MsgUpdateMarketPrices - pricesEncoder sdktypes.TxEncoder + pricesEncoder sdk.TxEncoder fundingResp *perpetualtypes.MsgAddPremiumVotes - fundingEncoder sdktypes.TxEncoder + fundingEncoder sdk.TxEncoder clobResp *clobtypes.MsgProposedOperations - clobEncoder sdktypes.TxEncoder + clobEncoder sdk.TxEncoder bridgeResp *bridgetypes.MsgAcknowledgeBridges - bridgeEncoder sdktypes.TxEncoder + bridgeEncoder sdk.TxEncoder expectedTxs [][]byte }{ @@ -325,7 +336,7 @@ func TestPrepareProposalHandler(t *testing.T) { t.Run(name, func(t *testing.T) { mockTxConfig := createMockTxConfig( nil, - []sdktypes.TxEncoder{ + []sdk.TxEncoder{ tc.pricesEncoder, tc.fundingEncoder, tc.bridgeEncoder, @@ -349,14 +360,14 @@ func TestPrepareProposalHandler(t *testing.T) { mockClobKeeper.On("GetOperations", mock.Anything, mock.Anything). Return(tc.clobResp) - ctx, _, _, _, _, _ := keepertest.PricesKeepers(t) + ctx, _, _, _, _ := keepertest.PricesKeepers(t) handler := prepare.PrepareProposalHandler( mockTxConfig, &mockBridgeKeeper, &mockClobKeeper, - &mockPricesKeeper, &mockPerpKeeper, + prices.NewDefaultPriceUpdateGenerator(&mockPricesKeeper), ) req := abci.RequestPrepareProposal{ @@ -429,14 +440,14 @@ func TestPrepareProposalHandler_OtherTxs(t *testing.T) { mockBridgeKeeper.On("GetAcknowledgeBridges", mock.Anything, mock.Anything). Return(constants.MsgAcknowledgeBridges_Ids0_1_Height0) - ctx, _, _, _, _, _ := keepertest.PricesKeepers(t) + ctx, _, _, _, _ := keepertest.PricesKeepers(t) handler := prepare.PrepareProposalHandler( encodingCfg.TxConfig, &mockBridgeKeeper, &mockClobKeeper, - &mockPricesKeeper, &mockPerpKeeper, + prices.NewDefaultPriceUpdateGenerator(&mockPricesKeeper), ) req := abci.RequestPrepareProposal{ @@ -451,10 +462,189 @@ func TestPrepareProposalHandler_OtherTxs(t *testing.T) { } } +func TestSlinkyPrepareProposalHandler(t *testing.T) { + // test an empty UpdateMarketPrices tx is inserted if ves are not enabled + t.Run("ves not enabled", func(t *testing.T) { + // mocks + mockPerpKeeper := mocks.PreparePerpetualsKeeper{} + mockPerpKeeper.On("GetAddPremiumVotes", mock.Anything). + Return(constants.ValidMsgAddPremiumVotes) + + mockClobKeeper := mocks.PrepareClobKeeper{} + mockClobKeeper.On("GetOperations", mock.Anything, mock.Anything). + Return(constants.ValidEmptyMsgProposedOperations) + + mockBridgeKeeper := mocks.PrepareBridgeKeeper{} + mockBridgeKeeper.On("GetAcknowledgeBridges", mock.Anything, mock.Anything). + Return(constants.MsgAcknowledgeBridges_Ids0_1_Height0) + + ctx := slinkytestutils.CreateBaseSDKContext(t) + ctx = slinkytestutils.UpdateContextWithVEHeight(ctx, 3) + ctx = ctx.WithBlockHeight(2) // disable vote-extensions + + gen := prices.NewSlinkyPriceUpdateGenerator(nil, nil, nil, nil) // ignore all fields, should immediately return + + handler := prepare.PrepareProposalHandler( + encoding.GetTestEncodingCfg().TxConfig, + &mockBridgeKeeper, + &mockClobKeeper, + &mockPerpKeeper, + gen, + ) + + txs := [][]byte{} + + resp, err := handler(ctx, &abci.RequestPrepareProposal{Txs: txs, MaxTxBytes: 100_000}) + require.NoError(t, err) + + // expect all txs to have been inserted + // check that the last tx is a valid update-market-prices tx + marketPricesTx, err := encoding.GetTestEncodingCfg().TxConfig.TxDecoder()(resp.Txs[len(resp.Txs)-1]) + require.NoError(t, err) + require.Len(t, marketPricesTx.GetMsgs(), 1) + + // expect the message to be an UpdateMarketPrices message w/ no markets + updateMarketPricesMsg := marketPricesTx.GetMsgs()[0].(*pricestypes.MsgUpdateMarketPrices) + require.Len(t, updateMarketPricesMsg.MarketPriceUpdates, 0) + }) + + // test that a valid UpdateMarketPricesTx is inserted if ves are enabled, and a valid ExtendedCommitInfo is present + t.Run("ves enabled", func(t *testing.T) { + // mocks + mockPerpKeeper := mocks.PreparePerpetualsKeeper{} + mockPerpKeeper.On("GetAddPremiumVotes", mock.Anything). + Return(constants.ValidMsgAddPremiumVotes) + + mockClobKeeper := mocks.PrepareClobKeeper{} + mockClobKeeper.On("GetOperations", mock.Anything, mock.Anything). + Return(constants.ValidEmptyMsgProposedOperations) + + mockBridgeKeeper := mocks.PrepareBridgeKeeper{} + mockBridgeKeeper.On("GetAcknowledgeBridges", mock.Anything, mock.Anything). + Return(constants.MsgAcknowledgeBridges_Ids0_1_Height0) + + ctx := slinkytestutils.CreateBaseSDKContext(t) + ctx = slinkytestutils.UpdateContextWithVEHeight(ctx, 3) + ctx = ctx.WithBlockHeight(4) // enable vote-extensions + ctx = ctx.WithLogger(log.NewTestLogger(t)) + + cpMock := strategymock.NewCurrencyPairStrategy(t) + aggMock := aggregatormock.NewVoteAggregator(t) + extCommitCodec := codec.NewDefaultExtendedCommitCodec() + veCodec := codec.NewDefaultVoteExtensionCodec() + gen := prices.NewSlinkyPriceUpdateGenerator( + aggMock, + extCommitCodec, + veCodec, + cpMock, + ) + + // mock dependencies + validator1 := []byte("validator1") + validator2 := []byte("validator2") + + validator1ve := vetypes.OracleVoteExtension{ + Prices: map[uint64][]byte{ + 0: []byte("99"), + 1: []byte("100"), + }, + } + validator2ve := vetypes.OracleVoteExtension{ + Prices: map[uint64][]byte{ + 0: []byte("99"), + 1: []byte("100"), + }, + } + + validator1veBz, err := veCodec.Encode(validator1ve) + require.NoError(t, err) + + validator2veBz, err := veCodec.Encode(validator2ve) + require.NoError(t, err) + + // setup extendedCommit + extCommit := abci.ExtendedCommitInfo{ + Votes: []abci.ExtendedVoteInfo{ + { + Validator: abci.Validator{ + Address: validator1, + }, + VoteExtension: validator1veBz, + }, + { + Validator: abci.Validator{ + Address: validator2, + }, + VoteExtension: validator2veBz, + }, + }, + } + extCommitBz, err := extCommitCodec.Encode(extCommit) + require.NoError(t, err) + + mogBtc := oracletypes.NewCurrencyPair("MOG", "BTC") + tiaPepe := oracletypes.NewCurrencyPair("TIA", "PEPE") + + aggMock.On("AggregateOracleVotes", ctx, []aggregator.Vote{ + { + ConsAddress: validator1, + OracleVoteExtension: validator1ve, + }, + { + ConsAddress: validator2, + OracleVoteExtension: validator2ve, + }, + }).Return(map[oracletypes.CurrencyPair]*big.Int{ + mogBtc: big.NewInt(100), + tiaPepe: big.NewInt(99), + }, nil) + + cpMock.On("ID", ctx, mogBtc).Return(uint64(0), nil) + cpMock.On("ID", ctx, tiaPepe).Return(uint64(1), nil) + + handler := prepare.PrepareProposalHandler( + encoding.GetTestEncodingCfg().TxConfig, + &mockBridgeKeeper, + &mockClobKeeper, + &mockPerpKeeper, + gen, + ) + + txs := [][]byte{ + extCommitBz, // extended commit should be first + constants.Msg_Send_TxBytes, + } + + resp, err := handler(ctx, &abci.RequestPrepareProposal{Txs: txs, MaxTxBytes: 100_000}) + require.NoError(t, err) + + // expect all txs to have been inserted + // check that the last tx is a valid update-market-prices tx + marketPricesTx, err := encoding.GetTestEncodingCfg().TxConfig.TxDecoder()(resp.Txs[len(resp.Txs)-1]) + require.NoError(t, err) + require.Len(t, marketPricesTx.GetMsgs(), 1) + + // expect the message to be an UpdateMarketPrices message w/ no markets + updateMarketPricesMsg := marketPricesTx.GetMsgs()[0].(*pricestypes.MsgUpdateMarketPrices) + require.Len(t, updateMarketPricesMsg.MarketPriceUpdates, 2) + + expectedPrices := map[uint64]uint64{ + 0: 100, + 1: 99, + } + + for _, update := range updateMarketPricesMsg.MarketPriceUpdates { + expectedPrice, ok := expectedPrices[uint64(update.MarketId)] + require.True(t, ok) + require.Equal(t, expectedPrice, update.Price) + } + }) +} + func TestGetUpdateMarketPricesTx(t *testing.T) { tests := map[string]struct { keeperResp *pricestypes.MsgUpdateMarketPrices - txEncoder sdktypes.TxEncoder + txEncoder sdk.TxEncoder expectedTx []byte expectedNumMarkets int @@ -496,12 +686,13 @@ func TestGetUpdateMarketPricesTx(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - mockTxConfig := createMockTxConfig(nil, []sdktypes.TxEncoder{tc.txEncoder}) + mockTxConfig := createMockTxConfig(nil, []sdk.TxEncoder{tc.txEncoder}) mockPricesKeeper := mocks.PreparePricesKeeper{} mockPricesKeeper.On("GetValidMarketPriceUpdates", mock.Anything). Return(tc.keeperResp) - resp, err := prepare.GetUpdateMarketPricesTx(ctx, mockTxConfig, &mockPricesKeeper) + resp, err := getMarketPriceUpdates(prices.NewDefaultPriceUpdateGenerator(&mockPricesKeeper), mockTxConfig) + if tc.expectedErr != nil { require.Equal(t, err, tc.expectedErr) } else { @@ -513,10 +704,20 @@ func TestGetUpdateMarketPricesTx(t *testing.T) { } } +func getMarketPriceUpdates( + gen prices.PriceUpdateGenerator, txConfig client.TxConfig) (prepare.PricesTxResponse, error) { + msg, err := gen.GetValidMarketPriceUpdates(sdk.Context{}, nil) + if err != nil { + return prepare.PricesTxResponse{}, err + } + + return prepare.EncodeMarketPriceUpdates(txConfig, msg) +} + func TestGetAcknowledgeBridgesTx(t *testing.T) { tests := map[string]struct { keeperResp *bridgetypes.MsgAcknowledgeBridges - txEncoder sdktypes.TxEncoder + txEncoder sdk.TxEncoder expectedTx []byte expectedNumBridges int @@ -558,7 +759,7 @@ func TestGetAcknowledgeBridgesTx(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - mockTxConfig := createMockTxConfig(nil, []sdktypes.TxEncoder{tc.txEncoder}) + mockTxConfig := createMockTxConfig(nil, []sdk.TxEncoder{tc.txEncoder}) mockBridgeKeeper := mocks.PrepareBridgeKeeper{} mockBridgeKeeper.On("GetAcknowledgeBridges", mock.Anything, mock.Anything). Return(tc.keeperResp) @@ -578,7 +779,7 @@ func TestGetAcknowledgeBridgesTx(t *testing.T) { func TestGetAddPremiumVotesTx(t *testing.T) { tests := map[string]struct { keeperResp *perpetualtypes.MsgAddPremiumVotes - txEncoder sdktypes.TxEncoder + txEncoder sdk.TxEncoder expectedTx []byte expectedNumVotes int @@ -620,7 +821,7 @@ func TestGetAddPremiumVotesTx(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - mockTxConfig := createMockTxConfig(nil, []sdktypes.TxEncoder{tc.txEncoder}) + mockTxConfig := createMockTxConfig(nil, []sdk.TxEncoder{tc.txEncoder}) mockPerpKeeper := mocks.PreparePerpetualsKeeper{} mockPerpKeeper.On("GetAddPremiumVotes", mock.Anything). Return(tc.keeperResp) @@ -640,7 +841,7 @@ func TestGetAddPremiumVotesTx(t *testing.T) { func TestGetProposedOperationsTx(t *testing.T) { tests := map[string]struct { keeperResp *clobtypes.MsgProposedOperations - txEncoder sdktypes.TxEncoder + txEncoder sdk.TxEncoder expectedTx []byte expectedNumPlaceOrders int @@ -685,7 +886,7 @@ func TestGetProposedOperationsTx(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - mockTxConfig := createMockTxConfig(nil, []sdktypes.TxEncoder{tc.txEncoder}) + mockTxConfig := createMockTxConfig(nil, []sdk.TxEncoder{tc.txEncoder}) mockClobKeeper := mocks.PrepareClobKeeper{} mockClobKeeper.On("GetOperations", mock.Anything, mock.Anything).Return(tc.keeperResp) @@ -703,7 +904,7 @@ func TestGetProposedOperationsTx(t *testing.T) { func TestEncodeMsgsIntoTxBytes(t *testing.T) { tests := map[string]struct { setMsgErr error - txEncoder sdktypes.TxEncoder + txEncoder sdk.TxEncoder expectedTx []byte expectedErr error @@ -725,7 +926,7 @@ func TestEncodeMsgsIntoTxBytes(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - mockTxConfig := createMockTxConfig(tc.setMsgErr, []sdktypes.TxEncoder{tc.txEncoder}) + mockTxConfig := createMockTxConfig(tc.setMsgErr, []sdk.TxEncoder{tc.txEncoder}) tx, err := prepare.EncodeMsgsIntoTxBytes(mockTxConfig, &clobtypes.MsgProposedOperations{}) @@ -739,7 +940,7 @@ func TestEncodeMsgsIntoTxBytes(t *testing.T) { } } -func createMockTxConfig(setMsgsError error, allTxEncoders []sdktypes.TxEncoder) *mocks.TxConfig { +func createMockTxConfig(setMsgsError error, allTxEncoders []sdk.TxEncoder) *mocks.TxConfig { mockTxConfig := mocks.TxConfig{} mockTxBuilder := mocks.TxBuilder{} diff --git a/protocol/app/prepare/prices/default_price_update_generator.go b/protocol/app/prepare/prices/default_price_update_generator.go new file mode 100644 index 0000000000..c91443a5ed --- /dev/null +++ b/protocol/app/prepare/prices/default_price_update_generator.go @@ -0,0 +1,37 @@ +package prices + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" +) + +// PricesKeeper defines the expected Prices keeper used for `PrepareProposal`. +type PricesKeeper interface { + GetValidMarketPriceUpdates(ctx sdk.Context) *pricestypes.MsgUpdateMarketPrices +} + +// DefaultPriceUpdateGenerator is the default implementation of the PriceUpdateGenerator interface. +// This implementation retrieves the `MsgUpdateMarketPrices` from the `PricesKeeper`, i.e the default +// behavior for dydx v4's PrepareProposalHandler. +type DefaultPriceUpdateGenerator struct { + // pk is an adapter for the + pk PricesKeeper +} + +// NewDefaultPriceUpdateGenerator returns a new DefaultPriceUpdateGenerator. +func NewDefaultPriceUpdateGenerator(keeper PricesKeeper) *DefaultPriceUpdateGenerator { + return &DefaultPriceUpdateGenerator{ + pk: keeper, + } +} + +func (pug *DefaultPriceUpdateGenerator) GetValidMarketPriceUpdates( + ctx sdk.Context, _ []byte) (*pricestypes.MsgUpdateMarketPrices, error) { + msgUpdateMarketPrices := pug.pk.GetValidMarketPriceUpdates(ctx) + if msgUpdateMarketPrices == nil { + return nil, fmt.Errorf("MsgUpdateMarketPrices cannot be nil") + } + return msgUpdateMarketPrices, nil +} diff --git a/protocol/app/prepare/prices/errors.go b/protocol/app/prepare/prices/errors.go new file mode 100644 index 0000000000..c4c5fd0757 --- /dev/null +++ b/protocol/app/prepare/prices/errors.go @@ -0,0 +1,17 @@ +package prices + +import ( + "fmt" +) + +// InvalidPriceError represents an error thrown when a price retrieved from a vote-extension is invalid. +// - MarketID: the market-id of the market with the invalid price +// - Reason: the reason the price is invalid +type InvalidPriceError struct { + MarketID uint64 + Reason string +} + +func (e *InvalidPriceError) Error() string { + return fmt.Sprintf("invalid price for market %d: %s", e.MarketID, e.Reason) +} diff --git a/protocol/app/prepare/prices/price_update_generator.go b/protocol/app/prepare/prices/price_update_generator.go new file mode 100644 index 0000000000..f2f90f09c0 --- /dev/null +++ b/protocol/app/prepare/prices/price_update_generator.go @@ -0,0 +1,12 @@ +package prices + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" +) + +// PriceUpdateGenerator is an interface to abstract the logic of retrieving a +// `MsgUpdateMarketPrices` for any block. +type PriceUpdateGenerator interface { + GetValidMarketPriceUpdates(ctx sdk.Context, extCommitBz []byte) (*pricestypes.MsgUpdateMarketPrices, error) +} diff --git a/protocol/app/prepare/prices/slinky_price_update_generator.go b/protocol/app/prepare/prices/slinky_price_update_generator.go new file mode 100644 index 0000000000..4754d836c7 --- /dev/null +++ b/protocol/app/prepare/prices/slinky_price_update_generator.go @@ -0,0 +1,105 @@ +package prices + +import ( + "sort" + + sdk "github.com/cosmos/cosmos-sdk/types" + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" + "github.com/skip-mev/slinky/abci/strategies/aggregator" + "github.com/skip-mev/slinky/abci/strategies/codec" + "github.com/skip-mev/slinky/abci/strategies/currencypair" + "github.com/skip-mev/slinky/abci/ve" +) + +// SlinkyPriceUpdateGenerator is an implementation of the PriceUpdateGenerator interface. This implementation +// retrieves the MsgUpdateMarketPricesTx by aggregating over all VoteExtensions from set of PreCommits on +// the last block (these commits are local to the proposer). +type SlinkyPriceUpdateGenerator struct { + // VoteAggregator is responsible for reading all votes in the extended-commit and unmarshalling + // them into a set of prices for the MsgUpdateMarketPricesTx. + agg aggregator.VoteAggregator + + // extCommitCodec is responsible for unmarshalling the extended-commit from the proposal. + extCommitCodec codec.ExtendedCommitCodec + + // veCodec is responsible for unmarshalling each vote-extension from the extended-commit + veCodec codec.VoteExtensionCodec + + // currencyPairStrategy is responsible for mapping the currency-pairs to market-ids in the MsgUpdatemarketPricesTx + currencyPairStrategy currencypair.CurrencyPairStrategy +} + +// NewSlinkyPriceUpdateGenerator returns a new SlinkyPriceUpdateGenerator +func NewSlinkyPriceUpdateGenerator( + agg aggregator.VoteAggregator, + extCommitCodec codec.ExtendedCommitCodec, + veCodec codec.VoteExtensionCodec, + currencyPairStrategy currencypair.CurrencyPairStrategy, +) *SlinkyPriceUpdateGenerator { + return &SlinkyPriceUpdateGenerator{ + agg: agg, + extCommitCodec: extCommitCodec, + veCodec: veCodec, + currencyPairStrategy: currencyPairStrategy, + } +} + +func (pug *SlinkyPriceUpdateGenerator) GetValidMarketPriceUpdates( + ctx sdk.Context, extCommitBz []byte) (*pricestypes.MsgUpdateMarketPrices, error) { + // check whether VEs are enabled + if !ve.VoteExtensionsEnabled(ctx) { + // return a nil MsgUpdateMarketPricesTx w/ no updates + return &pricestypes.MsgUpdateMarketPrices{}, nil + } + + // unmarshal the injected extended-commit + votes, err := aggregator.GetOracleVotes( + [][]byte{extCommitBz}, + pug.veCodec, + pug.extCommitCodec, + ) + if err != nil { + return nil, err + } + + // aggregate the votes into a MsgUpdateMarketPricesTx + prices, err := pug.agg.AggregateOracleVotes(ctx, votes) + if err != nil { + return nil, err + } + + // create the update-market prices tx + msg := &pricestypes.MsgUpdateMarketPrices{} + + // map the currency-pairs to market-ids + for cp, price := range prices { + marketID, err := pug.currencyPairStrategy.ID(ctx, cp) + if err != nil { + return nil, err + } + + if !price.IsUint64() { + return nil, &InvalidPriceError{ + MarketID: marketID, + Reason: "price is not a uint64", + } + } + + // add the price to the update-market prices tx + msg.MarketPriceUpdates = append(msg.MarketPriceUpdates, &pricestypes.MsgUpdateMarketPrices_MarketPrice{ + MarketId: uint32(marketID), + Price: price.Uint64(), + }) + } + + // sort the market-price updates + sort.Slice(msg.MarketPriceUpdates, func(i, j int) bool { + return msg.MarketPriceUpdates[i].MarketId < msg.MarketPriceUpdates[j].MarketId + }) + + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + return msg, nil +} diff --git a/protocol/app/prepare/prices/slinky_price_update_generator_test.go b/protocol/app/prepare/prices/slinky_price_update_generator_test.go new file mode 100644 index 0000000000..6769e9ad47 --- /dev/null +++ b/protocol/app/prepare/prices/slinky_price_update_generator_test.go @@ -0,0 +1,231 @@ +package prices_test + +import ( + "fmt" + cmtabci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dydxprotocol/v4-chain/protocol/app/prepare/prices" + "github.com/skip-mev/slinky/abci/strategies/aggregator" + aggregatormock "github.com/skip-mev/slinky/abci/strategies/aggregator/mocks" + codecmock "github.com/skip-mev/slinky/abci/strategies/codec/mocks" + strategymock "github.com/skip-mev/slinky/abci/strategies/currencypair/mocks" + "github.com/skip-mev/slinky/abci/testutils" + vetypes "github.com/skip-mev/slinky/abci/ve/types" + oracletypes "github.com/skip-mev/slinky/pkg/types" + "github.com/stretchr/testify/suite" + "math/big" + "testing" +) + +type SlinkyPriceUpdateGeneratorSuite struct { + suite.Suite + + spug *prices.SlinkyPriceUpdateGenerator + + cps *strategymock.CurrencyPairStrategy + + veCodec *codecmock.VoteExtensionCodec + + extCommitCodec *codecmock.ExtendedCommitCodec + + va *aggregatormock.VoteAggregator +} + +func TestSlinkyPriceUpdateGeneratorSuite(t *testing.T) { + suite.Run(t, new(SlinkyPriceUpdateGeneratorSuite)) +} + +func (suite *SlinkyPriceUpdateGeneratorSuite) SetupTest() { + // setup mocks + suite.veCodec = codecmock.NewVoteExtensionCodec(suite.T()) + suite.extCommitCodec = codecmock.NewExtendedCommitCodec(suite.T()) + suite.va = aggregatormock.NewVoteAggregator(suite.T()) + suite.cps = strategymock.NewCurrencyPairStrategy(suite.T()) + suite.spug = prices.NewSlinkyPriceUpdateGenerator( + suite.va, + suite.extCommitCodec, + suite.veCodec, + suite.cps, + ) +} + +// Test that if vote-extensions aren't enabled price-update-generator returns an empty update +func (suite *SlinkyPriceUpdateGeneratorSuite) TestWithVoteExtensionsNotEnabled() { + // setup + ctx := testutils.UpdateContextWithVEHeight(testutils.CreateBaseSDKContext(suite.T()), 5) + // ctx.BlockHeight() < ctx.ConsensusParams.VoteExtensionsEnableHeight (VEs are not enabled rn) + ctx = ctx.WithBlockHeight(4) + + // expect + msg, err := suite.spug.GetValidMarketPriceUpdates(ctx, []byte{}) // 2nd argument shld be irrelevant + + // assert + suite.NoError(err) + suite.NotNil(msg) + suite.Empty(msg.MarketPriceUpdates) // no updates +} + +// Test that if aggregating oracle votes fails, we fail +func (suite *SlinkyPriceUpdateGeneratorSuite) TestVoteExtensionAggregationFails() { + // setup + ctx := testutils.UpdateContextWithVEHeight(testutils.CreateBaseSDKContext(suite.T()), 5) + ctx = ctx.WithBlockHeight(6) // VEs enabled + + // create vote-extensions + validator := []byte("validator") + // we j mock what the actual wire-transmitted bz are for this vote-extension + voteExtensionBz := []byte("vote-extension") + extCommitBz := []byte("ext-commit") // '' for ext-commit + extCommit := cmtabci.ExtendedCommitInfo{ + Votes: []cmtabci.ExtendedVoteInfo{ + { + Validator: cmtabci.Validator{ + Address: validator, + }, + VoteExtension: voteExtensionBz, + }, + }, + } + + // mock codecs + suite.extCommitCodec.On("Decode", extCommitBz).Return(extCommit, nil) + + ve := vetypes.OracleVoteExtension{ + Prices: map[uint64][]byte{ + 1: []byte("price"), + }, + } + suite.veCodec.On("Decode", voteExtensionBz).Return(ve, nil) + + // expect an error from the vote-extension aggregator + suite.va.On("AggregateOracleVotes", ctx, []aggregator.Vote{ + { + ConsAddress: sdk.ConsAddress(validator), + OracleVoteExtension: ve, + }, + }).Return(nil, fmt.Errorf("error in aggregation")) + + // execute + msg, err := suite.spug.GetValidMarketPriceUpdates(ctx, extCommitBz) + suite.Nil(msg) + suite.Error(err, "error in aggregation") +} + +// test that if price / ID conversion fails we fail +func (suite *SlinkyPriceUpdateGeneratorSuite) TestCurrencyPairConversionFails() { + // setup + ctx := testutils.UpdateContextWithVEHeight(testutils.CreateBaseSDKContext(suite.T()), 5) + ctx = ctx.WithBlockHeight(6) // VEs enabled + + // create vote-extensions + validator := []byte("validator") + // we j mock what the actual wire-transmitted bz are for this vote-extension + voteExtensionBz := []byte("vote-extension") + extCommitBz := []byte("ext-commit") // '' for ext-commit + extCommit := cmtabci.ExtendedCommitInfo{ + Votes: []cmtabci.ExtendedVoteInfo{ + { + Validator: cmtabci.Validator{ + Address: validator, + }, + VoteExtension: voteExtensionBz, + }, + }, + } + + // mock codecs + suite.extCommitCodec.On("Decode", extCommitBz).Return(extCommit, nil) + + ve := vetypes.OracleVoteExtension{ + Prices: map[uint64][]byte{ + 1: []byte("price"), + }, + } + suite.veCodec.On("Decode", voteExtensionBz).Return(ve, nil) + + mogBtc := oracletypes.NewCurrencyPair("MOG", "BTC") + // expect an error from the vote-extension aggregator + suite.va.On("AggregateOracleVotes", ctx, []aggregator.Vote{ + { + ConsAddress: sdk.ConsAddress(validator), + OracleVoteExtension: ve, + }, + }).Return(map[oracletypes.CurrencyPair]*big.Int{ + mogBtc: big.NewInt(1), + }, nil) + + // expect an error from the currency-pair strategy + suite.cps.On("ID", ctx, mogBtc).Return(uint64(0), fmt.Errorf("error in currency-pair conversion")) + + // execute + msg, err := suite.spug.GetValidMarketPriceUpdates(ctx, extCommitBz) + suite.Nil(msg) + suite.Error(err, "error in currency-pair conversion") +} + +// test that the MsgUpdateMarketPricesTx is generated correctly +func (suite *SlinkyPriceUpdateGeneratorSuite) TestValidMarketPriceUpdate() { + // setup + ctx := testutils.UpdateContextWithVEHeight(testutils.CreateBaseSDKContext(suite.T()), 5) + ctx = ctx.WithBlockHeight(6) // VEs enabled + + // create vote-extensions + validator := []byte("validator") + // we j mock what the actual wire-transmitted bz are for this vote-extension + voteExtensionBz := []byte("vote-extension") + extCommitBz := []byte("ext-commit") // '' for ext-commit + extCommit := cmtabci.ExtendedCommitInfo{ + Votes: []cmtabci.ExtendedVoteInfo{ + { + Validator: cmtabci.Validator{ + Address: validator, + }, + VoteExtension: voteExtensionBz, + }, + }, + } + + // mock codecs + suite.extCommitCodec.On("Decode", extCommitBz).Return(extCommit, nil) + + ve := vetypes.OracleVoteExtension{ + Prices: map[uint64][]byte{ + 1: []byte("price"), + }, + } + suite.veCodec.On("Decode", voteExtensionBz).Return(ve, nil) + + mogBtc := oracletypes.NewCurrencyPair("MOG", "BTC") + pepeEth := oracletypes.NewCurrencyPair("PEPE", "ETH") + // expect an error from the vote-extension aggregator + suite.va.On("AggregateOracleVotes", ctx, []aggregator.Vote{ + { + ConsAddress: sdk.ConsAddress(validator), + OracleVoteExtension: ve, + }, + }).Return(map[oracletypes.CurrencyPair]*big.Int{ + mogBtc: big.NewInt(1), + pepeEth: big.NewInt(2), + }, nil) + + // expect no error from currency-pair strategies + suite.cps.On("ID", ctx, mogBtc).Return(uint64(0), nil) + suite.cps.On("ID", ctx, pepeEth).Return(uint64(1), nil) + + // execute + msg, err := suite.spug.GetValidMarketPriceUpdates(ctx, extCommitBz) + suite.NoError(err) + + // check the message + suite.Len(msg.MarketPriceUpdates, 2) + expectedPrices := map[uint64]uint64{ + 0: 1, + 1: 2, + } + for _, mpu := range msg.MarketPriceUpdates { + price, ok := expectedPrices[uint64(mpu.MarketId)] + suite.True(ok) + + suite.Equal(price, mpu.Price) + } +} diff --git a/protocol/app/process/default_market_price_decoder.go b/protocol/app/process/default_market_price_decoder.go new file mode 100644 index 0000000000..56684f5c48 --- /dev/null +++ b/protocol/app/process/default_market_price_decoder.go @@ -0,0 +1,42 @@ +package process + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// DefaultUpdateMarketPriceTxDecoder is the default implementation of the MarketPriceDecoder interface. +// This implementation is expected to default MarketPriceUpdates in accordance with the dydx process-proposal +// logic pre vote-extensions +type DefaultUpdateMarketPriceTxDecoder struct { + // pk is the expecte dependency on x/prices keeper, used for stateful validation of the returned MarketPriceUpdateTx + pk ProcessPricesKeeper + + // tx decoder used for unmarshalling the market-price-update tx + txDecoder sdk.TxDecoder +} + +// NewDefaultUpdateMarketPriceTxDecoder returns a new DefaultUpdateMarketPriceTxDecoder +func NewDefaultUpdateMarketPriceTxDecoder( + pk ProcessPricesKeeper, txDecoder sdk.TxDecoder) *DefaultUpdateMarketPriceTxDecoder { + return &DefaultUpdateMarketPriceTxDecoder{ + pk: pk, + txDecoder: txDecoder, + } +} + +// DecodeUpdateMarketPricesTx returns a new `UpdateMarketPricesTx` after validating the following: +// - decodes the given tx bytes +// - checks the num of msgs in the tx matches expectations +// - checks the msg is of expected type +// +// If error occurs during any of the checks, returns error. +func (mpd *DefaultUpdateMarketPriceTxDecoder) DecodeUpdateMarketPricesTx( + ctx sdk.Context, txs [][]byte) (*UpdateMarketPricesTx, error) { + return DecodeUpdateMarketPricesTx(ctx, mpd.pk, mpd.txDecoder, txs[len(txs)+updateMarketPricesTxLenOffset]) +} + +// GetTxOffset returns the offset that other injected txs should be placed with respect to their normally +// expected indices. No offset is expected for the default implementation. +func (mpd DefaultUpdateMarketPriceTxDecoder) GetTxOffset(sdk.Context) int { + return 0 +} diff --git a/protocol/app/process/errors.go b/protocol/app/process/errors.go index 0edc0d491d..32b95aa233 100644 --- a/protocol/app/process/errors.go +++ b/protocol/app/process/errors.go @@ -10,8 +10,40 @@ const ( var ( // 1 - 99: Default. - ErrDecodingTxBytes = errorsmod.Register(ModuleName, 1, "Decoding tx bytes failed") - ErrMsgValidateBasic = errorsmod.Register(ModuleName, 2, "ValidateBasic failed on msg") - ErrUnexpectedNumMsgs = errorsmod.Register(ModuleName, 3, "Unexpected num of msgs") - ErrUnexpectedMsgType = errorsmod.Register(ModuleName, 4, "Unexpected msg type") + ErrDecodingTxBytes = errorsmod.Register(ModuleName, 1, "Decoding tx bytes failed") + ErrMsgValidateBasic = errorsmod.Register(ModuleName, 2, "ValidateBasic failed on msg") + ErrUnexpectedNumMsgs = errorsmod.Register(ModuleName, 3, "Unexpected num of msgs") + ErrUnexpectedMsgType = errorsmod.Register(ModuleName, 4, "Unexpected msg type") + ErrProposedPriceValidation = errorsmod.Register(ModuleName, 5, "Validation of proposed MsgUpdateMarketPrices failed") ) + +func IncorrectNumberUpdatesError(expected, actual int) error { + return errorsmod.Wrapf( + ErrProposedPriceValidation, + "incorrect number of price-updates, expected: %d, actual: %d", + expected, + actual, + ) +} + +func MissingPriceUpdateForMarket(marketID uint32) error { + return errorsmod.Wrapf( + ErrProposedPriceValidation, + "missing price-update for market: %d", + marketID, + ) +} + +func IncorrectPriceUpdateForMarket(marketID uint32, expected, actual uint64) error { + return errorsmod.Wrapf( + ErrProposedPriceValidation, + "incorrect price-update for market: %d, expected %d, actual %d", + marketID, + expected, + actual, + ) +} + +func InvalidMarketPriceUpdateError(err error) error { + return errorsmod.Wrap(ErrProposedPriceValidation, err.Error()) +} diff --git a/protocol/app/process/expected_keepers.go b/protocol/app/process/expected_keepers.go index 9787c1d176..757be839ac 100644 --- a/protocol/app/process/expected_keepers.go +++ b/protocol/app/process/expected_keepers.go @@ -19,11 +19,6 @@ type ProcessPricesKeeper interface { marketPriceUpdates *pricestypes.MsgUpdateMarketPrices, performNonDeterministicValidation bool, ) error - - UpdateSmoothedPrices( - ctx sdk.Context, - linearInterpolateFunc func(v0 uint64, v1 uint64, ppm uint32) (uint64, error), - ) error } // ProcessClobKeeper defines the expected clob keeper used for `ProcessProposal`. diff --git a/protocol/app/process/full_node_process_proposal.go b/protocol/app/process/full_node_process_proposal.go index 5cd5cb7548..346e992a7c 100644 --- a/protocol/app/process/full_node_process_proposal.go +++ b/protocol/app/process/full_node_process_proposal.go @@ -15,7 +15,7 @@ func FullNodeProcessProposalHandler( clobKeeper ProcessClobKeeper, stakingKeeper ProcessStakingKeeper, perpetualKeeper ProcessPerpetualKeeper, - pricesKeeper ProcessPricesKeeper, + pricesTxDecoder UpdateMarketPriceTxDecoder, ) sdk.ProcessProposalHandler { // Keep track of the current block height and consensus round. currentBlockHeight := int64(0) @@ -34,7 +34,7 @@ func FullNodeProcessProposalHandler( } ctx = ctx.WithValue(ConsensusRound, currentConsensusRound) - txs, err := DecodeProcessProposalTxs(ctx, txConfig.TxDecoder(), req, bridgeKeeepr, pricesKeeper) + txs, err := DecodeProcessProposalTxs(ctx, txConfig.TxDecoder(), req, bridgeKeeepr, pricesTxDecoder) if err != nil { return response, nil } diff --git a/protocol/app/process/full_node_process_proposal_test.go b/protocol/app/process/full_node_process_proposal_test.go index f6cc34ef65..e0d0104882 100644 --- a/protocol/app/process/full_node_process_proposal_test.go +++ b/protocol/app/process/full_node_process_proposal_test.go @@ -73,7 +73,7 @@ func TestFullNodeProcessProposalHandler(t *testing.T) { // Setup. _, bridgeKeeper, _, _, _, _, _ := keepertest.BridgeKeepers(t) - ctx, pricesKeeper, _, indexPriceCache, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, pricesKeeper, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) keepertest.CreateTestMarkets(t, ctx, pricesKeeper) indexPriceCache.UpdatePrices(constants.AtTimeTSingleExchangePriceUpdate) @@ -88,7 +88,7 @@ func TestFullNodeProcessProposalHandler(t *testing.T) { mockClobKeeper, &mocks.ProcessStakingKeeper{}, &mocks.ProcessPerpetualKeeper{}, - pricesKeeper, + process.NewDefaultUpdateMarketPriceTxDecoder(pricesKeeper, constants.TestEncodingCfg.TxConfig.TxDecoder()), ) req := abci.RequestProcessProposal{Txs: tc.txsBytes} diff --git a/protocol/app/process/market_price_decoder.go b/protocol/app/process/market_price_decoder.go new file mode 100644 index 0000000000..e0674d31b5 --- /dev/null +++ b/protocol/app/process/market_price_decoder.go @@ -0,0 +1,28 @@ +package process + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" +) + +// MarketPriceDecoder is an interface for decoding market price transactions, This interface is responsible +// for distinguishing between logic for unmarshalling MarketPriceUpdates, between MarketPriceUpdates +// determined by the proposer's price-cache, and from VoteExtensions. +type UpdateMarketPriceTxDecoder interface { + // DecodeUpdateMarketPricesTx decodes the tx-bytes from the RequestProcessProposal and returns a MarketPriceUpdateTx. + DecodeUpdateMarketPricesTx(ctx sdk.Context, txs [][]byte) (*UpdateMarketPricesTx, error) + + // GetTxOffset returns the offset that other injected txs should be placed with respect to their normally + // expected indices. This method is used to account for injected vote-extensions, or any other injected + // txs from dependencies. + GetTxOffset(ctx sdk.Context) int +} + +func NewUpdateMarketPricesTx( + ctx sdk.Context, pk ProcessPricesKeeper, msg *pricestypes.MsgUpdateMarketPrices) *UpdateMarketPricesTx { + return &UpdateMarketPricesTx{ + ctx: ctx, + pricesKeeper: pk, + msg: msg, + } +} diff --git a/protocol/app/process/market_prices.go b/protocol/app/process/market_prices.go index 4e5d2b4b0b..52851e8b38 100644 --- a/protocol/app/process/market_prices.go +++ b/protocol/app/process/market_prices.go @@ -63,7 +63,7 @@ func (umpt *UpdateMarketPricesTx) Validate() error { return getValidateBasicError(umpt.msg, err) } - if err := umpt.pricesKeeper.PerformStatefulPriceUpdateValidation(umpt.ctx, umpt.msg, true); err != nil { + if err := umpt.pricesKeeper.PerformStatefulPriceUpdateValidation(umpt.ctx, umpt.msg, false); err != nil { return err } diff --git a/protocol/app/process/market_prices_test.go b/protocol/app/process/market_prices_test.go index 97ae1a239e..a77bf9c188 100644 --- a/protocol/app/process/market_prices_test.go +++ b/protocol/app/process/market_prices_test.go @@ -1,10 +1,11 @@ package process_test import ( - errorsmod "cosmossdk.io/errors" "errors" "testing" + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/dydxprotocol/v4-chain/protocol/app/process" "github.com/dydxprotocol/v4-chain/protocol/daemons/pricefeed/api" @@ -64,8 +65,9 @@ func TestDecodeUpdateMarketPricesTx(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, k, _, _, _, _ := keepertest.PricesKeepers(t) - umpt, err := process.DecodeUpdateMarketPricesTx(ctx, k, encodingCfg.TxConfig.TxDecoder(), tc.txBytes) + ctx, k, _, _, _ := keepertest.PricesKeepers(t) + pricesTxDecoder := process.NewDefaultUpdateMarketPriceTxDecoder(k, encodingCfg.TxConfig.TxDecoder()) + umpt, err := pricesTxDecoder.DecodeUpdateMarketPricesTx(ctx, [][]byte{tc.txBytes}) if tc.expectedErr != nil { require.ErrorContains(t, err, tc.expectedErr.Error()) require.Nil(t, umpt) @@ -101,11 +103,6 @@ func TestUpdateMarketPricesTx_Validate(t *testing.T) { "market param price (99) does not exist", ), }, - "Error: Stateful + NonDeterministic validation fails": { - txBytes: validMsgTxBytes, // Msg is valid, but there's no corresponding index price. - // Skip index price updates, so the validation fails. - expectedErr: errorsmod.Wrapf(types.ErrIndexPriceNotAvailable, "index price for market (0) is not available"), - }, "Error: ValidateBasic fails": { txBytes: invalidStatelessMsgTxBytes, indexPrices: constants.AtTimeTSingleExchangePriceUpdate, @@ -123,11 +120,14 @@ func TestUpdateMarketPricesTx_Validate(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, k, _, indexPriceCache, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, k, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) keepertest.CreateTestMarkets(t, ctx, k) indexPriceCache.UpdatePrices(tc.indexPrices) - umpt, err := process.DecodeUpdateMarketPricesTx(ctx, k, constants.TestEncodingCfg.TxConfig.TxDecoder(), tc.txBytes) + + // Decode. + pricesTxDecoder := process.NewDefaultUpdateMarketPriceTxDecoder(k, constants.TestEncodingCfg.TxConfig.TxDecoder()) + umpt, err := pricesTxDecoder.DecodeUpdateMarketPricesTx(ctx, [][]byte{tc.txBytes}) require.NoError(t, err) // Run and Validate. @@ -162,8 +162,12 @@ func TestUpdateMarketPricesTx_GetMsg(t *testing.T) { t.Run(name, func(t *testing.T) { var msg sdk.Msg if tc.txBytes != nil { - ctx, k, _, _, _, _ := keepertest.PricesKeepers(t) - umpt, err := process.DecodeUpdateMarketPricesTx(ctx, k, constants.TestEncodingCfg.TxConfig.TxDecoder(), tc.txBytes) + ctx, k, _, _, _ := keepertest.PricesKeepers(t) + + // Decode. + pricesTxDecoder := process.NewDefaultUpdateMarketPriceTxDecoder(k, constants.TestEncodingCfg.TxConfig.TxDecoder()) + + umpt, err := pricesTxDecoder.DecodeUpdateMarketPricesTx(ctx, [][]byte{tc.txBytes}) require.NoError(t, err) msg = umpt.GetMsg() } else { diff --git a/protocol/app/process/process_proposal.go b/protocol/app/process/process_proposal.go index d75842851e..f69af84151 100644 --- a/protocol/app/process/process_proposal.go +++ b/protocol/app/process/process_proposal.go @@ -3,7 +3,6 @@ package process import ( "time" - "github.com/dydxprotocol/v4-chain/protocol/lib" error_lib "github.com/dydxprotocol/v4-chain/protocol/lib/error" "github.com/dydxprotocol/v4-chain/protocol/lib/log" @@ -37,6 +36,7 @@ func ProcessProposalHandler( stakingKeeper ProcessStakingKeeper, perpetualKeeper ProcessPerpetualKeeper, pricesKeeper ProcessPricesKeeper, + pricesTxDecoder UpdateMarketPriceTxDecoder, ) sdk.ProcessProposalHandler { // Keep track of the current block height and consensus round. currentBlockHeight := int64(0) @@ -64,15 +64,7 @@ func ProcessProposalHandler( log.Module, ModuleName, ) - // Perform the update of smoothed prices here to ensure that smoothed prices are updated even if a block is later - // rejected by consensus. We want smoothed prices to be updated on fixed cadence, and we are piggybacking on - // consensus round to do so. - if err := pricesKeeper.UpdateSmoothedPrices(ctx, lib.Uint64LinearInterpolate); err != nil { - recordErrorMetricsWithLabel(metrics.UpdateSmoothedPrices) - error_lib.LogErrorWithOptionalContext(ctx, "UpdateSmoothedPrices failed", err) - } - - txs, err := DecodeProcessProposalTxs(ctx, txConfig.TxDecoder(), req, bridgeKeeper, pricesKeeper) + txs, err := DecodeProcessProposalTxs(ctx, txConfig.TxDecoder(), req, bridgeKeeper, pricesTxDecoder) if err != nil { error_lib.LogErrorWithOptionalContext(ctx, "DecodeProcessProposalTxs failed", err) recordErrorMetricsWithLabel(metrics.Decode) diff --git a/protocol/app/process/process_proposal_test.go b/protocol/app/process/process_proposal_test.go index 2909f99ab4..cccf4cee36 100644 --- a/protocol/app/process/process_proposal_test.go +++ b/protocol/app/process/process_proposal_test.go @@ -258,7 +258,7 @@ func TestProcessProposalHandler_Error(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, pricesKeeper, _, indexPriceCache, marketToSmoothedPrices, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, pricesKeeper, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) keepertest.CreateTestMarkets(t, ctx, pricesKeeper) indexPriceCache.UpdatePrices(constants.AtTimeTSingleExchangePriceUpdate) @@ -285,6 +285,7 @@ func TestProcessProposalHandler_Error(t *testing.T) { &mocks.ProcessStakingKeeper{}, &mocks.ProcessPerpetualKeeper{}, pricesKeeper, + process.NewDefaultUpdateMarketPriceTxDecoder(pricesKeeper, constants.TestEncodingCfg.TxConfig.TxDecoder()), ) req := abci.RequestProcessProposal{Txs: tc.txsBytes} @@ -294,11 +295,6 @@ func TestProcessProposalHandler_Error(t *testing.T) { // Validate. require.Equal(t, tc.expectedResponse, *resp) - require.Equal( - t, - marketToSmoothedPrices.GetSmoothedPricesForTest(), - constants.AtTimeTSingleExchangeSmoothedPrices, - ) }) } } diff --git a/protocol/app/process/slinky_market_price_decoder.go b/protocol/app/process/slinky_market_price_decoder.go new file mode 100644 index 0000000000..ab914f5de5 --- /dev/null +++ b/protocol/app/process/slinky_market_price_decoder.go @@ -0,0 +1,122 @@ +package process + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/skip-mev/slinky/abci/ve" + + "github.com/dydxprotocol/v4-chain/protocol/app/constants" + "github.com/dydxprotocol/v4-chain/protocol/app/prepare/prices" + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" +) + +// SlinkyMarketPriceDecoder wraps an existing MarketPriceDecoder with logic to verify that the MarketPriceUpdateTx +// was indeed derived from vote-extensions injected into the block. +type SlinkyMarketPriceDecoder struct { + // underlying UpdateMarketPriceTxDecoder + decoder UpdateMarketPriceTxDecoder + + // underlying vote-extension aggregator + agg prices.PriceUpdateGenerator +} + +// NewSlinkyMarketPriceDecoder returns a new SlinkyMarketPriceDecoder +func NewSlinkyMarketPriceDecoder( + decoder UpdateMarketPriceTxDecoder, agg prices.PriceUpdateGenerator) *SlinkyMarketPriceDecoder { + return &SlinkyMarketPriceDecoder{ + decoder: decoder, + agg: agg, + } +} + +// DecodeUpdateMarketPricesTx returns a new `UpdateMarketPricesTx` after validating the following: +// - the underlying decoder decodes successfully +// - the UpdateMarketPricesTx follows correctly from the vote-extensions +// - vote-extensions are enabled: each price per market-id is derived from the injected extended commit +// - vote-extensions are disabled: no price updates are proposed +// - vote-extensions are inserted into the block if necessary +// +// If error occurs during any of the checks, returns error. +func (mpd *SlinkyMarketPriceDecoder) DecodeUpdateMarketPricesTx( + ctx sdk.Context, txs [][]byte) (*UpdateMarketPricesTx, error) { + expectedMsg := &pricestypes.MsgUpdateMarketPrices{} + + // check if vote-extensions are enabled + if ve.VoteExtensionsEnabled(ctx) { + // if there isn't a vote-extension in the block when there should be, fail + if len(txs) < constants.OracleVEInjectedTxs { + return nil, getDecodingError( + msgUpdateMarketPricesType, + fmt.Errorf("expected %v txs, got %v", constants.OracleVEInjectedTxs, len(txs))) + } + + // get the expected message from the injected vote-extensions + var err error + expectedMsg, err = mpd.agg.GetValidMarketPriceUpdates(ctx, txs[constants.OracleInfoIndex]) + if err != nil { + return nil, getDecodingError(msgUpdateMarketPricesType, err) + } + } + + // use the underlying decoder to get the UpdateMarketPricesTx + updateMarketPrices, err := mpd.decoder.DecodeUpdateMarketPricesTx(ctx, txs) + if err != nil { + return nil, err + } + + updateMarketPricesMsg, ok := updateMarketPrices.GetMsg().(*pricestypes.MsgUpdateMarketPrices) + if !ok { + return nil, getDecodingError( + msgUpdateMarketPricesType, fmt.Errorf("expected %T, got %T", expectedMsg, updateMarketPricesMsg)) + } + + // check that the UpdateMarketPricesTx matches the expected message + if err := checkEqualityOfMarketPriceUpdate(expectedMsg, updateMarketPricesMsg); err != nil { + return nil, err + } + + return updateMarketPrices, nil +} + +// GetTxOffset returns the offset that other injected txs should be placed with respect to their normally +// expected indices. If vote-extensions are enabled, constants.OracleVEInjectedTxs is the expected offset, +// otherwise 0 is the expected offset. +func (mpd *SlinkyMarketPriceDecoder) GetTxOffset(ctx sdk.Context) int { + if ve.VoteExtensionsEnabled(ctx) { + return constants.OracleVEInjectedTxs + } + return 0 +} + +// checkEqualityOfMarketPriceUpdate checks that the given market-price updates are equivalent +// and both pass validate basic checks. +func checkEqualityOfMarketPriceUpdate(expectedMsg, actualMsg *pricestypes.MsgUpdateMarketPrices) error { + // assert that the market-price updates are valid + if err := expectedMsg.ValidateBasic(); err != nil { + return InvalidMarketPriceUpdateError(err) + } + + if err := actualMsg.ValidateBasic(); err != nil { + return InvalidMarketPriceUpdateError(err) + } + // assert len is correct + if len(expectedMsg.MarketPriceUpdates) != len(actualMsg.MarketPriceUpdates) { + return IncorrectNumberUpdatesError(len(expectedMsg.MarketPriceUpdates), len(actualMsg.MarketPriceUpdates)) + } + + // check that the actual prices match the expected prices (both are sorted so markets are in the same order) + for i, actualUpdate := range actualMsg.MarketPriceUpdates { + expectedUpdate := expectedMsg.MarketPriceUpdates[i] + + if expectedUpdate.MarketId != actualUpdate.MarketId { + return MissingPriceUpdateForMarket(expectedUpdate.MarketId) + } + + if expectedUpdate.Price != actualUpdate.Price { + return IncorrectPriceUpdateForMarket(expectedUpdate.MarketId, expectedUpdate.Price, actualUpdate.Price) + } + } + + return nil +} diff --git a/protocol/app/process/slinky_market_price_decoder_test.go b/protocol/app/process/slinky_market_price_decoder_test.go new file mode 100644 index 0000000000..6880737cce --- /dev/null +++ b/protocol/app/process/slinky_market_price_decoder_test.go @@ -0,0 +1,321 @@ +package process_test + +import ( + "fmt" + "testing" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/skip-mev/slinky/abci/testutils" + "github.com/stretchr/testify/suite" + + "github.com/dydxprotocol/v4-chain/protocol/app/constants" + "github.com/dydxprotocol/v4-chain/protocol/app/process" + "github.com/dydxprotocol/v4-chain/protocol/mocks" + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" +) + +type SlinkyMarketPriceDecoderSuite struct { + suite.Suite + + // mock price-update generator + gen *mocks.PriceUpdateGenerator + // mock UpdateMarketPriceTxDecoder + decoder *mocks.UpdateMarketPriceTxDecoder + // mock context + ctx sdk.Context +} + +func TestSlinkyMarketPriceDecoderSuite(t *testing.T) { + suite.Run(t, new(SlinkyMarketPriceDecoderSuite)) +} + +func (suite *SlinkyMarketPriceDecoderSuite) SetupTest() { + // setup context + suite.ctx = testutils.CreateBaseSDKContext(suite.T()) + + // setup mocks + suite.gen = mocks.NewPriceUpdateGenerator(suite.T()) + suite.decoder = mocks.NewUpdateMarketPriceTxDecoder(suite.T()) +} + +// test that if vote-extensions are not enabled, the MsgUpdateMarketPrices proposed should be empty +func (suite *SlinkyMarketPriceDecoderSuite) TestVoteExtensionsNotEnabled() { + suite.Run("test that a non-empty proposed market-price update fails", func() { + // disable VEs + suite.ctx = testutils.UpdateContextWithVEHeight(suite.ctx, 4) + suite.ctx = suite.ctx.WithBlockHeight(3) + + proposal := [][]byte{[]byte("test")} + + // mock decoder response that returns non-empty prices + suite.decoder.On("DecodeUpdateMarketPricesTx", + suite.ctx, proposal).Return( + process.NewUpdateMarketPricesTx(suite.ctx, nil, &pricestypes.MsgUpdateMarketPrices{ + MarketPriceUpdates: []*pricestypes.MsgUpdateMarketPrices_MarketPrice{ + { + MarketId: 1, // propose non-empty prices + Price: 100, + }, + }, + }), nil).Once() + + // expect an error + expectError := process.IncorrectNumberUpdatesError(0, 1) + + decoder := process.NewSlinkyMarketPriceDecoder(suite.decoder, suite.gen) + tx, err := decoder.DecodeUpdateMarketPricesTx(suite.ctx, proposal) + suite.Nil(tx) + suite.EqualError(expectError, err.Error()) + }) + + suite.Run("test that the proposed prices must be empty", func() { + // disable VEs + suite.ctx = testutils.UpdateContextWithVEHeight(suite.ctx, 4) + suite.ctx = suite.ctx.WithBlockHeight(3) + + proposal := [][]byte{[]byte("test")} + + // mock decoder response that returns non-empty prices + suite.decoder.On("DecodeUpdateMarketPricesTx", + suite.ctx, proposal).Return( + process.NewUpdateMarketPricesTx(suite.ctx, nil, &pricestypes.MsgUpdateMarketPrices{}), nil).Once() + + decoder := process.NewSlinkyMarketPriceDecoder(suite.decoder, suite.gen) + tx, err := decoder.DecodeUpdateMarketPricesTx(suite.ctx, proposal) + suite.Nil(err) + suite.Len(tx.GetMsg().(*pricestypes.MsgUpdateMarketPrices).MarketPriceUpdates, 0) + }) +} + +// test that if vote-extensions are enabled +// - missing extended commit -> failure +// - price-update generator fails -> failure +func (suite *SlinkyMarketPriceDecoderSuite) TestVoteExtensionsEnabled() { + suite.Run("test that missing extended commit -> failure", func() { + // enable VEs + suite.ctx = testutils.UpdateContextWithVEHeight(suite.ctx, 4) + suite.ctx = suite.ctx.WithBlockHeight(5) + + proposal := [][]byte{} + + decoder := process.NewSlinkyMarketPriceDecoder(suite.decoder, suite.gen) + tx, err := decoder.DecodeUpdateMarketPricesTx(suite.ctx, proposal) + suite.Nil(tx) + suite.Error(err) + }) + + suite.Run("test that price-update generator fails -> failure", func() { + // enable VEs + suite.ctx = testutils.UpdateContextWithVEHeight(suite.ctx, 4) + suite.ctx = suite.ctx.WithBlockHeight(5) + + proposal := [][]byte{[]byte("test")} + err := fmt.Errorf("error") + + suite.gen.On("GetValidMarketPriceUpdates", + suite.ctx, proposal[constants.OracleInfoIndex]).Return(nil, err) + + decoder := process.NewSlinkyMarketPriceDecoder(suite.decoder, suite.gen) + tx, err := decoder.DecodeUpdateMarketPricesTx(suite.ctx, proposal) + suite.Nil(tx) + suite.Error(err) + }) +} + +func (suite *SlinkyMarketPriceDecoderSuite) TestMarketPriceUpdateValidation_WithVoteExtensionsEnabled() { + suite.Run("if DecodeUpdatemarketPricesTx fails on underlying decoder - fail", func() { + // enable VEs + suite.ctx = testutils.UpdateContextWithVEHeight(suite.ctx, 4) + suite.ctx = suite.ctx.WithBlockHeight(5) + + proposal := [][]byte{[]byte("test")} + + suite.gen.On("GetValidMarketPriceUpdates", + suite.ctx, proposal[constants.OracleInfoIndex]).Return(&pricestypes.MsgUpdateMarketPrices{ + MarketPriceUpdates: []*pricestypes.MsgUpdateMarketPrices_MarketPrice{ + { + MarketId: 1, + Price: 100, + }, + }, + }, nil) + + suite.decoder.On("DecodeUpdateMarketPricesTx", + suite.ctx, proposal).Return(nil, fmt.Errorf("error")) + + decoder := process.NewSlinkyMarketPriceDecoder(suite.decoder, suite.gen) + tx, err := decoder.DecodeUpdateMarketPricesTx(suite.ctx, proposal) + suite.Nil(tx) + suite.Error(err) + }) + + suite.Run("if DecodeUpdateMarketPricesTx returns conflicting updates (missing market-id) - fail", func() { + // enable VEs + suite.ctx = testutils.UpdateContextWithVEHeight(suite.ctx, 4) + suite.ctx = suite.ctx.WithBlockHeight(5) + + proposal := [][]byte{[]byte("test")} + + expectedMsg := &pricestypes.MsgUpdateMarketPrices{ + MarketPriceUpdates: []*pricestypes.MsgUpdateMarketPrices_MarketPrice{ + { + MarketId: 1, + Price: 100, + }, + }, + } + + suite.gen.On("GetValidMarketPriceUpdates", + suite.ctx, proposal[constants.OracleInfoIndex]).Return(expectedMsg, nil) + + suite.decoder.On("DecodeUpdateMarketPricesTx", + suite.ctx, proposal).Return( + process.NewUpdateMarketPricesTx(suite.ctx, nil, &pricestypes.MsgUpdateMarketPrices{ + MarketPriceUpdates: []*pricestypes.MsgUpdateMarketPrices_MarketPrice{ + { + MarketId: 2, // propose non-empty prices + Price: 100, + }, + }, + }), nil) + + decoder := process.NewSlinkyMarketPriceDecoder(suite.decoder, suite.gen) + tx, err := decoder.DecodeUpdateMarketPricesTx(suite.ctx, proposal) + suite.Nil(tx) + suite.Error(err, process.MissingPriceUpdateForMarket(expectedMsg.MarketPriceUpdates[0].MarketId).Error()) + }) + + suite.Run("if DecodeUpdateMarketPricesTx returns conflicting updates (incorrect price for market-id) - fail", func() { + // enable VEs + suite.ctx = testutils.UpdateContextWithVEHeight(suite.ctx, 4) + suite.ctx = suite.ctx.WithBlockHeight(5) + + proposal := [][]byte{[]byte("test")} + + expectedMsg := &pricestypes.MsgUpdateMarketPrices{ + MarketPriceUpdates: []*pricestypes.MsgUpdateMarketPrices_MarketPrice{ + { + MarketId: 1, + Price: 100, + }, + }, + } + + suite.gen.On("GetValidMarketPriceUpdates", + suite.ctx, proposal[constants.OracleInfoIndex]).Return(expectedMsg, nil) + + suite.decoder.On("DecodeUpdateMarketPricesTx", + suite.ctx, proposal).Return( + process.NewUpdateMarketPricesTx(suite.ctx, nil, &pricestypes.MsgUpdateMarketPrices{ + MarketPriceUpdates: []*pricestypes.MsgUpdateMarketPrices_MarketPrice{ + { + MarketId: 1, // propose non-empty prices + Price: 101, + }, + }, + }), nil) + + decoder := process.NewSlinkyMarketPriceDecoder(suite.decoder, suite.gen) + tx, err := decoder.DecodeUpdateMarketPricesTx(suite.ctx, proposal) + suite.Nil(tx) + suite.Error(err, process.IncorrectPriceUpdateForMarket(1, 100, 101)) + }) + + suite.Run("if DecodeUpdateMarketPricesTx returns msgs that don't pass ValidateBasic - fail", func() { + // enable VEs + suite.ctx = testutils.UpdateContextWithVEHeight(suite.ctx, 4) + suite.ctx = suite.ctx.WithBlockHeight(5) + + proposal := [][]byte{[]byte("test")} + + expectedMsg := &pricestypes.MsgUpdateMarketPrices{ + MarketPriceUpdates: []*pricestypes.MsgUpdateMarketPrices_MarketPrice{ + { + MarketId: 2, // incorrect order + Price: 100, + }, + { + MarketId: 1, + Price: 100, + }, + }, + } + + suite.gen.On("GetValidMarketPriceUpdates", + suite.ctx, proposal[constants.OracleInfoIndex]).Return(expectedMsg, nil) + + suite.decoder.On("DecodeUpdateMarketPricesTx", + suite.ctx, proposal).Return(process.NewUpdateMarketPricesTx(suite.ctx, nil, expectedMsg), nil) + + decoder := process.NewSlinkyMarketPriceDecoder(suite.decoder, suite.gen) + tx, err := decoder.DecodeUpdateMarketPricesTx(suite.ctx, proposal) + suite.Nil(tx) + suite.Error(err) + }) +} + +// test happy path +func (suite *SlinkyMarketPriceDecoderSuite) TestHappyPath_VoteExtensionsEnabled() { + // enable VEs + suite.ctx = testutils.UpdateContextWithVEHeight(suite.ctx, 4) + suite.ctx = suite.ctx.WithBlockHeight(5) + + proposal := [][]byte{[]byte("test")} + + expectedMsg := &pricestypes.MsgUpdateMarketPrices{ + MarketPriceUpdates: []*pricestypes.MsgUpdateMarketPrices_MarketPrice{ + { + MarketId: 1, + Price: 100, + }, + }, + } + + suite.gen.On("GetValidMarketPriceUpdates", + suite.ctx, proposal[constants.OracleInfoIndex]).Return(expectedMsg, nil) + + suite.decoder.On("DecodeUpdateMarketPricesTx", + suite.ctx, proposal).Return( + process.NewUpdateMarketPricesTx(suite.ctx, nil, &pricestypes.MsgUpdateMarketPrices{ + MarketPriceUpdates: []*pricestypes.MsgUpdateMarketPrices_MarketPrice{ + { + MarketId: 1, // propose non-empty prices + Price: 100, + }, + }, + }), nil) + + decoder := process.NewSlinkyMarketPriceDecoder(suite.decoder, suite.gen) + tx, err := decoder.DecodeUpdateMarketPricesTx(suite.ctx, proposal) + suite.NoError(err) + suite.NotNil(tx) + + msg := tx.GetMsg().(*pricestypes.MsgUpdateMarketPrices) + suite.Len(msg.MarketPriceUpdates, 1) + suite.Equal(expectedMsg.MarketPriceUpdates[0], msg.MarketPriceUpdates[0]) +} + +func (suite *SlinkyMarketPriceDecoderSuite) TestGetTxOffset() { + suite.Run("TxOffset is 0 if VE is not enabled", func() { + decoder := process.NewSlinkyMarketPriceDecoder(suite.decoder, suite.gen) + + suite.ctx = testutils.CreateBaseSDKContext(suite.T()) + suite.ctx = testutils.UpdateContextWithVEHeight(suite.ctx, 4) + suite.ctx = suite.ctx.WithBlockHeight(2) + + offset := decoder.GetTxOffset(suite.ctx) + suite.Equal(0, offset) + }) + + suite.Run("TxOffset is constants.OracleVEInjectedTxs if VE is enabled", func() { + decoder := process.NewSlinkyMarketPriceDecoder( + process.NewDefaultUpdateMarketPriceTxDecoder(nil, nil), nil) // ignore deps + + suite.ctx = testutils.CreateBaseSDKContext(suite.T()) + suite.ctx = testutils.UpdateContextWithVEHeight(suite.ctx, 4) + suite.ctx = suite.ctx.WithBlockHeight(5) + + offset := decoder.GetTxOffset(suite.ctx) + suite.Equal(constants.OracleVEInjectedTxs, offset) + }) +} diff --git a/protocol/app/process/transactions.go b/protocol/app/process/transactions.go index 9468046d69..4cc4191e2f 100644 --- a/protocol/app/process/transactions.go +++ b/protocol/app/process/transactions.go @@ -1,11 +1,12 @@ package process import ( + "slices" + errorsmod "cosmossdk.io/errors" abci "github.com/cometbft/cometbft/abci/types" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/dydxprotocol/v4-chain/protocol/lib" - "slices" ) const ( @@ -61,7 +62,7 @@ type ProcessProposalTxs struct { ProposedOperationsTx *ProposedOperationsTx AcknowledgeBridgesTx *AcknowledgeBridgesTx AddPremiumVotesTx *AddPremiumVotesTx - UpdateMarketPricesTx *UpdateMarketPricesTx + UpdateMarketPricesTx *UpdateMarketPricesTx // abstract over MarketPriceUpdates from VEs or default. // Multi msgs txs. OtherTxs []*OtherMsgsTx @@ -73,21 +74,33 @@ func DecodeProcessProposalTxs( decoder sdk.TxDecoder, req *abci.RequestProcessProposal, bridgeKeeper ProcessBridgeKeeper, - pricesKeeper ProcessPricesKeeper, + pricesTxDecoder UpdateMarketPriceTxDecoder, ) (*ProcessProposalTxs, error) { - // Check len. + // Check len (accounting for offset from injected vote-extensions if applicable) + offset := pricesTxDecoder.GetTxOffset(ctx) + injectedTxCount := minTxsCount + offset numTxs := len(req.Txs) - if numTxs < minTxsCount { + if numTxs < injectedTxCount { return nil, errorsmod.Wrapf( ErrUnexpectedNumMsgs, "Expected the proposal to contain at least %d txs, but got %d", - minTxsCount, + injectedTxCount, numTxs, ) } + // Price updates. + updatePricesTx, err := pricesTxDecoder.DecodeUpdateMarketPricesTx( + ctx, + req.Txs, + ) + if err != nil { + return nil, err + } + // Operations. - operationsTx, err := DecodeProposedOperationsTx(decoder, req.Txs[proposedOperationsTxIndex]) + // if vote-extensions were injected, offset will be incremented. + operationsTx, err := DecodeProposedOperationsTx(decoder, req.Txs[proposedOperationsTxIndex+offset]) if err != nil { return nil, err } @@ -109,20 +122,10 @@ func DecodeProcessProposalTxs( return nil, err } - // Price updates. - updatePricesTx, err := DecodeUpdateMarketPricesTx( - ctx, - pricesKeeper, - decoder, - req.Txs[numTxs+updateMarketPricesTxLenOffset], - ) - if err != nil { - return nil, err - } - // Other txs. - allOtherTxs := make([]*OtherMsgsTx, numTxs-minTxsCount) - for i, txBytes := range req.Txs[firstOtherTxIndex : numTxs+lastOtherTxLenOffset] { + // if vote-extensions were injected, offset will be incremented. + allOtherTxs := make([]*OtherMsgsTx, numTxs-injectedTxCount) + for i, txBytes := range req.Txs[firstOtherTxIndex+offset : numTxs+lastOtherTxLenOffset] { otherTx, err := DecodeOtherMsgsTx(decoder, txBytes) if err != nil { return nil, err diff --git a/protocol/app/process/transactions_test.go b/protocol/app/process/transactions_test.go index a45ad583ca..62a93bc627 100644 --- a/protocol/app/process/transactions_test.go +++ b/protocol/app/process/transactions_test.go @@ -108,7 +108,7 @@ func TestDecodeProcessProposalTxs_Error(t *testing.T) { t.Run(name, func(t *testing.T) { // Setup. _, bridgeKeeper, _, _, _, _, _ := keepertest.BridgeKeepers(t) - ctx, pricesKeeper, _, _, _, _ := keepertest.PricesKeepers(t) + ctx, pricesKeeper, _, _, _ := keepertest.PricesKeepers(t) // Run. _, err := process.DecodeProcessProposalTxs( @@ -116,7 +116,7 @@ func TestDecodeProcessProposalTxs_Error(t *testing.T) { constants.TestEncodingCfg.TxConfig.TxDecoder(), &abci.RequestProcessProposal{Txs: tc.txsBytes}, bridgeKeeper, - pricesKeeper, + process.NewDefaultUpdateMarketPriceTxDecoder(pricesKeeper, constants.TestEncodingCfg.TxConfig.TxDecoder()), ) // Validate. @@ -188,7 +188,7 @@ func TestDecodeProcessProposalTxs_Valid(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, pricesKeeper, _, _, _, _ := keepertest.PricesKeepers(t) + ctx, pricesKeeper, _, _, _ := keepertest.PricesKeepers(t) _, bridgeKeeper, _, _, _, _, _ := keepertest.BridgeKeepers(t) // Run. @@ -197,7 +197,7 @@ func TestDecodeProcessProposalTxs_Valid(t *testing.T) { constants.TestEncodingCfg.TxConfig.TxDecoder(), &abci.RequestProcessProposal{Txs: tc.txsBytes}, bridgeKeeper, - pricesKeeper, + process.NewDefaultUpdateMarketPriceTxDecoder(pricesKeeper, constants.TestEncodingCfg.TxConfig.TxDecoder()), ) // Validate. @@ -318,7 +318,7 @@ func TestProcessProposalTxs_Validate_Error(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, pricesKeeper, _, indexPriceCache, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, pricesKeeper, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) keepertest.CreateTestMarkets(t, ctx, pricesKeeper) indexPriceCache.UpdatePrices(constants.AtTimeTSingleExchangePriceUpdate) @@ -345,7 +345,7 @@ func TestProcessProposalTxs_Validate_Error(t *testing.T) { encodingCfg.TxConfig.TxDecoder(), &abci.RequestProcessProposal{Txs: tc.txsBytes}, mockBridgeKeeper, - pricesKeeper, + process.NewDefaultUpdateMarketPriceTxDecoder(pricesKeeper, constants.TestEncodingCfg.TxConfig.TxDecoder()), ) require.NoError(t, err) @@ -425,7 +425,7 @@ func TestProcessProposalTxs_Validate_Valid(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, pricesKeeper, _, indexPriceCache, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, pricesKeeper, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) keepertest.CreateTestMarkets(t, ctx, pricesKeeper) indexPriceCache.UpdatePrices(constants.AtTimeTSingleExchangePriceUpdate) @@ -452,7 +452,7 @@ func TestProcessProposalTxs_Validate_Valid(t *testing.T) { constants.TestEncodingCfg.TxConfig.TxDecoder(), &abci.RequestProcessProposal{Txs: tc.txsBytes}, mockBridgeKeeper, - pricesKeeper, + process.NewDefaultUpdateMarketPriceTxDecoder(pricesKeeper, constants.TestEncodingCfg.TxConfig.TxDecoder()), ) require.NoError(t, err) diff --git a/protocol/app/testdata/default_genesis_state.json b/protocol/app/testdata/default_genesis_state.json index 1b30955b02..92e61f4219 100644 --- a/protocol/app/testdata/default_genesis_state.json +++ b/protocol/app/testdata/default_genesis_state.json @@ -86,9 +86,10 @@ } }, "block_rate_limit_config": { + "max_short_term_order_cancellations_per_n_blocks": [], "max_short_term_orders_per_n_blocks": [], - "max_stateful_orders_per_n_blocks": [], - "max_short_term_order_cancellations_per_n_blocks": [] + "max_short_term_orders_and_cancels_per_n_blocks": [], + "max_stateful_orders_per_n_blocks": [] }, "equity_tier_limit_config": { "short_term_order_equity_tiers": [], diff --git a/protocol/app/upgrades.go b/protocol/app/upgrades.go index 51a6dc9e39..99c6c7b0c1 100644 --- a/protocol/app/upgrades.go +++ b/protocol/app/upgrades.go @@ -31,6 +31,7 @@ func (app *App) setupUpgradeHandlers() { app.ModuleManager, app.configurator, app.PerpetualsKeeper, + app.ClobKeeper, ), ) } diff --git a/protocol/app/upgrades/v5.0.0/upgrade.go b/protocol/app/upgrades/v5.0.0/upgrade.go index 56f78e72c5..2e8176c179 100644 --- a/protocol/app/upgrades/v5.0.0/upgrade.go +++ b/protocol/app/upgrades/v5.0.0/upgrade.go @@ -6,6 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" + clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" upgradetypes "cosmossdk.io/x/upgrade/types" @@ -30,10 +31,78 @@ func perpetualsUpgrade( } } +// blockRateLimitConfigUpdate upgrades the block rate limit. It searches for the +// 1-block window limit for short term and cancellations, sums them, and creates a new +// combined rate limit. +func blockRateLimitConfigUpdate( + ctx sdk.Context, + clobKeeper clobtypes.ClobKeeper, +) { + oldBlockRateLimitConfig := clobKeeper.GetBlockRateLimitConfiguration(ctx) + ctx.Logger().Info( + fmt.Sprintf( + "Combining the short term order placement and cancellation limits of previous config: %+v\n", + oldBlockRateLimitConfig, + ), + ) + numAllowedShortTermOrderPlacementsInOneBlock := 0 + numAllowedShortTermOrderCancellationsInOneBlock := 0 + oldShortTermOrderRateLimits := oldBlockRateLimitConfig.MaxShortTermOrdersPerNBlocks + for _, limit := range oldShortTermOrderRateLimits { + if limit.NumBlocks == 1 { + numAllowedShortTermOrderPlacementsInOneBlock += int(limit.Limit) + break + } + } + if numAllowedShortTermOrderPlacementsInOneBlock == 0 { + panic("Failed to find MaxShortTermOrdersPerNBlocks with window 1.") + } + + oldShortTermOrderCancellationRateLimits := oldBlockRateLimitConfig.MaxShortTermOrderCancellationsPerNBlocks + for _, limit := range oldShortTermOrderCancellationRateLimits { + if limit.NumBlocks == 1 { + numAllowedShortTermOrderCancellationsInOneBlock += int(limit.Limit) + break + } + } + if numAllowedShortTermOrderCancellationsInOneBlock == 0 { + panic("Failed to find MaxShortTermOrdersPerNBlocks with window 1.") + } + + allowedNumShortTermPlaceAndCancelInFiveBlocks := + (numAllowedShortTermOrderPlacementsInOneBlock + numAllowedShortTermOrderCancellationsInOneBlock) * 5 + + blockRateLimitConfig := clobtypes.BlockRateLimitConfiguration{ + // Kept the same + MaxStatefulOrdersPerNBlocks: oldBlockRateLimitConfig.MaxStatefulOrdersPerNBlocks, + // Combine place and cancel, gate over 5 blocks to allow burst + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + { + NumBlocks: 5, + Limit: uint32(allowedNumShortTermPlaceAndCancelInFiveBlocks), + }, + }, + } + ctx.Logger().Info( + fmt.Sprintf( + "Attempting to set rate limiting config to newly combined config: %+v\n", + blockRateLimitConfig, + ), + ) + if err := clobKeeper.InitializeBlockRateLimit(ctx, blockRateLimitConfig); err != nil { + panic(fmt.Sprintf("failed to update the block rate limit configuration: %s", err)) + } + ctx.Logger().Info( + "Successfully upgraded block rate limit configuration to: %+v\n", + clobKeeper.GetBlockRateLimitConfiguration(ctx), + ) +} + func CreateUpgradeHandler( mm *module.Manager, configurator module.Configurator, perpetualsKeeper perptypes.PerpetualsKeeper, + clobKeeper clobtypes.ClobKeeper, ) upgradetypes.UpgradeHandler { return func(ctx context.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { sdkCtx := lib.UnwrapSDKContext(ctx, "app/upgrades") @@ -42,6 +111,9 @@ func CreateUpgradeHandler( // Set all perpetuals to cross market type perpetualsUpgrade(sdkCtx, perpetualsKeeper) + // Set block rate limit configuration + blockRateLimitConfigUpdate(sdkCtx, clobKeeper) + // TODO(TRA-93): Initialize `x/vault` module. return mm.RunMigrations(ctx, configurator, vm) diff --git a/protocol/app/vote_extensions/expected_keepers.go b/protocol/app/vote_extensions/expected_keepers.go new file mode 100644 index 0000000000..b472a201e9 --- /dev/null +++ b/protocol/app/vote_extensions/expected_keepers.go @@ -0,0 +1,18 @@ +package vote_extensions + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + oracletypes "github.com/skip-mev/slinky/pkg/types" + + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" +) + +// PricesKeeper is the expected interface for the x/price keeper used by the vote extension handlers +type PricesKeeper interface { + GetCurrencyPairFromID(ctx sdk.Context, id uint64) (cp oracletypes.CurrencyPair, found bool) + GetValidMarketPriceUpdates(ctx sdk.Context) *pricestypes.MsgUpdateMarketPrices + UpdateMarketPrices( + ctx sdk.Context, + updates []*pricestypes.MsgUpdateMarketPrices_MarketPrice, + ) (err error) +} diff --git a/protocol/app/vote_extensions/extend_vote_handler.go b/protocol/app/vote_extensions/extend_vote_handler.go new file mode 100644 index 0000000000..17c6af2417 --- /dev/null +++ b/protocol/app/vote_extensions/extend_vote_handler.go @@ -0,0 +1,62 @@ +package vote_extensions + +import ( + "fmt" + + cometabci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/dydxprotocol/v4-chain/protocol/app/process" + prices "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" +) + +// ExtendVoteHandler is a wrapper around the Slinky ExtendVoteHandler. This wrapper is responsible for +// applying the newest MarketPriceUpdates in a block so that the prices to be submitted in a vote extension are +// determined on the latest available information. +type ExtendVoteHandler struct { + SlinkyExtendVoteHandler sdk.ExtendVoteHandler + PricesTxDecoder process.UpdateMarketPriceTxDecoder + PricesKeeper PricesKeeper +} + +// ExtendVoteHandler returns a sdk.ExtendVoteHandler, responsible for the following: +// 1. Decoding the x/prices MsgUpdateMarketPrices in the current block - fail on errors +// 2. Validating the proposed MsgUpdateMarketPrices in accordance with the ProcessProposal check +// 3. Updating the market prices in the PricesKeeper so that the GetValidMarketPriceUpdates function returns the +// latest available market prices +// 4. Calling the Slinky ExtendVoteHandler to handle the rest of ExtendVote +// +// See https://github.com/skip-mev/slinky/blob/a5b1d3d3a2723e4746b5d588c512d7cc052dc0ff/abci/ve/vote_extension.go#L77 +// for the Slinky ExtendVoteHandler logic. +func (e *ExtendVoteHandler) ExtendVoteHandler() sdk.ExtendVoteHandler { + return func(ctx sdk.Context, req *cometabci.RequestExtendVote) (resp *cometabci.ResponseExtendVote, err error) { + // Decode the x/prices txn in the current block + updatePricesTx, err := e.PricesTxDecoder.DecodeUpdateMarketPricesTx(ctx, req.Txs) + if err != nil { + return nil, fmt.Errorf("DecodeMarketPricesTx failure %w", err) + } + + // ensure that the proposed MsgUpdateMarketPrices is valid in accordance w/ stateful information + // this check is equivalent to the check in ProcessProposal (indexPriceCache has not been updated) + err = updatePricesTx.Validate() + if err != nil { + return nil, fmt.Errorf("updatePricesTx.Validate failure %w", err) + } + + // Update the market prices in the PricesKeeper, so that the GetValidMarketPriceUpdates + // function returns the latest available market prices. + updateMarketPricesMsg, ok := updatePricesTx.GetMsg().(*prices.MsgUpdateMarketPrices) + if !ok { + return nil, fmt.Errorf("expected %s, got %T", "MsgUpdateMarketPrices", updateMarketPricesMsg) + } + + // Update the market prices in the PricesKeeper + err = e.PricesKeeper.UpdateMarketPrices(ctx, updateMarketPricesMsg.MarketPriceUpdates) + if err != nil { + return nil, fmt.Errorf("failed to update market prices in extend vote handler pre-slinky invocation %w", err) + } + + // Call the Slinky ExtendVoteHandler + return e.SlinkyExtendVoteHandler(ctx, req) + } +} diff --git a/protocol/app/vote_extensions/extend_vote_handler_test.go b/protocol/app/vote_extensions/extend_vote_handler_test.go new file mode 100644 index 0000000000..2b42e46841 --- /dev/null +++ b/protocol/app/vote_extensions/extend_vote_handler_test.go @@ -0,0 +1,133 @@ +package vote_extensions + +import ( + "fmt" + "testing" + + cometabci "github.com/cometbft/cometbft/abci/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/dydxprotocol/v4-chain/protocol/app/process" + "github.com/dydxprotocol/v4-chain/protocol/mocks" + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" +) + +func TestExtendVoteHandlerDeecodeMarketPricesFailure(t *testing.T) { + slinkyEvh := mocks.NewExtendVoteHandler(t) + pricesTxDecoder := mocks.NewUpdateMarketPriceTxDecoder(t) + pricesKeeper := mocks.NewPricesKeeper(t) + evh := ExtendVoteHandler{ + SlinkyExtendVoteHandler: slinkyEvh.Execute, + PricesTxDecoder: pricesTxDecoder, + PricesKeeper: pricesKeeper, + } + + pricesTxDecoder.On("DecodeUpdateMarketPricesTx", mock.Anything, mock.Anything).Return( + nil, fmt.Errorf("foobar")) + _, err := evh.ExtendVoteHandler()(sdk.Context{}, &cometabci.RequestExtendVote{Txs: make([][]byte, 0)}) + + require.ErrorContains(t, err, "DecodeMarketPricesTx failure foobar") + pricesTxDecoder.AssertExpectations(t) + pricesKeeper.AssertExpectations(t) + slinkyEvh.AssertExpectations(t) +} + +func TestExtendVoteHandlerUpdatePricesTxValidateFailure(t *testing.T) { + slinkyEvh := mocks.NewExtendVoteHandler(t) + pricesTxDecoder := mocks.NewUpdateMarketPriceTxDecoder(t) + pricesKeeper := mocks.NewPricesKeeper(t) + evh := ExtendVoteHandler{ + SlinkyExtendVoteHandler: slinkyEvh.Execute, + PricesTxDecoder: pricesTxDecoder, + PricesKeeper: pricesKeeper, + } + + pricesTxDecoder.On("DecodeUpdateMarketPricesTx", mock.Anything, mock.Anything).Return( + process.NewUpdateMarketPricesTx(sdk.Context{}, pricesKeeper, constants.InvalidMsgUpdateMarketPricesStateless), + nil) + _, err := evh.ExtendVoteHandler()(sdk.Context{}, &cometabci.RequestExtendVote{Txs: make([][]byte, 0)}) + + require.ErrorContains(t, err, "updatePricesTx.Validate failure") + pricesTxDecoder.AssertExpectations(t) + pricesKeeper.AssertExpectations(t) + slinkyEvh.AssertExpectations(t) +} + +func TestExtendVoteHandlerUpdateMarketPricesError(t *testing.T) { + slinkyEvh := mocks.NewExtendVoteHandler(t) + pricesTxDecoder := mocks.NewUpdateMarketPriceTxDecoder(t) + pricesKeeper := mocks.NewPricesKeeper(t) + evh := ExtendVoteHandler{ + SlinkyExtendVoteHandler: slinkyEvh.Execute, + PricesTxDecoder: pricesTxDecoder, + PricesKeeper: pricesKeeper, + } + + pricesTxDecoder.On("DecodeUpdateMarketPricesTx", mock.Anything, mock.Anything).Return( + process.NewUpdateMarketPricesTx(sdk.Context{}, pricesKeeper, constants.EmptyMsgUpdateMarketPrices), + nil) + pricesKeeper.On("PerformStatefulPriceUpdateValidation", mock.Anything, mock.Anything, mock.Anything). + Return(nil) + pricesKeeper.On("UpdateMarketPrices", mock.Anything, mock.Anything). + Return(fmt.Errorf("")) + _, err := evh.ExtendVoteHandler()(sdk.Context{}, &cometabci.RequestExtendVote{Txs: make([][]byte, 0)}) + + require.ErrorContains(t, err, "failed to update market prices in extend vote handler pre-slinky invocation") + pricesTxDecoder.AssertExpectations(t) + pricesKeeper.AssertExpectations(t) + slinkyEvh.AssertExpectations(t) +} + +func TestExtendVoteHandlerSlinkyFailure(t *testing.T) { + slinkyEvh := mocks.NewExtendVoteHandler(t) + pricesTxDecoder := mocks.NewUpdateMarketPriceTxDecoder(t) + pricesKeeper := mocks.NewPricesKeeper(t) + evh := ExtendVoteHandler{ + SlinkyExtendVoteHandler: slinkyEvh.Execute, + PricesTxDecoder: pricesTxDecoder, + PricesKeeper: pricesKeeper, + } + + pricesTxDecoder.On("DecodeUpdateMarketPricesTx", mock.Anything, mock.Anything).Return( + process.NewUpdateMarketPricesTx(sdk.Context{}, pricesKeeper, constants.EmptyMsgUpdateMarketPrices), + nil) + pricesKeeper.On("PerformStatefulPriceUpdateValidation", mock.Anything, mock.Anything, mock.Anything). + Return(nil) + pricesKeeper.On("UpdateMarketPrices", mock.Anything, mock.Anything).Return(nil) + slinkyEvh.On("Execute", mock.Anything, mock.Anything). + Return(&cometabci.ResponseExtendVote{}, fmt.Errorf("slinky failure")) + _, err := evh.ExtendVoteHandler()(sdk.Context{}, &cometabci.RequestExtendVote{Txs: make([][]byte, 0)}) + + require.ErrorContains(t, err, "slinky failure") + pricesTxDecoder.AssertExpectations(t) + pricesKeeper.AssertExpectations(t) + slinkyEvh.AssertExpectations(t) +} + +func TestExtendVoteHandlerSlinkySuccess(t *testing.T) { + slinkyEvh := mocks.NewExtendVoteHandler(t) + pricesTxDecoder := mocks.NewUpdateMarketPriceTxDecoder(t) + pricesKeeper := mocks.NewPricesKeeper(t) + evh := ExtendVoteHandler{ + SlinkyExtendVoteHandler: slinkyEvh.Execute, + PricesTxDecoder: pricesTxDecoder, + PricesKeeper: pricesKeeper, + } + + pricesTxDecoder.On("DecodeUpdateMarketPricesTx", mock.Anything, mock.Anything).Return( + process.NewUpdateMarketPricesTx(sdk.Context{}, pricesKeeper, constants.EmptyMsgUpdateMarketPrices), + nil) + pricesKeeper.On("PerformStatefulPriceUpdateValidation", mock.Anything, mock.Anything, mock.Anything). + Return(nil) + pricesKeeper.On("UpdateMarketPrices", mock.Anything, mock.Anything).Return(nil) + slinkyEvh.On("Execute", mock.Anything, mock.Anything). + Return(&cometabci.ResponseExtendVote{}, nil) + _, err := evh.ExtendVoteHandler()(sdk.Context{}, &cometabci.RequestExtendVote{Txs: make([][]byte, 0)}) + + require.NoError(t, err) + pricesTxDecoder.AssertExpectations(t) + pricesKeeper.AssertExpectations(t) + slinkyEvh.AssertExpectations(t) +} diff --git a/protocol/app/vote_extensions/oracle_client.go b/protocol/app/vote_extensions/oracle_client.go new file mode 100644 index 0000000000..07fc0e647f --- /dev/null +++ b/protocol/app/vote_extensions/oracle_client.go @@ -0,0 +1,77 @@ +package vote_extensions + +import ( + "context" + "fmt" + "strconv" + "time" + + sdk "github.com/cosmos/cosmos-sdk/types" + oracleservicetypes "github.com/skip-mev/slinky/service/servers/oracle/types" + "google.golang.org/grpc" +) + +// OraclePrices is an implementation of the Slinky OracleClient interface. +// This object is responsible for requesting prices from the x/prices module, and injecting those prices into the +// vote-extension. +// The +type OraclePrices struct { + PricesKeeper PricesKeeper +} + +// NewOraclePrices returns a new OracleClient object. +func NewOraclePrices(pricesKeeper PricesKeeper) *OraclePrices { + return &OraclePrices{ + PricesKeeper: pricesKeeper, + } +} + +// Start is a no-op. +func (o *OraclePrices) Start(_ context.Context) error { + return nil +} + +// Stop is a no-op. +func (o *OraclePrices) Stop() error { + return nil +} + +// Prices is called in ExtendVoteHandler to determine which Prices are put into the extended commit. +// This method is responsible for doing the following: +// 1. Get the latest prices from the x/prices module's indexPriceCache via GetValidMarketPriceUpdates +// 2. Translate the response from x/prices into a QueryPricesResponse, and return it. +// +// This method fails if: +// - The passed in context is not an sdk.Context +func (o *OraclePrices) Prices(ctx context.Context, + _ *oracleservicetypes.QueryPricesRequest, + _ ...grpc.CallOption) (*oracleservicetypes.QueryPricesResponse, error) { + sdkCtx, ok := ctx.(sdk.Context) + if !ok { + return nil, fmt.Errorf("OraclePrices was passed on non-sdk context object") + } + + // get the final prices to include in the vote-extension from the x/prices module + validUpdates := o.PricesKeeper.GetValidMarketPriceUpdates(sdkCtx) + if validUpdates == nil { + sdkCtx.Logger().Info("prices keeper returned no valid market price updates") + return nil, nil + } + sdkCtx.Logger().Info("prices keeper returned valid updates", "length", len(validUpdates.MarketPriceUpdates)) + + // translate price updates into oracle response + var outputResponse = &oracleservicetypes.QueryPricesResponse{ + Prices: make(map[string]string), + Timestamp: time.Now(), + } + for _, update := range validUpdates.MarketPriceUpdates { + mappedPair, found := o.PricesKeeper.GetCurrencyPairFromID(sdkCtx, uint64(update.GetMarketId())) + if found { + sdkCtx.Logger().Info("added currency pair", "pair", mappedPair.String()) + outputResponse.Prices[mappedPair.String()] = strconv.FormatUint(update.Price, 10) + } else { + sdkCtx.Logger().Info("failed to add currency pair", "pair", mappedPair.String()) + } + } + return outputResponse, nil +} diff --git a/protocol/app/vote_extensions/oracle_client_test.go b/protocol/app/vote_extensions/oracle_client_test.go new file mode 100644 index 0000000000..0bf1a3ed4f --- /dev/null +++ b/protocol/app/vote_extensions/oracle_client_test.go @@ -0,0 +1,101 @@ +package vote_extensions + +import ( + "context" + "testing" + + "cosmossdk.io/log" + sdk "github.com/cosmos/cosmos-sdk/types" + oracletypes "github.com/skip-mev/slinky/pkg/types" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" + + "github.com/dydxprotocol/v4-chain/protocol/mocks" + "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" +) + +func TestStartStopNoop(t *testing.T) { + cli := NewOraclePrices(nil) + + err := cli.Start(context.TODO()) + require.NoError(t, err) + err = cli.Stop() + require.NoError(t, err) +} + +func TestValidPriceResponse(t *testing.T) { + pk := mocks.NewPricesKeeper(t) + cli := NewOraclePrices(pk) + pk.On("GetValidMarketPriceUpdates", mock.Anything). + Return(&types.MsgUpdateMarketPrices{ + MarketPriceUpdates: []*types.MsgUpdateMarketPrices_MarketPrice{ + {MarketId: 2, Price: 1}, + }, + }).Once() + pk.On("GetCurrencyPairFromID", mock.Anything, mock.Anything). + Return( + oracletypes.CurrencyPair{Base: "FOO", Quote: "BAR"}, + true, + ).Once() + + _, err := cli.Prices(sdk.Context{}.WithLogger(log.NewNopLogger()), nil) + + require.NoError(t, err) + pk.AssertExpectations(t) +} + +func TestNonSdkContextFails(t *testing.T) { + pk := mocks.NewPricesKeeper(t) + cli := NewOraclePrices(pk) + + _, err := cli.Prices(context.TODO(), nil) + + require.Error(t, err) +} + +func TestEmptyUpdatesPasses(t *testing.T) { + pk := mocks.NewPricesKeeper(t) + cli := NewOraclePrices(pk) + pk.On("GetValidMarketPriceUpdates", mock.Anything). + Return(&types.MsgUpdateMarketPrices{ + MarketPriceUpdates: []*types.MsgUpdateMarketPrices_MarketPrice{}, + }).Once() + + _, err := cli.Prices(sdk.Context{}.WithLogger(log.NewNopLogger()), nil) + + require.NoError(t, err) + pk.AssertExpectations(t) +} + +func TestNilUpdatesPasses(t *testing.T) { + pk := mocks.NewPricesKeeper(t) + cli := NewOraclePrices(pk) + pk.On("GetValidMarketPriceUpdates", mock.Anything). + Return(nil).Once() + + _, err := cli.Prices(sdk.Context{}.WithLogger(log.NewNopLogger()), nil) + + require.NoError(t, err) + pk.AssertExpectations(t) +} + +func TestPairNotFoundNoOps(t *testing.T) { + pk := mocks.NewPricesKeeper(t) + cli := NewOraclePrices(pk) + pk.On("GetValidMarketPriceUpdates", mock.Anything). + Return(&types.MsgUpdateMarketPrices{ + MarketPriceUpdates: []*types.MsgUpdateMarketPrices_MarketPrice{ + {MarketId: 2, Price: 1}, + }, + }).Once() + pk.On("GetCurrencyPairFromID", mock.Anything, mock.Anything). + Return( + oracletypes.CurrencyPair{Base: "", Quote: ""}, + false, + ).Once() + + _, err := cli.Prices(sdk.Context{}.WithLogger(log.NewNopLogger()), nil) + + require.NoError(t, err) + pk.AssertExpectations(t) +} diff --git a/protocol/cmd/dydxprotocold/cmd/config.go b/protocol/cmd/dydxprotocold/cmd/config.go index 4124544c1b..422b37a9c6 100644 --- a/protocol/cmd/dydxprotocold/cmd/config.go +++ b/protocol/cmd/dydxprotocold/cmd/config.go @@ -54,6 +54,13 @@ func initAppConfig() (string, *DydxAppConfig) { appConfig := DydxAppConfig{ Config: *srvCfg, + Oracle: oracleconfig.AppConfig{ + Enabled: true, + OracleAddress: "localhost:8080", + ClientTimeout: time.Second * 2, + MetricsEnabled: false, + PrometheusServerAddress: "", + }, } // Enable telemetry. diff --git a/protocol/cmd/dydxprotocold/cmd/root.go b/protocol/cmd/dydxprotocold/cmd/root.go index a8e843f858..c4cae45379 100644 --- a/protocol/cmd/dydxprotocold/cmd/root.go +++ b/protocol/cmd/dydxprotocold/cmd/root.go @@ -387,6 +387,7 @@ func newApp( baseapp.SetIndexEvents(cast.ToStringSlice(appOpts.Get(server.FlagIndexEvents))), baseapp.SetSnapshot(snapshotStore, snapshotOptions), baseapp.SetIAVLCacheSize(int(cast.ToUint64(appOpts.Get(flagIAVLCacheSize)))), + baseapp.SetIAVLDisableFastNode(true), baseapp.SetChainID(chainID), ) } diff --git a/protocol/daemons/flags/flags.go b/protocol/daemons/flags/flags.go index 99dd8f804c..50ba9c4ea7 100644 --- a/protocol/daemons/flags/flags.go +++ b/protocol/daemons/flags/flags.go @@ -107,15 +107,16 @@ func GetDefaultDaemonFlags() DaemonFlags { QueryPageLimit: 1_000, }, Price: PriceFlags{ - Enabled: true, + Enabled: false, LoopDelayMs: 3_000, }, Slinky: SlinkyFlags{ AppConfig: oracleconfig.AppConfig{ + Enabled: true, OracleAddress: "localhost:8080", ClientTimeout: time.Second * 2, - MetricsEnabled: false, - PrometheusServerAddress: "", + MetricsEnabled: true, + PrometheusServerAddress: "0.0.0.0:8001", }, }, } diff --git a/protocol/daemons/liquidation/client/grpc_helper_test.go b/protocol/daemons/liquidation/client/grpc_helper_test.go index 2d164072ad..1b25ff48b8 100644 --- a/protocol/daemons/liquidation/client/grpc_helper_test.go +++ b/protocol/daemons/liquidation/client/grpc_helper_test.go @@ -411,6 +411,7 @@ func TestGetAllMarketPrices(t *testing.T) { MarketPrices: []pricestypes.MarketPrice{ constants.TestMarketPrices[2], constants.TestMarketPrices[3], + constants.TestMarketPrices[4], }, } mck.On("AllMarketPrices", mock.Anything, req2).Return(response2, nil) diff --git a/protocol/daemons/pricefeed/client/sub_task_runner.go b/protocol/daemons/pricefeed/client/sub_task_runner.go index 5a05453a53..bfa5f085e9 100644 --- a/protocol/daemons/pricefeed/client/sub_task_runner.go +++ b/protocol/daemons/pricefeed/client/sub_task_runner.go @@ -17,6 +17,7 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/daemons/pricefeed/client/handler" "github.com/dydxprotocol/v4-chain/protocol/daemons/pricefeed/client/types" pricefeedmetrics "github.com/dydxprotocol/v4-chain/protocol/daemons/pricefeed/metrics" + daemonlib "github.com/dydxprotocol/v4-chain/protocol/daemons/shared" "github.com/dydxprotocol/v4-chain/protocol/lib/metrics" gometrics "github.com/hashicorp/go-metrics" ) @@ -328,7 +329,7 @@ func RunMarketParamUpdaterTaskLoop( logger = logger.With(constants.SubmoduleLogKey, constants.MarketParamUpdaterSubmoduleName) // Query all market params from the query client. - getAllMarketsResponse, err := pricesQueryClient.AllMarketParams(ctx, &pricetypes.QueryAllMarketParamsRequest{}) + marketParams, err := daemonlib.AllPaginatedMarketParams(ctx, pricesQueryClient) if err != nil { var logMethod = logger.Info if isPastGracePeriod { @@ -352,9 +353,9 @@ func RunMarketParamUpdaterTaskLoop( } // Update shared, in-memory config with the latest market params. Report update success/failure via logging/metrics. - marketParamErrors, err := configs.UpdateMarkets(getAllMarketsResponse.MarketParams) + marketParamErrors, err := configs.UpdateMarkets(marketParams) - for _, marketParam := range getAllMarketsResponse.MarketParams { + for _, marketParam := range marketParams { outcome := metrics.Success // Mark this update as an error either if this market failed to update, or if all markets failed. diff --git a/protocol/daemons/shared/paginated_grpc_request.go b/protocol/daemons/shared/paginated_grpc_request.go new file mode 100644 index 0000000000..21e3480068 --- /dev/null +++ b/protocol/daemons/shared/paginated_grpc_request.go @@ -0,0 +1,64 @@ +package shared + +import ( + "context" + "github.com/cosmos/cosmos-sdk/types/query" + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" +) + +const ( + // PaginatedRequestLimit is the maximum number of entries that can be returned in a paginated request + PaginatedRequestLimit = 10000 +) + +// AllPaginatedMarketParams returns all MarketParams from the prices module, paginating through the results and +// returning a list of all the aggregated market-params. +func AllPaginatedMarketParams(ctx context.Context, client pricestypes.QueryClient) ([]pricestypes.MarketParam, error) { + mps := make([]pricestypes.MarketParam, 0) + + pq := func(ctx context.Context, req *query.PageRequest) (ResponseWithPagination, error) { + resp, err := client.AllMarketParams(ctx, &pricestypes.QueryAllMarketParamsRequest{ + Pagination: req, + }) + if err != nil { + return nil, err + } + + mps = append(mps, resp.MarketParams...) + return resp, nil + } + + if err := HandlePaginatedQuery(ctx, pq, &query.PageRequest{ + Limit: PaginatedRequestLimit, + }); err != nil { + return nil, err + } + + return mps, nil +} + +// ResponseWithPagination represents a response-type from a cosmos-module's GRPC service for entries that are paginated +type ResponseWithPagination interface { + GetPagination() *query.PageResponse +} + +// PaginatedQuery is a function type that represents a paginated query to a cosmos-module's GRPC service +type PaginatedQuery func(ctx context.Context, req *query.PageRequest) (ResponseWithPagination, error) + +func HandlePaginatedQuery(ctx context.Context, pq PaginatedQuery, initialPagination *query.PageRequest) error { + for { + // make the query + resp, err := pq(ctx, initialPagination) + if err != nil { + return err + } + + // break if there is no next-key + if resp.GetPagination() == nil || len(resp.GetPagination().NextKey) == 0 { + return nil + } + + // otherwise, update the next-key and continue + initialPagination.Key = resp.GetPagination().NextKey + } +} diff --git a/protocol/daemons/shared/paginated_grpc_request_test.go b/protocol/daemons/shared/paginated_grpc_request_test.go new file mode 100644 index 0000000000..888e4b156d --- /dev/null +++ b/protocol/daemons/shared/paginated_grpc_request_test.go @@ -0,0 +1,144 @@ +package shared_test + +import ( + "testing" + + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/query" + "github.com/dydxprotocol/v4-chain/protocol/daemons/shared" + "github.com/dydxprotocol/v4-chain/protocol/mocks" + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" + "github.com/stretchr/testify/require" +) + +var ( + nextKey1 = []byte("next-key-1") + nextKey2 = []byte("next-key-2") +) + +// tests that AllPaginatedMarketParams makes queries with the given next-key + returns the +// aggregated responses +func TestPaginatedGRPCRequests(t *testing.T) { + qc := mocks.NewQueryClient(t) + + marketParam1 := pricestypes.MarketParam{ + Id: 1, + Pair: "BTC-USDC", + } + + marketParam2 := pricestypes.MarketParam{ + Id: 2, + Pair: "ETH-USDC", + } + + marketParam3 := pricestypes.MarketParam{ + Id: 3, + Pair: "LINK-USDC", + } + + ctx := sdk.Context{} + + // ON ... + // a query that returns a single-market param with a next-key-1 on receiving a page-request + // with a limit of types.PaginatedRequestLimit + initialPagination := &query.PageRequest{ + Limit: shared.PaginatedRequestLimit, + } + qc.On("AllMarketParams", ctx, &pricestypes.QueryAllMarketParamsRequest{ + Pagination: initialPagination, + }).Return( + &pricestypes.QueryAllMarketParamsResponse{ + MarketParams: []pricestypes.MarketParam{marketParam1}, + Pagination: &query.PageResponse{ + NextKey: nextKey1, + }, + }, + nil, + ) + + // a query that returns a single-market param with a next-key-2 on receiving a page-request + // with a limit of types.PaginatedRequestLimit with the next-key-1 return marketParam2 and next-key-2 + qc.On("AllMarketParams", ctx, &pricestypes.QueryAllMarketParamsRequest{ + Pagination: &query.PageRequest{ + Limit: shared.PaginatedRequestLimit, + Key: nextKey1, + }, + }).Return( + &pricestypes.QueryAllMarketParamsResponse{ + MarketParams: []pricestypes.MarketParam{marketParam2}, + Pagination: &query.PageResponse{ + NextKey: nextKey2, + }, + }, + nil, + ) + + // a query that returns a single-market param with no next-key on receiving a page-request with a limit + // of types.PaginatedRequestLimit with the next-key-2 return marketParam3 and no next-key + qc.On("AllMarketParams", ctx, &pricestypes.QueryAllMarketParamsRequest{ + Pagination: &query.PageRequest{ + Limit: shared.PaginatedRequestLimit, + Key: nextKey2, + }, + }).Return( + &pricestypes.QueryAllMarketParamsResponse{ + MarketParams: []pricestypes.MarketParam{marketParam3}, + Pagination: &query.PageResponse{}, + }, + nil, + ) + + // ... THEN + // the function should return all the market-params + marketParams, err := shared.AllPaginatedMarketParams(ctx, qc) + require.NoError(t, err) + + require.Equal(t, []pricestypes.MarketParam{marketParam1, marketParam2, marketParam3}, marketParams) +} + +func TestPaginatedGRPCRequestsWithError(t *testing.T) { + qc := mocks.NewQueryClient(t) + + marketParam1 := pricestypes.MarketParam{ + Id: 1, + Pair: "BTC-USDC", + } + + ctx := sdk.Context{} + + // ON ... + // a query that returns a single-market param with a next-key-1 on receiving a page-request + // with a limit of types.PaginatedRequestLimit + initialPagination := &query.PageRequest{ + Limit: shared.PaginatedRequestLimit, + } + qc.On("AllMarketParams", ctx, &pricestypes.QueryAllMarketParamsRequest{ + Pagination: initialPagination, + }).Return( + &pricestypes.QueryAllMarketParamsResponse{ + MarketParams: []pricestypes.MarketParam{marketParam1}, + Pagination: &query.PageResponse{ + NextKey: nextKey1, + }, + }, + nil, + ) + + expErr := fmt.Errorf("error") + // a query that returns an error on receiving a page-request with a limit of types.PaginatedRequestLimit + next-key-1 + qc.On("AllMarketParams", ctx, &pricestypes.QueryAllMarketParamsRequest{ + Pagination: &query.PageRequest{ + Limit: shared.PaginatedRequestLimit, + Key: nextKey1, + }, + }).Return( + nil, + expErr, + ) + + // ... THEN + // the function should return the error + _, err := shared.AllPaginatedMarketParams(ctx, qc) + require.EqualError(t, err, expErr.Error()) +} diff --git a/protocol/daemons/slinky/client/client.go b/protocol/daemons/slinky/client/client.go index cbce91fe7d..eca04745c1 100644 --- a/protocol/daemons/slinky/client/client.go +++ b/protocol/daemons/slinky/client/client.go @@ -125,8 +125,9 @@ func (c *Client) RunMarketPairFetcher(ctx context.Context, appFlags appflags.Fla if err != nil { c.logger.Error("Failed to run fetch id mappings for slinky daemon", "error", err) c.ReportFailure(errors.Wrap(err, "failed to run FetchIdMappings for slinky daemon")) + } else { + c.ReportSuccess() } - c.ReportSuccess() case <-ctx.Done(): return } diff --git a/protocol/daemons/slinky/client/client_test.go b/protocol/daemons/slinky/client/client_test.go index e8d234f870..3c52eb339e 100644 --- a/protocol/daemons/slinky/client/client_test.go +++ b/protocol/daemons/slinky/client/client_test.go @@ -3,14 +3,13 @@ package client_test import ( "context" "net" - "sync" "testing" "time" "cosmossdk.io/log" "github.com/skip-mev/slinky/service/servers/oracle/types" "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" + "github.com/stretchr/testify/require" "google.golang.org/grpc" appflags "github.com/dydxprotocol/v4-chain/protocol/app/flags" @@ -23,48 +22,18 @@ import ( pricetypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" ) -func TestClientTestSuite(t *testing.T) { - suite.Run(t, &ClientTestSuite{}) -} - -type ClientTestSuite struct { - suite.Suite - daemonFlags daemonflags.DaemonFlags - appFlags appflags.Flags - grpcServer *grpc.Server - pricesMockQueryServer *mocks.QueryServer - wg sync.WaitGroup -} - -func (c *ClientTestSuite) SetupTest() { - // Setup grpc server. - c.daemonFlags = daemonflags.GetDefaultDaemonFlags() - c.appFlags = appflags.GetFlagValuesFromOptions(appoptions.GetDefaultTestAppOptions("", nil)) - c.grpcServer = grpc.NewServer() - - c.pricesMockQueryServer = &mocks.QueryServer{} - pricetypes.RegisterQueryServer(c.grpcServer, c.pricesMockQueryServer) - - c.wg.Add(1) - go func() { - defer c.wg.Done() - ls, err := net.Listen("tcp", c.appFlags.GrpcAddress) - c.Require().NoError(err) - _ = c.grpcServer.Serve(ls) - }() -} - -func (c *ClientTestSuite) TearDownTest() { - c.grpcServer.Stop() - c.wg.Wait() -} - -func (c *ClientTestSuite) TestClient() { +func TestClient(t *testing.T) { var cli *client.Client - slinky := mocks.NewOracleClient(c.T()) - logger := log.NewTestLogger(c.T()) + slinky := mocks.NewOracleClient(t) + logger := log.NewTestLogger(t) + + daemonFlags := daemonflags.GetDefaultDaemonFlags() + appFlags := appflags.GetFlagValuesFromOptions(appoptions.GetDefaultTestAppOptions("", nil)) - c.pricesMockQueryServer.On("AllMarketParams", mock.Anything, mock.Anything). + grpcServer := grpc.NewServer() + pricesMockQueryServer := &mocks.QueryServer{} + pricetypes.RegisterQueryServer(grpcServer, pricesMockQueryServer) + pricesMockQueryServer.On("AllMarketParams", mock.Anything, mock.Anything). Return( &pricetypes.QueryAllMarketParamsResponse{ MarketParams: []pricetypes.MarketParam{ @@ -74,33 +43,37 @@ func (c *ClientTestSuite) TestClient() { nil, ) - c.Run("services are all started and call their deps", func() { - slinky.On("Stop").Return(nil) - slinky.On("Start", mock.Anything).Return(nil).Once() - slinky.On("Prices", mock.Anything, mock.Anything). - Return(&types.QueryPricesResponse{ - Prices: map[string]string{ - "FOO/BAR": "100000000000", - }, - Timestamp: time.Now(), - }, nil) - client.SlinkyPriceFetchDelay = time.Millisecond - client.SlinkyMarketParamFetchDelay = time.Millisecond - cli = client.StartNewClient( - context.Background(), - slinky, - pricefeedtypes.NewMarketToExchangePrices(5*time.Second), - &daemontypes.GrpcClientImpl{}, - c.daemonFlags, - c.appFlags, - logger, - ) - waitTime := time.Second * 5 - c.Require().Eventually(func() bool { - return cli.HealthCheck() == nil - }, waitTime, time.Millisecond*500, "Slinky daemon failed to become healthy within %s", waitTime) - // Need to wait until a single c - cli.Stop() - c.Require().NoError(cli.HealthCheck()) - }) + defer grpcServer.Stop() + go func() { + ls, err := net.Listen("tcp", appFlags.GrpcAddress) + require.NoError(t, err) + err = grpcServer.Serve(ls) + require.NoError(t, err) + }() + + slinky.On("Stop").Return(nil) + slinky.On("Start", mock.Anything).Return(nil).Once() + slinky.On("Prices", mock.Anything, mock.Anything). + Return(&types.QueryPricesResponse{ + Prices: map[string]string{ + "FOO/BAR": "100000000000", + }, + Timestamp: time.Now(), + }, nil) + client.SlinkyPriceFetchDelay = time.Millisecond + client.SlinkyMarketParamFetchDelay = time.Millisecond + cli = client.StartNewClient( + context.Background(), + slinky, + pricefeedtypes.NewMarketToExchangePrices(5*time.Second), + &daemontypes.GrpcClientImpl{}, + daemonFlags, + appFlags, + logger, + ) + waitTime := time.Second * 5 + require.Eventually(t, func() bool { + return cli.HealthCheck() == nil + }, waitTime, time.Millisecond*500, "Slinky daemon failed to become healthy within %s", waitTime) + cli.Stop() } diff --git a/protocol/daemons/slinky/client/market_pair_fetcher.go b/protocol/daemons/slinky/client/market_pair_fetcher.go index c80169304a..e618231ee0 100644 --- a/protocol/daemons/slinky/client/market_pair_fetcher.go +++ b/protocol/daemons/slinky/client/market_pair_fetcher.go @@ -8,12 +8,12 @@ import ( "cosmossdk.io/log" "google.golang.org/grpc" - oracletypes "github.com/skip-mev/slinky/x/oracle/types" - appflags "github.com/dydxprotocol/v4-chain/protocol/app/flags" + daemonlib "github.com/dydxprotocol/v4-chain/protocol/daemons/shared" daemontypes "github.com/dydxprotocol/v4-chain/protocol/daemons/types" "github.com/dydxprotocol/v4-chain/protocol/lib/slinky" pricetypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" + slinkytypes "github.com/skip-mev/slinky/pkg/types" ) // MarketPairFetcher is a lightweight process run in a goroutine by the slinky client. @@ -23,7 +23,7 @@ import ( type MarketPairFetcher interface { Start(context.Context, appflags.Flags, daemontypes.GrpcClient) error Stop() - GetIDForPair(oracletypes.CurrencyPair) (uint32, error) + GetIDForPair(slinkytypes.CurrencyPair) (uint32, error) FetchIdMappings(context.Context) error } @@ -34,14 +34,14 @@ type MarketPairFetcherImpl struct { PricesQueryClient pricetypes.QueryClient // compatMappings stores a mapping between CurrencyPair and the corresponding market(param|price) ID - compatMappings map[oracletypes.CurrencyPair]uint32 + compatMappings map[slinkytypes.CurrencyPair]uint32 compatMu sync.RWMutex } func NewMarketPairFetcher(logger log.Logger) MarketPairFetcher { return &MarketPairFetcherImpl{ Logger: logger, - compatMappings: make(map[oracletypes.CurrencyPair]uint32), + compatMappings: make(map[slinkytypes.CurrencyPair]uint32), } } @@ -73,7 +73,7 @@ func (m *MarketPairFetcherImpl) Stop() { // GetIDForPair returns the ID corresponding to the currency pair in the x/prices module. // If the currency pair is not found it will return an error. -func (m *MarketPairFetcherImpl) GetIDForPair(cp oracletypes.CurrencyPair) (uint32, error) { +func (m *MarketPairFetcherImpl) GetIDForPair(cp slinkytypes.CurrencyPair) (uint32, error) { var id uint32 m.compatMu.RLock() defer m.compatMu.RUnlock() @@ -88,12 +88,13 @@ func (m *MarketPairFetcherImpl) GetIDForPair(cp oracletypes.CurrencyPair) (uint3 // CurrencyPair and MarketParam ID. func (m *MarketPairFetcherImpl) FetchIdMappings(ctx context.Context) error { // fetch all market params - resp, err := m.PricesQueryClient.AllMarketParams(ctx, &pricetypes.QueryAllMarketParamsRequest{}) + marketParams, err := daemonlib.AllPaginatedMarketParams(ctx, m.PricesQueryClient) if err != nil { return err } - var compatMappings = make(map[oracletypes.CurrencyPair]uint32, len(resp.MarketParams)) - for _, mp := range resp.MarketParams { + + var compatMappings = make(map[slinkytypes.CurrencyPair]uint32, len(marketParams)) + for _, mp := range marketParams { cp, err := slinky.MarketPairToCurrencyPair(mp.Pair) if err != nil { return err diff --git a/protocol/daemons/slinky/client/market_pair_fetcher_test.go b/protocol/daemons/slinky/client/market_pair_fetcher_test.go index 18c511ec82..34cacb3967 100644 --- a/protocol/daemons/slinky/client/market_pair_fetcher_test.go +++ b/protocol/daemons/slinky/client/market_pair_fetcher_test.go @@ -9,7 +9,7 @@ import ( "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" - slinkytypes "github.com/skip-mev/slinky/x/oracle/types" + slinkytypes "github.com/skip-mev/slinky/pkg/types" "github.com/dydxprotocol/v4-chain/protocol/daemons/slinky/client" "github.com/dydxprotocol/v4-chain/protocol/mocks" diff --git a/protocol/daemons/slinky/client/price_fetcher.go b/protocol/daemons/slinky/client/price_fetcher.go index 32a663e5ad..d2258ddd2f 100644 --- a/protocol/daemons/slinky/client/price_fetcher.go +++ b/protocol/daemons/slinky/client/price_fetcher.go @@ -5,9 +5,9 @@ import ( "strconv" "cosmossdk.io/log" + slinkytypes "github.com/skip-mev/slinky/pkg/types" oracleclient "github.com/skip-mev/slinky/service/clients/oracle" "github.com/skip-mev/slinky/service/servers/oracle/types" - oracletypes "github.com/skip-mev/slinky/x/oracle/types" "github.com/dydxprotocol/v4-chain/protocol/daemons/pricefeed/api" pricefeedtypes "github.com/dydxprotocol/v4-chain/protocol/daemons/server/types/pricefeed" @@ -70,7 +70,7 @@ func (p *PriceFetcherImpl) FetchPrices(ctx context.Context) error { var updates []*api.MarketPriceUpdate for currencyPairString, priceString := range slinkyResponse.Prices { // convert currency-pair string (index) into currency-pair object - currencyPair, err := oracletypes.CurrencyPairFromString(currencyPairString) + currencyPair, err := slinkytypes.CurrencyPairFromString(currencyPairString) if err != nil { return err } diff --git a/protocol/daemons/slinky/client/price_fetcher_test.go b/protocol/daemons/slinky/client/price_fetcher_test.go index 647d6a544d..4a9d7f21d8 100644 --- a/protocol/daemons/slinky/client/price_fetcher_test.go +++ b/protocol/daemons/slinky/client/price_fetcher_test.go @@ -3,90 +3,28 @@ package client_test import ( "context" "fmt" - "net" - "sync" "testing" "time" "cosmossdk.io/log" - "github.com/stretchr/testify/mock" - "github.com/stretchr/testify/suite" - "google.golang.org/grpc" - "github.com/skip-mev/slinky/service/servers/oracle/types" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" - appflags "github.com/dydxprotocol/v4-chain/protocol/app/flags" - daemonflags "github.com/dydxprotocol/v4-chain/protocol/daemons/flags" pricefeed_types "github.com/dydxprotocol/v4-chain/protocol/daemons/pricefeed/types" - daemonserver "github.com/dydxprotocol/v4-chain/protocol/daemons/server" pricefeedserver_types "github.com/dydxprotocol/v4-chain/protocol/daemons/server/types/pricefeed" "github.com/dydxprotocol/v4-chain/protocol/daemons/slinky/client" - daemontypes "github.com/dydxprotocol/v4-chain/protocol/daemons/types" "github.com/dydxprotocol/v4-chain/protocol/mocks" - "github.com/dydxprotocol/v4-chain/protocol/testutil/appoptions" ) -func TestPriceFetcherTestSuite(t *testing.T) { - suite.Run(t, &PriceFetcherTestSuite{}) -} - -type PriceFetcherTestSuite struct { - suite.Suite - daemonFlags daemonflags.DaemonFlags - appFlags appflags.Flags - daemonServer *daemonserver.Server - pricesGrpcServer *grpc.Server - wg sync.WaitGroup -} - -func (p *PriceFetcherTestSuite) SetupTest() { - // Setup daemon and grpc servers. - p.daemonFlags = daemonflags.GetDefaultDaemonFlags() - p.appFlags = appflags.GetFlagValuesFromOptions(appoptions.GetDefaultTestAppOptions("", nil)) - - // Configure and run daemon server. - p.daemonServer = daemonserver.NewServer( - log.NewNopLogger(), - grpc.NewServer(), - &daemontypes.FileHandlerImpl{}, - p.daemonFlags.Shared.SocketAddress, - ) - p.daemonServer.WithPriceFeedMarketToExchangePrices( - pricefeedserver_types.NewMarketToExchangePrices(5 * time.Second), - ) - - p.wg.Add(1) - go func() { - defer p.wg.Done() - p.daemonServer.Start() - }() - - // Create a gRPC server running on the default port and attach the mock prices query response. - p.pricesGrpcServer = grpc.NewServer() - - p.wg.Add(1) - go func() { - defer p.wg.Done() - ls, err := net.Listen("tcp", p.appFlags.GrpcAddress) - p.Require().NoError(err) - _ = p.pricesGrpcServer.Serve(ls) - }() -} - -func (p *PriceFetcherTestSuite) TearDownTest() { - p.daemonServer.Stop() - p.pricesGrpcServer.Stop() - p.wg.Wait() -} - -func (p *PriceFetcherTestSuite) TestPriceFetcher() { - logger := log.NewTestLogger(p.T()) - mpf := mocks.NewMarketPairFetcher(p.T()) - slinky := mocks.NewOracleClient(p.T()) +func TestPriceFetcher(t *testing.T) { + logger := log.NewTestLogger(t) + mpf := mocks.NewMarketPairFetcher(t) + slinky := mocks.NewOracleClient(t) slinky.On("Stop").Return(nil) var fetcher client.PriceFetcher - p.Run("fetches prices on valid inputs", func() { + t.Run("fetches prices on valid inputs", func(t *testing.T) { slinky.On("Start", mock.Anything).Return(nil).Once() slinky.On("Prices", mock.Anything, mock.Anything). Return(&types.QueryPricesResponse{ @@ -103,12 +41,12 @@ func (p *PriceFetcherTestSuite) TestPriceFetcher() { slinky, logger, ) - p.Require().NoError(fetcher.Start(context.Background())) - p.Require().NoError(fetcher.FetchPrices(context.Background())) + require.NoError(t, fetcher.Start(context.Background())) + require.NoError(t, fetcher.FetchPrices(context.Background())) fetcher.Stop() }) - p.Run("errors on slinky.Prices failure", func() { + t.Run("errors on slinky.Prices failure", func(t *testing.T) { slinky.On("Start", mock.Anything).Return(nil).Once() slinky.On("Prices", mock.Anything, mock.Anything). Return(&types.QueryPricesResponse{}, fmt.Errorf("foobar")).Once() @@ -119,12 +57,12 @@ func (p *PriceFetcherTestSuite) TestPriceFetcher() { logger, ) - p.Require().NoError(fetcher.Start(context.Background())) - p.Require().Errorf(fetcher.FetchPrices(context.Background()), "foobar") + require.NoError(t, fetcher.Start(context.Background())) + require.Errorf(t, fetcher.FetchPrices(context.Background()), "foobar") fetcher.Stop() }) - p.Run("errors on slinky.Prices returning invalid currency pairs", func() { + t.Run("errors on slinky.Prices returning invalid currency pairs", func(t *testing.T) { slinky.On("Start", mock.Anything).Return(nil).Once() slinky.On("Prices", mock.Anything, mock.Anything). Return(&types.QueryPricesResponse{ @@ -139,12 +77,12 @@ func (p *PriceFetcherTestSuite) TestPriceFetcher() { logger, ) - p.Require().NoError(fetcher.Start(context.Background())) - p.Require().Errorf(fetcher.FetchPrices(context.Background()), "incorrectly formatted CurrencyPair") + require.NoError(t, fetcher.Start(context.Background())) + require.Errorf(t, fetcher.FetchPrices(context.Background()), "incorrectly formatted CurrencyPair") fetcher.Stop() }) - p.Run("no-ops on marketPairFetcher currency pair not found", func() { + t.Run("no-ops on marketPairFetcher currency pair not found", func(t *testing.T) { slinky.On("Start", mock.Anything).Return(nil).Once() slinky.On("Prices", mock.Anything, mock.Anything). Return(&types.QueryPricesResponse{ @@ -161,12 +99,12 @@ func (p *PriceFetcherTestSuite) TestPriceFetcher() { slinky, logger, ) - p.Require().NoError(fetcher.Start(context.Background())) - p.Require().NoError(fetcher.FetchPrices(context.Background())) + require.NoError(t, fetcher.Start(context.Background())) + require.NoError(t, fetcher.FetchPrices(context.Background())) fetcher.Stop() }) - p.Run("continues on non-parsable price data", func() { + t.Run("continues on non-parsable price data", func(t *testing.T) { slinky.On("Start", mock.Anything).Return(nil).Once() slinky.On("Prices", mock.Anything, mock.Anything). Return(&types.QueryPricesResponse{ @@ -183,12 +121,12 @@ func (p *PriceFetcherTestSuite) TestPriceFetcher() { slinky, logger, ) - p.Require().NoError(fetcher.Start(context.Background())) - p.Require().NoError(fetcher.FetchPrices(context.Background())) + require.NoError(t, fetcher.Start(context.Background())) + require.NoError(t, fetcher.FetchPrices(context.Background())) fetcher.Stop() }) - p.Run("no-ops on empty price response", func() { + t.Run("no-ops on empty price response", func(t *testing.T) { slinky.On("Start", mock.Anything).Return(nil).Once() slinky.On("Prices", mock.Anything, mock.Anything). Return(&types.QueryPricesResponse{ @@ -202,8 +140,8 @@ func (p *PriceFetcherTestSuite) TestPriceFetcher() { slinky, logger, ) - p.Require().NoError(fetcher.Start(context.Background())) - p.Require().NoError(fetcher.FetchPrices(context.Background())) + require.NoError(t, fetcher.Start(context.Background())) + require.NoError(t, fetcher.FetchPrices(context.Background())) fetcher.Stop() }) } diff --git a/protocol/daemons/slinky/config/market.json b/protocol/daemons/slinky/config/market.json index d4433a03a2..82e9654b73 100644 --- a/protocol/daemons/slinky/config/market.json +++ b/protocol/daemons/slinky/config/market.json @@ -424,6 +424,14 @@ "decimals": 8, "min_provider_count": 1 }, + "MOG/USD": { + "currency_pair": { + "Base": "MOG", + "Quote": "USD" + }, + "decimals": 18, + "min_provider_count": 1 + }, "NEAR/USD": { "currency_pair": { "Base": "NEAR", @@ -694,19 +702,23 @@ "ADA/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "ADA-USD" + "name": "KrakenAPI", + "off_chain_ticker": "ADAUSD" + }, + { + "name": "Kraken", + "off_chain_ticker": "ADA/USD" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "ADA-USD" }, { - "name": "kraken", - "off_chain_ticker": "ADA/USD" + "name": "CoinbasePro", + "off_chain_ticker": "ADA-USD" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "ADA-USD" } ] @@ -714,15 +726,15 @@ "ADA/USDC": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "ADA-USDC" + "name": "Mexc", + "off_chain_ticker": "ADAUSDC" }, { - "name": "mexc", - "off_chain_ticker": "ADAUSDC" + "name": "Kucoin", + "off_chain_ticker": "ADA-USDC" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "ADA-USDC" } ] @@ -730,63 +742,67 @@ "ADA/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "ADA-USDT" + "name": "KrakenAPI", + "off_chain_ticker": "ADAUSDT" }, { - "name": "mexc", - "off_chain_ticker": "ADAUSDT" + "name": "Gate", + "off_chain_ticker": "ADA_USDT" }, { - "name": "bybit", - "off_chain_ticker": "ADAUSDT" + "name": "Huobi", + "off_chain_ticker": "adausdt" }, { - "name": "gate.io", - "off_chain_ticker": "ADA_USDT" + "name": "Mexc", + "off_chain_ticker": "ADAUSDT" }, { - "name": "huobi", - "off_chain_ticker": "adausdt" + "name": "Binance", + "off_chain_ticker": "ADAUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "ADA-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "ADAUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "ADA-USDT" } ] }, "APE/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "APE-USD" + "name": "Kraken", + "off_chain_ticker": "APE/USD" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "APE-USD" }, { - "name": "kraken", - "off_chain_ticker": "APE/USD" + "name": "CoinbasePro", + "off_chain_ticker": "APE-USD" } ] }, "APE/USDC": { "providers": [ { - "name": "kucoin", + "name": "CoinbaseProAPI", "off_chain_ticker": "APE-USDC" }, { - "name": "coinbase", + "name": "Kucoin", "off_chain_ticker": "APE-USDC" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "APE-USDC" } ] @@ -794,43 +810,51 @@ "APE/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "APE-USDT" - }, - { - "name": "mexc", + "name": "KrakenAPI", "off_chain_ticker": "APEUSDT" }, { - "name": "coinbase_websocket", + "name": "CoinbaseProAPI", "off_chain_ticker": "APE-USDT" }, { - "name": "gate.io", + "name": "Gate", "off_chain_ticker": "APE_USDT" }, { - "name": "coinbase", + "name": "Mexc", + "off_chain_ticker": "APEUSDT" + }, + { + "name": "Binance", + "off_chain_ticker": "APEUSDT" + }, + { + "name": "Kucoin", "off_chain_ticker": "APE-USDT" }, { - "name": "okx", + "name": "CoinbasePro", "off_chain_ticker": "APE-USDT" }, { - "name": "binance", - "off_chain_ticker": "APEUSDT" + "name": "Okx", + "off_chain_ticker": "APE-USDT" } ] }, "APT/USD": { "providers": [ { - "name": "coinbase_websocket", + "name": "KrakenAPI", + "off_chain_ticker": "APTUSD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "APT-USD" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "APT-USD" } ] @@ -838,7 +862,7 @@ "APT/USDC": { "providers": [ { - "name": "okx", + "name": "Okx", "off_chain_ticker": "APT-USDC" } ] @@ -846,43 +870,47 @@ "APT/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "APT-USDT" + "name": "Gate", + "off_chain_ticker": "APT_USDT" }, { - "name": "mexc", - "off_chain_ticker": "APTUSDT" + "name": "Huobi", + "off_chain_ticker": "aptusdt" }, { - "name": "bybit", + "name": "Mexc", "off_chain_ticker": "APTUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "APT_USDT" - }, - { - "name": "huobi", - "off_chain_ticker": "aptusdt" + "name": "Binance", + "off_chain_ticker": "APTUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "APT-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "APTUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "APT-USDT" } ] }, "ARB/USD": { "providers": [ { - "name": "coinbase_websocket", + "name": "KrakenAPI", + "off_chain_ticker": "ARBUSD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "ARB-USD" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "ARB-USD" } ] @@ -890,59 +918,63 @@ "ARB/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "ARB-USDT" + "name": "Gate", + "off_chain_ticker": "ARB_USDT" }, { - "name": "mexc", - "off_chain_ticker": "ARBUSDT" + "name": "Huobi", + "off_chain_ticker": "arbusdt" }, { - "name": "bybit", + "name": "Mexc", "off_chain_ticker": "ARBUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "ARB_USDT" - }, - { - "name": "huobi", - "off_chain_ticker": "arbusdt" + "name": "Binance", + "off_chain_ticker": "ARBUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "ARB-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "ARBUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "ARB-USDT" } ] }, "ATOM/USD": { "providers": [ { - "name": "coingecko", + "name": "Coingecko", "off_chain_ticker": "cosmos/usd" }, { - "name": "coinbase_websocket", - "off_chain_ticker": "ATOM-USD" + "name": "KrakenAPI", + "off_chain_ticker": "ATOMUSD" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "ATOMUSD-PERP" + "name": "Kraken", + "off_chain_ticker": "ATOM/USD" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "ATOM-USD" }, { - "name": "kraken", - "off_chain_ticker": "ATOM/USD" + "name": "CoinbasePro", + "off_chain_ticker": "ATOM-USD" + }, + { + "name": "CryptoCom", + "off_chain_ticker": "ATOMUSD-PERP" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "ATOM-USD" } ] @@ -950,19 +982,19 @@ "ATOM/USDC": { "providers": [ { - "name": "kucoin", + "name": "CoinbaseProAPI", "off_chain_ticker": "ATOM-USDC" }, { - "name": "mexc", + "name": "Mexc", "off_chain_ticker": "ATOMUSDC" }, { - "name": "coinbase", + "name": "Kucoin", "off_chain_ticker": "ATOM-USDC" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "ATOM-USDC" } ] @@ -970,71 +1002,79 @@ "ATOM/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "ATOM-USDT" - }, - { - "name": "mexc", + "name": "KrakenAPI", "off_chain_ticker": "ATOMUSDT" }, { - "name": "coinbase_websocket", + "name": "CoinbaseProAPI", "off_chain_ticker": "ATOM-USDT" }, { - "name": "bybit", - "off_chain_ticker": "ATOMUSDT" + "name": "Gate", + "off_chain_ticker": "ATOM_USDT" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "ATOM_USDT" + "name": "Huobi", + "off_chain_ticker": "atomusdt" }, { - "name": "gate.io", - "off_chain_ticker": "ATOM_USDT" + "name": "Mexc", + "off_chain_ticker": "ATOMUSDT" + }, + { + "name": "Binance", + "off_chain_ticker": "ATOMUSDT" }, { - "name": "coinbase", + "name": "Kucoin", "off_chain_ticker": "ATOM-USDT" }, { - "name": "huobi", - "off_chain_ticker": "atomusdt" + "name": "Bybit", + "off_chain_ticker": "ATOMUSDT" }, { - "name": "okx", + "name": "CoinbasePro", "off_chain_ticker": "ATOM-USDT" }, { - "name": "binance", - "off_chain_ticker": "ATOMUSDT" + "name": "CryptoCom", + "off_chain_ticker": "ATOM_USDT" + }, + { + "name": "Okx", + "off_chain_ticker": "ATOM-USDT" } ] }, "AVAX/USD": { "providers": [ { - "name": "bitstamp", - "off_chain_ticker": "avaxusd" + "name": "KrakenAPI", + "off_chain_ticker": "AVAXUSD" }, { - "name": "coinbase_websocket", + "name": "Kraken", + "off_chain_ticker": "AVAX/USD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "AVAX-USD" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "AVAXUSD-PERP" + "name": "Bitstamp", + "off_chain_ticker": "avaxusd" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "AVAX-USD" }, { - "name": "kraken", - "off_chain_ticker": "AVAX/USD" + "name": "CryptoCom", + "off_chain_ticker": "AVAXUSD-PERP" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "AVAX-USD" } ] @@ -1042,23 +1082,23 @@ "AVAX/USDC": { "providers": [ { - "name": "kucoin", + "name": "CoinbaseProAPI", "off_chain_ticker": "AVAX-USDC" }, { - "name": "mexc", + "name": "Mexc", "off_chain_ticker": "AVAXUSDC" }, { - "name": "bybit", - "off_chain_ticker": "AVAXUSDC" + "name": "Kucoin", + "off_chain_ticker": "AVAX-USDC" }, { - "name": "coinbase", - "off_chain_ticker": "AVAX-USDC" + "name": "Bybit", + "off_chain_ticker": "AVAXUSDC" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "AVAX-USDC" } ] @@ -1066,131 +1106,143 @@ "AVAX/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "AVAX-USDT" + "name": "KrakenAPI", + "off_chain_ticker": "AVAXUSDT" }, { - "name": "mexc", - "off_chain_ticker": "AVAXUSDT" + "name": "Kraken", + "off_chain_ticker": "AVAX/USDT" }, { - "name": "coinbase_websocket", + "name": "CoinbaseProAPI", "off_chain_ticker": "AVAX-USDT" }, { - "name": "bybit", - "off_chain_ticker": "AVAXUSDT" + "name": "Gate", + "off_chain_ticker": "AVAX_USDT" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "AVAX_USDT" + "name": "Huobi", + "off_chain_ticker": "avaxusdt" }, { - "name": "gate.io", - "off_chain_ticker": "AVAX_USDT" + "name": "Mexc", + "off_chain_ticker": "AVAXUSDT" }, { - "name": "coinbase", - "off_chain_ticker": "AVAX-USDT" + "name": "Binance", + "off_chain_ticker": "AVAXUSDT" }, { - "name": "huobi", - "off_chain_ticker": "avaxusdt" + "name": "Kucoin", + "off_chain_ticker": "AVAX-USDT" }, { - "name": "kraken", - "off_chain_ticker": "AVAX/USDT" + "name": "Bybit", + "off_chain_ticker": "AVAXUSDT" }, { - "name": "okx", + "name": "CoinbasePro", "off_chain_ticker": "AVAX-USDT" }, { - "name": "binance", - "off_chain_ticker": "AVAXUSDT" + "name": "CryptoCom", + "off_chain_ticker": "AVAX_USDT" + }, + { + "name": "Okx", + "off_chain_ticker": "AVAX-USDT" } ] }, "BCH/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "BCH-USD" + "name": "KrakenAPI", + "off_chain_ticker": "BCHUSD" + }, + { + "name": "Kraken", + "off_chain_ticker": "BCH/USD" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "BCH-USD" }, { - "name": "kraken", - "off_chain_ticker": "BCH/USD" + "name": "CoinbasePro", + "off_chain_ticker": "BCH-USD" } ] }, "BCH/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "BCH-USDT" + "name": "KrakenAPI", + "off_chain_ticker": "BCHUSDT" }, { - "name": "mexc", - "off_chain_ticker": "BCHUSDT" + "name": "Gate", + "off_chain_ticker": "BCH_USDT" }, { - "name": "bybit", - "off_chain_ticker": "BCHUSDT" + "name": "Huobi", + "off_chain_ticker": "bchusdt" }, { - "name": "gate.io", - "off_chain_ticker": "BCH_USDT" + "name": "Mexc", + "off_chain_ticker": "BCHUSDT" }, { - "name": "huobi", - "off_chain_ticker": "bchusdt" + "name": "Binance", + "off_chain_ticker": "BCHUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "BCH-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "BCHUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "BCH-USDT" } ] }, "BLUR/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "BLUR-USD" + "name": "Kraken", + "off_chain_ticker": "BLUR/USD" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "BLUR-USD" }, { - "name": "kraken", - "off_chain_ticker": "BLUR/USD" + "name": "CoinbasePro", + "off_chain_ticker": "BLUR-USD" } ] }, "BLUR/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "BLUR-USDT" + "name": "Gate", + "off_chain_ticker": "BLUR_USDT" }, { - "name": "mexc", + "name": "Mexc", "off_chain_ticker": "BLURUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "BLUR_USDT" + "name": "Kucoin", + "off_chain_ticker": "BLUR-USDT" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "BLUR-USDT" } ] @@ -1198,35 +1250,39 @@ "BTC/USD": { "providers": [ { - "name": "bitstamp", - "off_chain_ticker": "btcusd" + "name": "Coingecko", + "off_chain_ticker": "bitcoin/usd" }, { - "name": "coingecko", - "off_chain_ticker": "bitcoin/usd" + "name": "KrakenAPI", + "off_chain_ticker": "XXBTZUSD" + }, + { + "name": "Kraken", + "off_chain_ticker": "XBT/USD" }, { - "name": "coinbase_websocket", + "name": "CoinbaseProAPI", "off_chain_ticker": "BTC-USD" }, { - "name": "bitfinex", + "name": "Bitfinex", "off_chain_ticker": "BTCUSD" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "BTCUSD-PERP" + "name": "Bitstamp", + "off_chain_ticker": "btcusd" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "BTC-USD" }, { - "name": "kraken", - "off_chain_ticker": "XBT/USD" + "name": "CryptoCom", + "off_chain_ticker": "BTCUSD-PERP" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "BTC-USD" } ] @@ -1234,283 +1290,319 @@ "BTC/USDC": { "providers": [ { - "name": "bitstamp", - "off_chain_ticker": "btcusdc" + "name": "KrakenAPI", + "off_chain_ticker": "XBTUSDC" + }, + { + "name": "Kraken", + "off_chain_ticker": "XBT/USDC" }, { - "name": "kucoin", + "name": "CoinbaseProAPI", "off_chain_ticker": "BTC-USDC" }, { - "name": "mexc", + "name": "Huobi", + "off_chain_ticker": "btcusdc" + }, + { + "name": "Mexc", "off_chain_ticker": "BTCUSDC" }, { - "name": "bybit", + "name": "Binance", "off_chain_ticker": "BTCUSDC" }, { - "name": "coinbase", + "name": "Kucoin", "off_chain_ticker": "BTC-USDC" }, { - "name": "huobi", + "name": "Bitstamp", "off_chain_ticker": "btcusdc" }, { - "name": "kraken", - "off_chain_ticker": "XBT/USDC" + "name": "Bybit", + "off_chain_ticker": "BTCUSDC" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "BTC-USDC" - }, - { - "name": "binance", - "off_chain_ticker": "BTCUSDC" } ] }, "BTC/USDT": { "providers": [ { - "name": "bitstamp", - "off_chain_ticker": "btcusdt" + "name": "KrakenAPI", + "off_chain_ticker": "XBTUSDT" }, { - "name": "kucoin", - "off_chain_ticker": "BTC-USDT" + "name": "Kraken", + "off_chain_ticker": "XBT/USDT" }, { - "name": "mexc", - "off_chain_ticker": "BTCUSDT" + "name": "CoinbaseProAPI", + "off_chain_ticker": "BTC-USDT" }, { - "name": "coinbase_websocket", - "off_chain_ticker": "BTC-USDT" + "name": "Gate", + "off_chain_ticker": "BTC_USDT" }, { - "name": "bybit", - "off_chain_ticker": "BTCUSDT" + "name": "Huobi", + "off_chain_ticker": "btcusdt" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "BTC_USDT" + "name": "Mexc", + "off_chain_ticker": "BTCUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "BTC_USDT" + "name": "Binance", + "off_chain_ticker": "BTCUSDT" }, { - "name": "coinbase", + "name": "Kucoin", "off_chain_ticker": "BTC-USDT" }, { - "name": "huobi", + "name": "Bitstamp", "off_chain_ticker": "btcusdt" }, { - "name": "kraken", - "off_chain_ticker": "XBT/USDT" + "name": "Bybit", + "off_chain_ticker": "BTCUSDT" }, { - "name": "okx", + "name": "CoinbasePro", "off_chain_ticker": "BTC-USDT" }, { - "name": "binance", - "off_chain_ticker": "BTCUSDT" + "name": "CryptoCom", + "off_chain_ticker": "BTC_USDT" + }, + { + "name": "Okx", + "off_chain_ticker": "BTC-USDT" } ] }, "COMP/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "COMP-USD" + "name": "KrakenAPI", + "off_chain_ticker": "COMPUSD" + }, + { + "name": "Kraken", + "off_chain_ticker": "COMP/USD" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "COMP-USD" }, { - "name": "kraken", - "off_chain_ticker": "COMP/USD" + "name": "CoinbasePro", + "off_chain_ticker": "COMP-USD" } ] }, "COMP/USDT": { "providers": [ { - "name": "mexc", - "off_chain_ticker": "COMPUSDT" - }, - { - "name": "gate.io", + "name": "Gate", "off_chain_ticker": "COMP_USDT" }, { - "name": "okx", - "off_chain_ticker": "COMP-USDT" + "name": "Mexc", + "off_chain_ticker": "COMPUSDT" }, { - "name": "binance", + "name": "Binance", "off_chain_ticker": "COMPUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "COMP-USDT" } ] }, "CRV/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "CRV-USD" + "name": "KrakenAPI", + "off_chain_ticker": "CRVUSD" }, { - "name": "coinbase", + "name": "Kraken", + "off_chain_ticker": "CRV/USD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "CRV-USD" }, { - "name": "kraken", - "off_chain_ticker": "CRV/USD" + "name": "CoinbasePro", + "off_chain_ticker": "CRV-USD" } ] }, "CRV/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "CRV-USDT" + "name": "Gate", + "off_chain_ticker": "CRV_USDT" }, { - "name": "mexc", + "name": "Mexc", "off_chain_ticker": "CRVUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "CRV_USDT" + "name": "Binance", + "off_chain_ticker": "CRVUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "CRV-USDT" }, { - "name": "binance", - "off_chain_ticker": "CRVUSDT" + "name": "Okx", + "off_chain_ticker": "CRV-USDT" } ] }, "DOGE/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "DOGE-USD" + "name": "KrakenAPI", + "off_chain_ticker": "XDGUSD" }, { - "name": "coinbase", + "name": "Kraken", + "off_chain_ticker": "XDG/USD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "DOGE-USD" }, { - "name": "kraken", - "off_chain_ticker": "XDG/USD" + "name": "CoinbasePro", + "off_chain_ticker": "DOGE-USD" } ] }, "DOGE/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "DOGE-USDT" + "name": "KrakenAPI", + "off_chain_ticker": "XDGUSDT" }, { - "name": "mexc", - "off_chain_ticker": "DOGEUSDT" + "name": "Gate", + "off_chain_ticker": "DOGE_USDT" }, { - "name": "bybit", - "off_chain_ticker": "DOGEUSDT" + "name": "Huobi", + "off_chain_ticker": "dogeusdt" }, { - "name": "gate.io", - "off_chain_ticker": "DOGE_USDT" + "name": "Mexc", + "off_chain_ticker": "DOGEUSDT" }, { - "name": "huobi", - "off_chain_ticker": "dogeusdt" + "name": "Binance", + "off_chain_ticker": "DOGEUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "DOGE-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "DOGEUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "DOGE-USDT" } ] }, "DOT/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "DOT-USD" + "name": "KrakenAPI", + "off_chain_ticker": "DOTUSD" + }, + { + "name": "Kraken", + "off_chain_ticker": "DOT/USD" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "DOT-USD" }, { - "name": "kraken", - "off_chain_ticker": "DOT/USD" + "name": "CoinbasePro", + "off_chain_ticker": "DOT-USD" } ] }, "DOT/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "DOT-USDT" + "name": "KrakenAPI", + "off_chain_ticker": "DOTUSDT" + }, + { + "name": "Gate", + "off_chain_ticker": "DOT_USDT" }, { - "name": "mexc", + "name": "Mexc", "off_chain_ticker": "DOTUSDT" }, { - "name": "bybit", + "name": "Binance", "off_chain_ticker": "DOTUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "DOT_USDT" - }, - { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "DOT-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "DOTUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "DOT-USDT" } ] }, "DYDX/USD": { "providers": [ { - "name": "coingecko", + "name": "Coingecko", "off_chain_ticker": "dydx-chain/usd" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "DYDXUSD-PERP" + "name": "KrakenAPI", + "off_chain_ticker": "DYDXUSD" }, { - "name": "coinbase", + "name": "Kraken", + "off_chain_ticker": "DYDX/USD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "DYDX-USD" }, { - "name": "kraken", - "off_chain_ticker": "DYDX/USD" + "name": "CryptoCom", + "off_chain_ticker": "DYDXUSD-PERP" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "DYDX-USD" } ] @@ -1518,7 +1610,7 @@ "DYDX/USDC": { "providers": [ { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "DYDX-USDC" } ] @@ -1526,51 +1618,55 @@ "DYDX/USDT": { "providers": [ { - "name": "kucoin", + "name": "CoinbaseProAPI", "off_chain_ticker": "DYDX-USDT" }, { - "name": "mexc", - "off_chain_ticker": "DYDXUSDT" + "name": "Gate", + "off_chain_ticker": "DYDX_USDT" }, { - "name": "bybit", - "off_chain_ticker": "DYDXUSDT" + "name": "Huobi", + "off_chain_ticker": "dydxusdt" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "DYDX_USDT" + "name": "Mexc", + "off_chain_ticker": "DYDXUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "DYDX_USDT" + "name": "Binance", + "off_chain_ticker": "DYDXUSDT" }, { - "name": "coinbase", + "name": "Kucoin", "off_chain_ticker": "DYDX-USDT" }, { - "name": "huobi", - "off_chain_ticker": "dydxusdt" + "name": "Bybit", + "off_chain_ticker": "DYDXUSDT" }, { - "name": "okx", - "off_chain_ticker": "DYDX-USDT" + "name": "CryptoCom", + "off_chain_ticker": "DYDX_USDT" }, { - "name": "binance", - "off_chain_ticker": "DYDXUSDT" + "name": "Okx", + "off_chain_ticker": "DYDX-USDT" } ] }, "ETC/USD": { "providers": [ { - "name": "coinbase_websocket", + "name": "KrakenAPI", + "off_chain_ticker": "ETCUSD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "ETC-USD" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "ETC-USD" } ] @@ -1578,119 +1674,127 @@ "ETC/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "ETC-USDT" + "name": "Gate", + "off_chain_ticker": "ETC_USDT" }, { - "name": "mexc", - "off_chain_ticker": "ETCUSDT" + "name": "Huobi", + "off_chain_ticker": "etcusdt" }, { - "name": "gate.io", - "off_chain_ticker": "ETC_USDT" + "name": "Mexc", + "off_chain_ticker": "ETCUSDT" }, { - "name": "huobi", - "off_chain_ticker": "etcusdt" + "name": "Binance", + "off_chain_ticker": "ETCUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "ETC-USDT" }, { - "name": "binance", - "off_chain_ticker": "ETCUSDT" + "name": "Okx", + "off_chain_ticker": "ETC-USDT" } ] }, "ETH/BTC": { "providers": [ { - "name": "bitstamp", - "off_chain_ticker": "ethbtc" + "name": "Coingecko", + "off_chain_ticker": "ethereum/btc" }, { - "name": "coingecko", - "off_chain_ticker": "ethereum/btc" + "name": "KrakenAPI", + "off_chain_ticker": "XETHXXBT" + }, + { + "name": "Kraken", + "off_chain_ticker": "ETH/XBT" }, { - "name": "kucoin", + "name": "CoinbaseProAPI", "off_chain_ticker": "ETH-BTC" }, { - "name": "mexc", - "off_chain_ticker": "ETHBTC" + "name": "Gate", + "off_chain_ticker": "ETH_BTC" }, { - "name": "coinbase_websocket", - "off_chain_ticker": "ETH-BTC" + "name": "Huobi", + "off_chain_ticker": "ethbtc" }, { - "name": "bitfinex", + "name": "Mexc", "off_chain_ticker": "ETHBTC" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "ETH_BTC" + "name": "Binance", + "off_chain_ticker": "ETHBTC" }, { - "name": "gate.io", - "off_chain_ticker": "ETH_BTC" + "name": "Kucoin", + "off_chain_ticker": "ETH-BTC" }, { - "name": "coinbase", - "off_chain_ticker": "ETH-BTC" + "name": "Bitfinex", + "off_chain_ticker": "ETHBTC" }, { - "name": "huobi", + "name": "Bitstamp", "off_chain_ticker": "ethbtc" }, { - "name": "kraken", - "off_chain_ticker": "ETH/XBT" + "name": "CoinbasePro", + "off_chain_ticker": "ETH-BTC" }, { - "name": "okx", - "off_chain_ticker": "ETH-BTC" + "name": "CryptoCom", + "off_chain_ticker": "ETH_BTC" }, { - "name": "binance", - "off_chain_ticker": "ETHBTC" + "name": "Okx", + "off_chain_ticker": "ETH-BTC" } ] }, "ETH/USD": { "providers": [ { - "name": "bitstamp", - "off_chain_ticker": "ethusd" + "name": "Coingecko", + "off_chain_ticker": "ethereum/usd" }, { - "name": "coingecko", - "off_chain_ticker": "ethereum/usd" + "name": "KrakenAPI", + "off_chain_ticker": "XETHZUSD" }, { - "name": "coinbase_websocket", + "name": "Kraken", + "off_chain_ticker": "ETH/USD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "ETH-USD" }, { - "name": "bitfinex", + "name": "Bitfinex", "off_chain_ticker": "ETHUSD" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "ETHUSD-PERP" + "name": "Bitstamp", + "off_chain_ticker": "ethusd" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "ETH-USD" }, { - "name": "kraken", - "off_chain_ticker": "ETH/USD" + "name": "CryptoCom", + "off_chain_ticker": "ETHUSD-PERP" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "ETH-USD" } ] @@ -1698,339 +1802,395 @@ "ETH/USDC": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "ETH-USDC" - }, - { - "name": "mexc", + "name": "KrakenAPI", "off_chain_ticker": "ETHUSDC" }, { - "name": "bybit", - "off_chain_ticker": "ETHUSDC" + "name": "Kraken", + "off_chain_ticker": "ETH/USDC" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "ETH-USDC" }, { - "name": "huobi", + "name": "Huobi", "off_chain_ticker": "ethusdc" }, { - "name": "kraken", - "off_chain_ticker": "ETH/USDC" + "name": "Mexc", + "off_chain_ticker": "ETHUSDC" + }, + { + "name": "Binance", + "off_chain_ticker": "ETHUSDC" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "ETH-USDC" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "ETHUSDC" + }, + { + "name": "Okx", + "off_chain_ticker": "ETH-USDC" } ] }, "ETH/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "ETH-USDT" + "name": "KrakenAPI", + "off_chain_ticker": "ETHUSDT" }, { - "name": "mexc", - "off_chain_ticker": "ETHUSDT" + "name": "Kraken", + "off_chain_ticker": "ETH/USDT" }, { - "name": "coinbase_websocket", + "name": "CoinbaseProAPI", "off_chain_ticker": "ETH-USDT" }, { - "name": "bybit", - "off_chain_ticker": "ETHUSDT" + "name": "Gate", + "off_chain_ticker": "ETH_USDT" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "ETH_USDT" + "name": "Huobi", + "off_chain_ticker": "ethusdt" }, { - "name": "gate.io", - "off_chain_ticker": "ETH_USDT" + "name": "Mexc", + "off_chain_ticker": "ETHUSDT" }, { - "name": "coinbase", - "off_chain_ticker": "ETH-USDT" + "name": "Binance", + "off_chain_ticker": "ETHUSDT" }, { - "name": "huobi", - "off_chain_ticker": "ethusdt" + "name": "Kucoin", + "off_chain_ticker": "ETH-USDT" }, { - "name": "kraken", - "off_chain_ticker": "ETH/USDT" + "name": "Bybit", + "off_chain_ticker": "ETHUSDT" }, { - "name": "okx", + "name": "CoinbasePro", "off_chain_ticker": "ETH-USDT" }, { - "name": "binance", - "off_chain_ticker": "ETHUSDT" + "name": "CryptoCom", + "off_chain_ticker": "ETH_USDT" + }, + { + "name": "Okx", + "off_chain_ticker": "ETH-USDT" } ] }, "FIL/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "FIL-USD" + "name": "KrakenAPI", + "off_chain_ticker": "FILUSD" }, { - "name": "coinbase", + "name": "Kraken", + "off_chain_ticker": "FIL/USD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "FIL-USD" }, { - "name": "kraken", - "off_chain_ticker": "FIL/USD" + "name": "CoinbasePro", + "off_chain_ticker": "FIL-USD" } ] }, "FIL/USDT": { "providers": [ { - "name": "mexc", - "off_chain_ticker": "FILUSDT" - }, - { - "name": "gate.io", + "name": "Gate", "off_chain_ticker": "FIL_USDT" }, { - "name": "huobi", + "name": "Huobi", "off_chain_ticker": "filusdt" }, { - "name": "okx", - "off_chain_ticker": "FIL-USDT" + "name": "Mexc", + "off_chain_ticker": "FILUSDT" }, { - "name": "binance", + "name": "Binance", "off_chain_ticker": "FILUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "FIL-USDT" } ] }, "LDO/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "LDO-USD" + "name": "KrakenAPI", + "off_chain_ticker": "LDOUSD" }, { - "name": "coinbase", + "name": "Kraken", + "off_chain_ticker": "LDO/USD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "LDO-USD" }, { - "name": "kraken", - "off_chain_ticker": "LDO/USD" + "name": "CoinbasePro", + "off_chain_ticker": "LDO-USD" } ] }, "LDO/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "LDO-USDT" + "name": "Mexc", + "off_chain_ticker": "LDOUSDT" }, { - "name": "mexc", + "name": "Binance", "off_chain_ticker": "LDOUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "LDO-USDT" }, { - "name": "binance", - "off_chain_ticker": "LDOUSDT" + "name": "Okx", + "off_chain_ticker": "LDO-USDT" } ] }, "LINK/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "LINK-USD" + "name": "KrakenAPI", + "off_chain_ticker": "LINKUSD" + }, + { + "name": "Kraken", + "off_chain_ticker": "LINK/USD" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "LINK-USD" }, { - "name": "kraken", - "off_chain_ticker": "LINK/USD" + "name": "CoinbasePro", + "off_chain_ticker": "LINK-USD" } ] }, "LINK/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "LINK-USDT" + "name": "KrakenAPI", + "off_chain_ticker": "LINKUSDT" }, { - "name": "mexc", + "name": "Mexc", "off_chain_ticker": "LINKUSDT" }, { - "name": "bybit", + "name": "Binance", "off_chain_ticker": "LINKUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "LINK-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "LINKUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "LINK-USDT" } ] }, "LTC/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "LTC-USD" + "name": "KrakenAPI", + "off_chain_ticker": "XLTCZUSD" }, { - "name": "coinbase", + "name": "Kraken", + "off_chain_ticker": "XLTCZ/USD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "LTC-USD" }, { - "name": "kraken", - "off_chain_ticker": "XLTCZ/USD" + "name": "CoinbasePro", + "off_chain_ticker": "LTC-USD" } ] }, "LTC/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "LTC-USDT" + "name": "KrakenAPI", + "off_chain_ticker": "LTCUSDT" }, { - "name": "mexc", - "off_chain_ticker": "LTCUSDT" + "name": "Huobi", + "off_chain_ticker": "ltcusdt" }, { - "name": "bybit", + "name": "Mexc", "off_chain_ticker": "LTCUSDT" }, { - "name": "huobi", - "off_chain_ticker": "ltcusdt" + "name": "Binance", + "off_chain_ticker": "LTCUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "LTC-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "LTCUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "LTC-USDT" } ] }, "MATIC/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "MATIC-USD" + "name": "KrakenAPI", + "off_chain_ticker": "MATICUSD" + }, + { + "name": "Kraken", + "off_chain_ticker": "MATIC/USD" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "MATIC-USD" }, { - "name": "kraken", - "off_chain_ticker": "MATIC/USD" + "name": "CoinbasePro", + "off_chain_ticker": "MATIC-USD" } ] }, "MATIC/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "MATIC-USDT" + "name": "KrakenAPI", + "off_chain_ticker": "MATICUSDT" }, { - "name": "mexc", - "off_chain_ticker": "MATICUSDT" + "name": "Gate", + "off_chain_ticker": "MATIC_USDT" }, { - "name": "bybit", - "off_chain_ticker": "MATICUSDT" + "name": "Huobi", + "off_chain_ticker": "maticusdt" }, { - "name": "gate.io", - "off_chain_ticker": "MATIC_USDT" + "name": "Mexc", + "off_chain_ticker": "MATICUSDT" }, { - "name": "huobi", - "off_chain_ticker": "maticusdt" + "name": "Binance", + "off_chain_ticker": "MATICUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "MATIC-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "MATICUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "MATIC-USDT" } ] }, "MKR/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "MKR-USD" + "name": "KrakenAPI", + "off_chain_ticker": "MKRUSD" }, { - "name": "coinbase", + "name": "Kraken", + "off_chain_ticker": "MKR/USD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "MKR-USD" }, { - "name": "kraken", - "off_chain_ticker": "MKR/USD" + "name": "CoinbasePro", + "off_chain_ticker": "MKR-USD" } ] }, "MKR/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "MKR-USDT" + "name": "Mexc", + "off_chain_ticker": "MKRUSDT" }, { - "name": "mexc", + "name": "Binance", "off_chain_ticker": "MKRUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "MKR-USDT" }, { - "name": "binance", - "off_chain_ticker": "MKRUSDT" + "name": "Okx", + "off_chain_ticker": "MKR-USDT" + } + ] + }, + "MOG/USD": { + "providers": [ + { + "name": "GeckoTerminal", + "off_chain_ticker": "0xaaee1a9723aadb7afa2810263653a34ba2c21c7a" } ] }, "NEAR/USD": { "providers": [ { - "name": "coinbase_websocket", + "name": "KrakenAPI", + "off_chain_ticker": "NEARUSD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "NEAR-USD" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "NEAR-USD" } ] @@ -2038,39 +2198,43 @@ "NEAR/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "NEAR-USDT" + "name": "Gate", + "off_chain_ticker": "NEAR_USDT" }, { - "name": "mexc", - "off_chain_ticker": "NEARUSDT" + "name": "Huobi", + "off_chain_ticker": "nearusdt" }, { - "name": "gate.io", - "off_chain_ticker": "NEAR_USDT" + "name": "Mexc", + "off_chain_ticker": "NEARUSDT" }, { - "name": "huobi", - "off_chain_ticker": "nearusdt" + "name": "Binance", + "off_chain_ticker": "NEARUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "NEAR-USDT" }, { - "name": "binance", - "off_chain_ticker": "NEARUSDT" + "name": "Okx", + "off_chain_ticker": "NEAR-USDT" } ] }, "OP/USD": { "providers": [ { - "name": "coinbase_websocket", + "name": "KrakenAPI", + "off_chain_ticker": "OPUSD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "OP-USD" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "OP-USD" } ] @@ -2078,55 +2242,55 @@ "OP/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "OP-USDT" + "name": "Gate", + "off_chain_ticker": "OP_USDT" }, { - "name": "mexc", + "name": "Mexc", "off_chain_ticker": "OPUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "OP_USDT" + "name": "Binance", + "off_chain_ticker": "OPUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "OP-USDT" }, { - "name": "binance", - "off_chain_ticker": "OPUSDT" + "name": "Okx", + "off_chain_ticker": "OP-USDT" } ] }, "OSMO/USD": { "providers": [ { - "name": "coingecko", + "name": "Coingecko", "off_chain_ticker": "osmosis/usd" }, { - "name": "coinbase_websocket", + "name": "CoinbaseProAPI", "off_chain_ticker": "OSMO-USD" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "OSMO_USD" + "name": "CoinbasePro", + "off_chain_ticker": "OSMO-USD" }, { - "name": "coinbase", - "off_chain_ticker": "OSMO-USD" + "name": "CryptoCom", + "off_chain_ticker": "OSMO_USD" } ] }, "OSMO/USDC": { "providers": [ { - "name": "coinbase_websocket", + "name": "CoinbaseProAPI", "off_chain_ticker": "OSMO-USDC" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "OSMO-USDC" } ] @@ -2134,15 +2298,15 @@ "OSMO/USDT": { "providers": [ { - "name": "kucoin", + "name": "CoinbaseProAPI", "off_chain_ticker": "OSMO-USDT" }, { - "name": "coinbase_websocket", + "name": "Kucoin", "off_chain_ticker": "OSMO-USDT" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "OSMO-USDT" } ] @@ -2150,7 +2314,15 @@ "PEPE/USD": { "providers": [ { - "name": "kraken", + "name": "GeckoTerminal", + "off_chain_ticker": "0x6982508145454Ce325dDbE47a25d4ec3d2311933" + }, + { + "name": "KrakenAPI", + "off_chain_ticker": "PEPEUSD" + }, + { + "name": "Kraken", "off_chain_ticker": "PEPE/USD" } ] @@ -2158,39 +2330,43 @@ "PEPE/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "PEPE-USDT" + "name": "Gate", + "off_chain_ticker": "PEPE_USDT" }, { - "name": "mexc", + "name": "Mexc", "off_chain_ticker": "PEPEUSDT" }, { - "name": "bybit", + "name": "Binance", "off_chain_ticker": "PEPEUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "PEPE_USDT" - }, - { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "PEPE-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "PEPEUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "PEPE-USDT" } ] }, "SEI/USD": { "providers": [ { - "name": "coinbase_websocket", + "name": "KrakenAPI", + "off_chain_ticker": "SEIUSD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "SEI-USD" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "SEI-USD" } ] @@ -2198,27 +2374,27 @@ "SEI/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "SEI-USDT" + "name": "Gate", + "off_chain_ticker": "SEI_USDT" }, { - "name": "mexc", - "off_chain_ticker": "SEIUSDT" + "name": "Huobi", + "off_chain_ticker": "seiusdt" }, { - "name": "bybit", + "name": "Mexc", "off_chain_ticker": "SEIUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "SEI_USDT" + "name": "Binance", + "off_chain_ticker": "SEIUSDT" }, { - "name": "huobi", - "off_chain_ticker": "seiusdt" + "name": "Kucoin", + "off_chain_ticker": "SEI-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "SEIUSDT" } ] @@ -2226,79 +2402,91 @@ "SHIB/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "SHIB-USD" + "name": "KrakenAPI", + "off_chain_ticker": "SHIBUSD" + }, + { + "name": "Kraken", + "off_chain_ticker": "SHIB/USD" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "SHIB-USD" }, { - "name": "kraken", - "off_chain_ticker": "SHIB/USD" + "name": "CoinbasePro", + "off_chain_ticker": "SHIB-USD" } ] }, "SHIB/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "SHIB-USDT" + "name": "KrakenAPI", + "off_chain_ticker": "SHIBUSDT" }, { - "name": "mexc", - "off_chain_ticker": "SHIBUSDT" + "name": "Gate", + "off_chain_ticker": "SHIB_USDT" }, { - "name": "bybit", + "name": "Mexc", "off_chain_ticker": "SHIBUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "SHIB_USDT" + "name": "Binance", + "off_chain_ticker": "SHIBUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "SHIB-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "SHIBUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "SHIB-USDT" } ] }, "SOL/USD": { "providers": [ { - "name": "bitstamp", - "off_chain_ticker": "solusd" + "name": "Coingecko", + "off_chain_ticker": "solana/usd" }, { - "name": "coingecko", - "off_chain_ticker": "solana/usd" + "name": "KrakenAPI", + "off_chain_ticker": "SOLUSD" }, { - "name": "coinbase_websocket", + "name": "Kraken", + "off_chain_ticker": "SOL/USD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "SOL-USD" }, { - "name": "bitfinex", + "name": "Bitfinex", "off_chain_ticker": "SOLUSD" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "SOLUSD-PERP" + "name": "Bitstamp", + "off_chain_ticker": "solusd" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "SOL-USD" }, { - "name": "kraken", - "off_chain_ticker": "SOL/USD" + "name": "CryptoCom", + "off_chain_ticker": "SOLUSD-PERP" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "SOL-USD" } ] @@ -2306,95 +2494,103 @@ "SOL/USDC": { "providers": [ { - "name": "kucoin", + "name": "CoinbaseProAPI", "off_chain_ticker": "SOL-USDC" }, { - "name": "mexc", - "off_chain_ticker": "SOLUSDC" + "name": "Gate", + "off_chain_ticker": "SOL_USDC" }, { - "name": "coinbase_websocket", - "off_chain_ticker": "SOL-USDC" + "name": "Mexc", + "off_chain_ticker": "SOLUSDC" }, { - "name": "bybit", + "name": "Binance", "off_chain_ticker": "SOLUSDC" }, { - "name": "gate.io", - "off_chain_ticker": "SOL_USDC" + "name": "Kucoin", + "off_chain_ticker": "SOL-USDC" }, { - "name": "coinbase", - "off_chain_ticker": "SOL-USDC" + "name": "Bybit", + "off_chain_ticker": "SOLUSDC" }, { - "name": "okx", + "name": "CoinbasePro", "off_chain_ticker": "SOL-USDC" }, { - "name": "binance", - "off_chain_ticker": "SOLUSDC" + "name": "Okx", + "off_chain_ticker": "SOL-USDC" } ] }, "SOL/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "SOL-USDT" + "name": "KrakenAPI", + "off_chain_ticker": "SOLUSDT" }, { - "name": "mexc", - "off_chain_ticker": "SOLUSDT" + "name": "Kraken", + "off_chain_ticker": "SOL/USDT" }, { - "name": "coinbase_websocket", + "name": "CoinbaseProAPI", "off_chain_ticker": "SOL-USDT" }, { - "name": "bybit", - "off_chain_ticker": "SOLUSDT" + "name": "Gate", + "off_chain_ticker": "SOL_USDT" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "SOL_USDT" + "name": "Huobi", + "off_chain_ticker": "solusdt" }, { - "name": "gate.io", - "off_chain_ticker": "SOL_USDT" + "name": "Mexc", + "off_chain_ticker": "SOLUSDT" }, { - "name": "coinbase", - "off_chain_ticker": "SOL-USDT" + "name": "Binance", + "off_chain_ticker": "SOLUSDT" }, { - "name": "huobi", - "off_chain_ticker": "solusdt" + "name": "Kucoin", + "off_chain_ticker": "SOL-USDT" }, { - "name": "kraken", - "off_chain_ticker": "SOL/USDT" + "name": "Bybit", + "off_chain_ticker": "SOLUSDT" }, { - "name": "okx", + "name": "CoinbasePro", "off_chain_ticker": "SOL-USDT" }, { - "name": "binance", - "off_chain_ticker": "SOLUSDT" + "name": "CryptoCom", + "off_chain_ticker": "SOL_USDT" + }, + { + "name": "Okx", + "off_chain_ticker": "SOL-USDT" } ] }, "SUI/USD": { "providers": [ { - "name": "coinbase_websocket", + "name": "KrakenAPI", + "off_chain_ticker": "SUIUSD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "SUI-USD" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "SUI-USD" } ] @@ -2402,63 +2598,63 @@ "SUI/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "SUI-USDT" + "name": "Gate", + "off_chain_ticker": "SUI_USDT" }, { - "name": "mexc", - "off_chain_ticker": "SUIUSDT" + "name": "Huobi", + "off_chain_ticker": "suiusdt" }, { - "name": "bybit", + "name": "Mexc", "off_chain_ticker": "SUIUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "SUI_USDT" - }, - { - "name": "huobi", - "off_chain_ticker": "suiusdt" + "name": "Binance", + "off_chain_ticker": "SUIUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "SUI-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "SUIUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "SUI-USDT" } ] }, "TIA/USD": { "providers": [ { - "name": "coingecko", + "name": "Coingecko", "off_chain_ticker": "celestia/usd" }, { - "name": "coinbase_websocket", - "off_chain_ticker": "TIA-USD" + "name": "Kraken", + "off_chain_ticker": "TIA/USD" }, { - "name": "bitfinex", - "off_chain_ticker": "TIAUSD" + "name": "CoinbaseProAPI", + "off_chain_ticker": "TIA-USD" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "TIAUSD-PERP" + "name": "Bitfinex", + "off_chain_ticker": "TIAUSD" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "TIA-USD" }, { - "name": "kraken", - "off_chain_ticker": "TIA/USD" + "name": "CryptoCom", + "off_chain_ticker": "TIAUSD-PERP" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "TIA-USD" } ] @@ -2466,11 +2662,11 @@ "TIA/USDC": { "providers": [ { - "name": "coinbase_websocket", + "name": "CoinbaseProAPI", "off_chain_ticker": "TIA-USDC" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "TIA-USDC" } ] @@ -2478,31 +2674,31 @@ "TIA/USDT": { "providers": [ { - "name": "kucoin", + "name": "CoinbaseProAPI", "off_chain_ticker": "TIA-USDT" }, { - "name": "coinbase_websocket", - "off_chain_ticker": "TIA-USDT" + "name": "Gate", + "off_chain_ticker": "TIA_USDT" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "TIA_USDT" + "name": "Huobi", + "off_chain_ticker": "tiausdt" }, { - "name": "gate.io", - "off_chain_ticker": "TIA_USDT" + "name": "Kucoin", + "off_chain_ticker": "TIA-USDT" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "TIA-USDT" }, { - "name": "huobi", - "off_chain_ticker": "tiausdt" + "name": "CryptoCom", + "off_chain_ticker": "TIA_USDT" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "TIA-USDT" } ] @@ -2510,7 +2706,11 @@ "TRX/USD": { "providers": [ { - "name": "kraken", + "name": "KrakenAPI", + "off_chain_ticker": "TRXUSD" + }, + { + "name": "Kraken", "off_chain_ticker": "TRX/USD" } ] @@ -2518,87 +2718,91 @@ "TRX/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "TRX-USDT" + "name": "Gate", + "off_chain_ticker": "TRX_USDT" }, { - "name": "mexc", - "off_chain_ticker": "TRXUSDT" + "name": "Huobi", + "off_chain_ticker": "trxusdt" }, { - "name": "bybit", + "name": "Mexc", "off_chain_ticker": "TRXUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "TRX_USDT" - }, - { - "name": "huobi", - "off_chain_ticker": "trxusdt" + "name": "Binance", + "off_chain_ticker": "TRXUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "TRX-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "TRXUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "TRX-USDT" } ] }, "UNI/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "UNI-USD" + "name": "KrakenAPI", + "off_chain_ticker": "UNIUSD" }, { - "name": "coinbase", + "name": "Kraken", + "off_chain_ticker": "UNI/USD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "UNI-USD" }, { - "name": "kraken", - "off_chain_ticker": "UNI/USD" + "name": "CoinbasePro", + "off_chain_ticker": "UNI-USD" } ] }, "UNI/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "UNI-USDT" + "name": "Gate", + "off_chain_ticker": "UNI_USDT" }, { - "name": "bybit", + "name": "Binance", "off_chain_ticker": "UNIUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "UNI_USDT" - }, - { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "UNI-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "UNIUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "UNI-USDT" } ] }, "USDC/USD": { "providers": [ { - "name": "coinbase", - "off_chain_ticker": "USDC-USD" + "name": "Kraken", + "off_chain_ticker": "USDC/USD" }, { - "name": "kraken", - "off_chain_ticker": "USDC/USD" + "name": "CoinbaseProAPI", + "off_chain_ticker": "USDC-USD" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "USDC-USD" } ] @@ -2606,75 +2810,87 @@ "USDC/USDT": { "providers": [ { - "name": "bitstamp", - "off_chain_ticker": "usdcusdt" + "name": "KrakenAPI", + "off_chain_ticker": "USDCUSDT" + }, + { + "name": "Kraken", + "off_chain_ticker": "USDC/USDT" }, { - "name": "kucoin", + "name": "CoinbaseProAPI", "off_chain_ticker": "USDC-USDT" }, { - "name": "mexc", - "off_chain_ticker": "USDCUSDT" + "name": "Gate", + "off_chain_ticker": "USDC_USDT" }, { - "name": "coinbase_websocket", - "off_chain_ticker": "USDC-USDT" + "name": "Huobi", + "off_chain_ticker": "usdcusdt" }, { - "name": "bybit", + "name": "Mexc", "off_chain_ticker": "USDCUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "USDC_USDT" + "name": "Binance", + "off_chain_ticker": "USDCUSDT" }, { - "name": "coinbase", + "name": "Kucoin", "off_chain_ticker": "USDC-USDT" }, { - "name": "huobi", + "name": "Bitstamp", "off_chain_ticker": "usdcusdt" }, { - "name": "kraken", - "off_chain_ticker": "USDC/USDT" + "name": "Bybit", + "off_chain_ticker": "USDCUSDT" }, { - "name": "okx", + "name": "CoinbasePro", "off_chain_ticker": "USDC-USDT" }, { - "name": "binance", - "off_chain_ticker": "USDCUSDT" + "name": "Okx", + "off_chain_ticker": "USDC-USDT" } ] }, "USDT/USD": { "providers": [ { - "name": "bitstamp", - "off_chain_ticker": "usdtusd" + "name": "KrakenAPI", + "off_chain_ticker": "USDTZUSD" }, { - "name": "coinbase_websocket", + "name": "Kraken", + "off_chain_ticker": "USDT/USD" + }, + { + "name": "CoinbaseProAPI", "off_chain_ticker": "USDT-USD" }, { - "name": "crypto_dot_com", - "off_chain_ticker": "USDT_USD" + "name": "Binance", + "off_chain_ticker": "USDTUSD" + }, + { + "name": "Bitstamp", + "off_chain_ticker": "usdtusd" }, { - "name": "coinbase", + "name": "CoinbasePro", "off_chain_ticker": "USDT-USD" }, { - "name": "kraken", - "off_chain_ticker": "USDT/USD" + "name": "CryptoCom", + "off_chain_ticker": "USDT_USD" }, { - "name": "okx", + "name": "Okx", "off_chain_ticker": "USDT-USD" } ] @@ -2682,120 +2898,132 @@ "WLD/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "WLD-USDT" + "name": "Gate", + "off_chain_ticker": "WLD_USDT" }, { - "name": "mexc", - "off_chain_ticker": "WLDUSDT" + "name": "Huobi", + "off_chain_ticker": "wldusdt" }, { - "name": "bybit", + "name": "Mexc", "off_chain_ticker": "WLDUSDT" }, { - "name": "gate.io", - "off_chain_ticker": "WLD_USDT" - }, - { - "name": "huobi", - "off_chain_ticker": "wldusdt" + "name": "Binance", + "off_chain_ticker": "WLDUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "WLD-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "WLDUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "WLD-USDT" } ] }, "XLM/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "XLM-USD" + "name": "KrakenAPI", + "off_chain_ticker": "XXLMZUSD" + }, + { + "name": "Kraken", + "off_chain_ticker": "XXLMZ/USD" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "XLM-USD" }, { - "name": "kraken", - "off_chain_ticker": "XXLMZ/USD" + "name": "CoinbasePro", + "off_chain_ticker": "XLM-USD" } ] }, "XLM/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "XLM-USDT" - }, - { - "name": "mexc", + "name": "Mexc", "off_chain_ticker": "XLMUSDT" }, { - "name": "bybit", + "name": "Binance", "off_chain_ticker": "XLMUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "XLM-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "XLMUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "XLM-USDT" } ] }, "XRP/USD": { "providers": [ { - "name": "coinbase_websocket", - "off_chain_ticker": "XRP-USD" + "name": "KrakenAPI", + "off_chain_ticker": "XXRPZUSD" + }, + { + "name": "Kraken", + "off_chain_ticker": "XXRPZ/USD" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "off_chain_ticker": "XRP-USD" }, { - "name": "kraken", - "off_chain_ticker": "XXRPZ/USD" + "name": "CoinbasePro", + "off_chain_ticker": "XRP-USD" } ] }, "XRP/USDT": { "providers": [ { - "name": "kucoin", - "off_chain_ticker": "XRP-USDT" + "name": "KrakenAPI", + "off_chain_ticker": "XRPUSDT" }, { - "name": "mexc", - "off_chain_ticker": "XRPUSDT" + "name": "Gate", + "off_chain_ticker": "XRP_USDT" }, { - "name": "bybit", - "off_chain_ticker": "XRPUSDT" + "name": "Huobi", + "off_chain_ticker": "xrpusdt" }, { - "name": "gate.io", - "off_chain_ticker": "XRP_USDT" + "name": "Mexc", + "off_chain_ticker": "XRPUSDT" }, { - "name": "huobi", - "off_chain_ticker": "xrpusdt" + "name": "Binance", + "off_chain_ticker": "XRPUSDT" }, { - "name": "okx", + "name": "Kucoin", "off_chain_ticker": "XRP-USDT" }, { - "name": "binance", + "name": "Bybit", "off_chain_ticker": "XRPUSDT" + }, + { + "name": "Okx", + "off_chain_ticker": "XRP-USDT" } ] } diff --git a/protocol/daemons/slinky/config/oracle.json b/protocol/daemons/slinky/config/oracle.json index bbcf0d4d48..23a1738f70 100644 --- a/protocol/daemons/slinky/config/oracle.json +++ b/protocol/daemons/slinky/config/oracle.json @@ -3,15 +3,16 @@ "maxPriceAge": 120000000000, "providers": [ { - "name": "binance", + "name": "Binance", "api": { "enabled": true, "timeout": 500000000, "interval": 150000000, + "reconnectTimeout": 2000000000, "maxQueries": 1, "atomic": true, "url": "https://api.binance.com/api/v3/ticker/price?symbols=%s%s%s", - "name": "binance" + "name": "Binance" }, "webSocket": { "enabled": false, @@ -28,18 +29,20 @@ "pingInterval": 0, "maxReadErrorCount": 0, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" }, { - "name": "coinbase", + "name": "CoinbaseProAPI", "api": { "enabled": true, "timeout": 500000000, - "interval": 20000000, - "maxQueries": 10, + "interval": 100000000, + "reconnectTimeout": 2000000000, + "maxQueries": 5, "atomic": false, "url": "https://api.coinbase.com/v2/prices/%s/spot", - "name": "coinbase" + "name": "CoinbaseProAPI" }, "webSocket": { "enabled": false, @@ -56,18 +59,80 @@ "pingInterval": 0, "maxReadErrorCount": 0, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" }, { - "name": "coingecko", + "name": "Coingecko", "api": { "enabled": true, "timeout": 500000000, "interval": 15000000000, + "reconnectTimeout": 2000000000, "maxQueries": 1, "atomic": true, "url": "https://api.coingecko.com/api/v3", - "name": "coingecko" + "name": "Coingecko" + }, + "webSocket": { + "enabled": false, + "maxBufferSize": 0, + "reconnectionTimeout": 0, + "wss": "", + "name": "", + "readBufferSize": 0, + "writeBufferSize": 0, + "handshakeTimeout": 0, + "enableCompression": false, + "readTimeout": 0, + "writeTimeout": 0, + "pingInterval": 0, + "maxReadErrorCount": 0, + "maxSubscriptionsPerConnection": 0 + }, + "type": "price_provider" + }, + { + "name": "GeckoTerminal", + "api": { + "enabled": true, + "timeout": 500000000, + "interval": 5000000000, + "reconnectTimeout": 2000000000, + "maxQueries": 1, + "atomic": false, + "url": "https://api.geckoterminal.com/api/v2/simple/networks/eth/token_price/%s", + "name": "GeckoTerminal" + }, + "webSocket": { + "enabled": false, + "maxBufferSize": 0, + "reconnectionTimeout": 0, + "wss": "", + "name": "", + "readBufferSize": 0, + "writeBufferSize": 0, + "handshakeTimeout": 0, + "enableCompression": false, + "readTimeout": 0, + "writeTimeout": 0, + "pingInterval": 0, + "maxReadErrorCount": 0, + "maxSubscriptionsPerConnection": 0 + }, + "type": "price_provider" + }, + { + "name": "KrakenAPI", + "api": { + "enabled": true, + "timeout": 500000000, + "interval": 150000000, + "reconnectTimeout": 2000000000, + "maxQueries": 1, + "atomic": true, + "url": "https://api.kraken.com/0/public/Ticker?pair=%s", + "name": "KrakenAPI" }, "webSocket": { "enabled": false, @@ -84,14 +149,16 @@ "pingInterval": 0, "maxReadErrorCount": 0, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" }, { - "name": "bitfinex", + "name": "Bitfinex", "api": { "enabled": false, "timeout": 0, "interval": 0, + "reconnectTimeout": 0, "maxQueries": 0, "atomic": false, "url": "", @@ -102,7 +169,7 @@ "maxBufferSize": 1000, "reconnectionTimeout": 10000000000, "wss": "wss://api-pub.bitfinex.com/ws/2", - "name": "bitfinex", + "name": "Bitfinex", "readBufferSize": 0, "writeBufferSize": 0, "handshakeTimeout": 45000000000, @@ -112,14 +179,16 @@ "pingInterval": 0, "maxReadErrorCount": 100, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" }, { - "name": "bitstamp", + "name": "Bitstamp", "api": { "enabled": false, "timeout": 0, "interval": 0, + "reconnectTimeout": 0, "maxQueries": 0, "atomic": false, "url": "", @@ -130,7 +199,7 @@ "maxBufferSize": 1024, "reconnectionTimeout": 10000000000, "wss": "wss://ws.bitstamp.net", - "name": "bitstamp", + "name": "Bitstamp", "readBufferSize": 0, "writeBufferSize": 0, "handshakeTimeout": 45000000000, @@ -140,14 +209,16 @@ "pingInterval": 10000000000, "maxReadErrorCount": 100, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" }, { - "name": "bybit", + "name": "Bybit", "api": { "enabled": false, "timeout": 0, "interval": 0, + "reconnectTimeout": 0, "maxQueries": 0, "atomic": false, "url": "", @@ -158,7 +229,7 @@ "maxBufferSize": 1000, "reconnectionTimeout": 10000000000, "wss": "wss://stream.bybit.com/v5/public/spot", - "name": "bybit", + "name": "Bybit", "readBufferSize": 0, "writeBufferSize": 0, "handshakeTimeout": 45000000000, @@ -168,14 +239,16 @@ "pingInterval": 15000000000, "maxReadErrorCount": 100, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" }, { - "name": "coinbase_websocket", + "name": "CoinbasePro", "api": { "enabled": false, "timeout": 0, "interval": 0, + "reconnectTimeout": 0, "maxQueries": 0, "atomic": false, "url": "", @@ -186,7 +259,7 @@ "maxBufferSize": 1024, "reconnectionTimeout": 10000000000, "wss": "wss://ws-feed.exchange.coinbase.com", - "name": "coinbase_websocket", + "name": "CoinbasePro", "readBufferSize": 0, "writeBufferSize": 0, "handshakeTimeout": 45000000000, @@ -196,14 +269,16 @@ "pingInterval": 0, "maxReadErrorCount": 100, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" }, { - "name": "crypto_dot_com", + "name": "CryptoCom", "api": { "enabled": false, "timeout": 0, "interval": 0, + "reconnectTimeout": 0, "maxQueries": 0, "atomic": false, "url": "", @@ -214,7 +289,7 @@ "maxBufferSize": 1024, "reconnectionTimeout": 10000000000, "wss": "wss://stream.crypto.com/exchange/v1/market", - "name": "crypto_dot_com", + "name": "CryptoCom", "readBufferSize": 0, "writeBufferSize": 0, "handshakeTimeout": 45000000000, @@ -224,14 +299,16 @@ "pingInterval": 0, "maxReadErrorCount": 100, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" }, { - "name": "gate.io", + "name": "Gate", "api": { "enabled": false, "timeout": 0, "interval": 0, + "reconnectTimeout": 0, "maxQueries": 0, "atomic": false, "url": "", @@ -242,7 +319,7 @@ "maxBufferSize": 1000, "reconnectionTimeout": 10000000000, "wss": "wss://api.gateio.ws/ws/v4/", - "name": "gate.io", + "name": "Gate", "readBufferSize": 0, "writeBufferSize": 0, "handshakeTimeout": 45000000000, @@ -252,14 +329,16 @@ "pingInterval": 0, "maxReadErrorCount": 100, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" }, { - "name": "huobi", + "name": "Huobi", "api": { "enabled": false, "timeout": 0, "interval": 0, + "reconnectTimeout": 0, "maxQueries": 0, "atomic": false, "url": "", @@ -270,7 +349,7 @@ "maxBufferSize": 1000, "reconnectionTimeout": 10000000000, "wss": "wss://api.huobi.pro/ws", - "name": "huobi", + "name": "Huobi", "readBufferSize": 0, "writeBufferSize": 0, "handshakeTimeout": 45000000000, @@ -280,14 +359,16 @@ "pingInterval": 0, "maxReadErrorCount": 100, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" }, { - "name": "kraken", + "name": "Kraken", "api": { "enabled": false, "timeout": 0, "interval": 0, + "reconnectTimeout": 0, "maxQueries": 0, "atomic": false, "url": "", @@ -298,7 +379,7 @@ "maxBufferSize": 1000, "reconnectionTimeout": 10000000000, "wss": "wss://ws.kraken.com", - "name": "kraken", + "name": "Kraken", "readBufferSize": 0, "writeBufferSize": 0, "handshakeTimeout": 45000000000, @@ -308,25 +389,27 @@ "pingInterval": 0, "maxReadErrorCount": 100, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" }, { - "name": "kucoin", + "name": "Kucoin", "api": { "enabled": false, "timeout": 5000000000, "interval": 60000000000, + "reconnectTimeout": 0, "maxQueries": 1, "atomic": false, "url": "https://api.kucoin.com", - "name": "kucoin" + "name": "Kucoin" }, "webSocket": { "enabled": true, "maxBufferSize": 1024, "reconnectionTimeout": 10000000000, "wss": "wss://ws-api-spot.kucoin.com/", - "name": "kucoin", + "name": "Kucoin", "readBufferSize": 0, "writeBufferSize": 0, "handshakeTimeout": 45000000000, @@ -336,14 +419,16 @@ "pingInterval": 10000000000, "maxReadErrorCount": 100, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" }, { - "name": "mexc", + "name": "Mexc", "api": { "enabled": false, "timeout": 0, "interval": 0, + "reconnectTimeout": 0, "maxQueries": 0, "atomic": false, "url": "", @@ -354,7 +439,7 @@ "maxBufferSize": 1000, "reconnectionTimeout": 10000000000, "wss": "wss://wbs.mexc.com/ws", - "name": "mexc", + "name": "Mexc", "readBufferSize": 0, "writeBufferSize": 0, "handshakeTimeout": 45000000000, @@ -363,15 +448,17 @@ "writeTimeout": 45000000000, "pingInterval": 20000000000, "maxReadErrorCount": 100, - "maxSubscriptionsPerConnection": 0 - } + "maxSubscriptionsPerConnection": 20 + }, + "type": "price_provider" }, { - "name": "okx", + "name": "Okx", "api": { "enabled": false, "timeout": 0, "interval": 0, + "reconnectTimeout": 0, "maxQueries": 0, "atomic": false, "url": "", @@ -382,7 +469,7 @@ "maxBufferSize": 1000, "reconnectionTimeout": 10000000000, "wss": "wss://ws.okx.com:8443/ws/v5/public", - "name": "okx", + "name": "Okx", "readBufferSize": 0, "writeBufferSize": 0, "handshakeTimeout": 45000000000, @@ -392,10 +479,11 @@ "pingInterval": 0, "maxReadErrorCount": 100, "maxSubscriptionsPerConnection": 0 - } + }, + "type": "price_provider" } ], - "production": false, + "production": true, "metrics": { "prometheusServerAddress": "0.0.0.0:8002", "enabled": true diff --git a/protocol/go.mod b/protocol/go.mod index 84acaa53dd..4303d3034d 100644 --- a/protocol/go.mod +++ b/protocol/go.mod @@ -6,7 +6,7 @@ toolchain go1.22.0 require ( cosmossdk.io/api v0.7.3 - cosmossdk.io/math v1.2.0 + cosmossdk.io/math v1.3.0 github.com/Shopify/sarama v1.37.2 github.com/cometbft/cometbft v0.38.5 github.com/cometbft/cometbft-db v0.9.1 // indirect @@ -19,7 +19,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/mock v1.6.0 github.com/golang/protobuf v1.5.3 - github.com/golangci/golangci-lint v1.55.2 + github.com/golangci/golangci-lint v1.56.2 github.com/google/go-cmp v0.6.0 github.com/gorilla/mux v1.8.1 github.com/grpc-ecosystem/grpc-gateway v1.16.0 @@ -31,11 +31,11 @@ require ( github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.8.4 - github.com/vektra/mockery/v2 v2.40.1 + github.com/vektra/mockery/v2 v2.42.0 github.com/zyedidia/generic v1.0.0 golang.org/x/exp v0.0.0-20240213143201-ec583247a57a - google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 // indirect - google.golang.org/grpc v1.61.0 + google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 // indirect + google.golang.org/grpc v1.62.0 gopkg.in/DataDog/dd-trace-go.v1 v1.48.0 gopkg.in/typ.v4 v4.1.0 ) @@ -53,8 +53,8 @@ require ( cosmossdk.io/x/tx v0.13.0 cosmossdk.io/x/upgrade v0.1.1 github.com/burdiyan/kafkautil v0.0.0-20190131162249-eaf83ed22d5b - github.com/cosmos/cosmos-db v1.0.0 - github.com/cosmos/iavl v1.0.1 + github.com/cosmos/cosmos-db v1.0.2 + github.com/cosmos/iavl v1.1.1-0.20240313174921-bfaf5fcda7b3 github.com/cosmos/ibc-go/modules/capability v1.0.0 github.com/cosmos/ibc-go/v8 v8.0.0 github.com/cosmos/rosetta v0.50.3 @@ -66,10 +66,11 @@ require ( github.com/pelletier/go-toml v1.9.5 github.com/rs/zerolog v1.32.0 github.com/shopspring/decimal v1.3.1 - github.com/skip-mev/slinky v0.2.2 + github.com/skip-mev/slinky v0.2.1-0.20240301171048-53cc00fdca4e github.com/spf13/viper v1.18.2 github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d - google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 + go.uber.org/zap v1.27.0 + google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 google.golang.org/protobuf v1.32.0 gotest.tools/v3 v3.5.1 ) @@ -77,11 +78,11 @@ require ( require ( 4d63.com/gocheckcompilerdirectives v1.2.1 // indirect 4d63.com/gochecknoglobals v0.2.1 // indirect - cloud.google.com/go v0.110.10 // indirect + cloud.google.com/go v0.112.0 // indirect cloud.google.com/go/compute v1.23.3 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect cloud.google.com/go/iam v1.1.5 // indirect - cloud.google.com/go/storage v1.35.1 // indirect + cloud.google.com/go/storage v1.36.0 // indirect cosmossdk.io/collections v0.4.0 // indirect cosmossdk.io/depinject v1.0.0-alpha.4 // indirect filippo.io/edwards25519 v1.0.0 // indirect @@ -91,7 +92,7 @@ require ( github.com/Abirdcfly/dupword v0.0.13 // indirect github.com/Antonboom/errname v0.1.12 // indirect github.com/Antonboom/nilnil v0.1.7 // indirect - github.com/Antonboom/testifylint v0.2.3 // indirect + github.com/Antonboom/testifylint v1.1.2 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/BurntSushi/toml v1.3.2 // indirect github.com/DataDog/datadog-go v4.8.2+incompatible // indirect @@ -99,14 +100,14 @@ require ( github.com/DataDog/gostackparse v0.5.0 // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect - github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0 // indirect + github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 // indirect github.com/IBM/sarama v1.40.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Microsoft/go-winio v0.6.1 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect - github.com/OpenPeeDeeP/depguard/v2 v2.1.0 // indirect + github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 // indirect - github.com/alecthomas/go-check-sumtype v0.1.3 // indirect + github.com/alecthomas/go-check-sumtype v0.1.4 // indirect github.com/alexkohler/nakedret/v2 v2.0.2 // indirect github.com/alexkohler/prealloc v1.0.0 // indirect github.com/alingse/asasalint v0.0.11 // indirect @@ -120,14 +121,14 @@ require ( github.com/bits-and-blooms/bitset v1.13.0 // indirect github.com/bkielbasa/cyclop v1.2.1 // indirect github.com/blizzy78/varnamelen v0.8.0 // indirect - github.com/bombsimon/wsl/v3 v3.4.0 // indirect + github.com/bombsimon/wsl/v4 v4.2.1 // indirect github.com/breml/bidichk v0.2.7 // indirect github.com/breml/errchkjson v0.3.6 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect - github.com/butuzov/ireturn v0.2.2 // indirect + github.com/butuzov/ireturn v0.3.0 // indirect github.com/butuzov/mirror v1.1.0 // indirect - github.com/catenacyber/perfsprint v0.2.0 // indirect - github.com/ccojocar/zxcvbn-go v1.0.1 // indirect + github.com/catenacyber/perfsprint v0.6.0 // indirect + github.com/ccojocar/zxcvbn-go v1.0.2 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/cenkalti/backoff/v4 v4.2.1 // indirect github.com/cespare/xxhash v1.1.0 // indirect @@ -152,7 +153,7 @@ require ( github.com/creachadair/atomicfile v0.3.1 // indirect github.com/creachadair/tomledit v0.0.24 // indirect github.com/curioswitch/go-reassign v0.2.0 // indirect - github.com/daixiang0/gci v0.11.2 // indirect + github.com/daixiang0/gci v0.12.1 // 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.2.0 // indirect @@ -172,7 +173,7 @@ require ( github.com/eapache/queue v1.1.0 // indirect github.com/emicklei/dot v1.6.1 // indirect github.com/esimonov/ifshort v1.0.4 // indirect - github.com/ettle/strcase v0.1.1 // indirect + github.com/ettle/strcase v0.2.0 // indirect github.com/fatih/color v1.16.0 // indirect github.com/fatih/structtag v1.2.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect @@ -181,21 +182,24 @@ require ( github.com/fzipp/gocyclo v0.6.0 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect - github.com/ghostiam/protogetter v0.2.3 // indirect - github.com/go-critic/go-critic v0.9.0 // indirect + github.com/ghostiam/protogetter v0.3.4 // indirect + github.com/go-critic/go-critic v0.11.1 // indirect github.com/go-kit/kit v0.13.0 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/logr v1.3.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-toolsmith/astcast v1.1.0 // indirect github.com/go-toolsmith/astcopy v1.1.0 // indirect - github.com/go-toolsmith/astequal v1.1.0 // indirect + github.com/go-toolsmith/astequal v1.2.0 // indirect github.com/go-toolsmith/astfmt v1.1.0 // indirect github.com/go-toolsmith/astp v1.1.0 // indirect github.com/go-toolsmith/strparse v1.1.0 // indirect github.com/go-toolsmith/typep v1.1.0 // indirect + github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect @@ -217,10 +221,10 @@ require ( github.com/google/pprof v0.0.0-20230228050547-1710fef4ab10 // indirect github.com/google/s2a-go v0.1.7 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect - github.com/google/uuid v1.4.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect github.com/googleapis/gax-go/v2 v2.12.0 // indirect - github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601 // indirect + github.com/gordonklaus/ineffassign v0.1.0 // indirect github.com/gorilla/handlers v1.5.2 // indirect github.com/gorilla/websocket v1.5.1 // indirect github.com/gostaticanalysis/analysisutil v0.7.1 // indirect @@ -257,21 +261,22 @@ require ( github.com/jcmturner/gofork v1.7.6 // indirect github.com/jcmturner/gokrb5/v8 v8.4.3 // indirect github.com/jcmturner/rpc/v2 v2.0.3 // indirect - github.com/jgautheron/goconst v1.6.0 // indirect + github.com/jgautheron/goconst v1.7.0 // indirect github.com/jingyugao/rowserrcheck v1.1.1 // indirect github.com/jinzhu/copier v0.4.0 // indirect github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect + github.com/jjti/go-spancheck v0.5.2 // indirect github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/jmhodges/levigo v1.0.0 // indirect github.com/julz/importas v0.1.0 // indirect - github.com/kisielk/errcheck v1.6.3 // indirect + github.com/kisielk/errcheck v1.7.0 // indirect github.com/kisielk/gotool v1.0.0 // indirect github.com/kkHAIKE/contextcheck v1.1.4 // indirect - github.com/klauspost/compress v1.17.6 // indirect + github.com/klauspost/compress v1.17.7 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/kulti/thelper v0.6.3 // indirect - github.com/kunwardeep/paralleltest v1.0.8 // indirect + github.com/kunwardeep/paralleltest v1.0.9 // indirect github.com/kyoh86/exportloopref v0.1.11 // indirect github.com/ldez/gomoddirectives v0.2.3 // indirect github.com/ldez/tagliatelle v0.5.0 // indirect @@ -282,7 +287,7 @@ require ( github.com/linxGnu/grocksdb v1.8.12 // indirect github.com/lovoo/goka v1.1.9 // indirect github.com/lufeee/execinquery v1.2.1 // indirect - github.com/macabu/inamedparam v0.1.2 // indirect + github.com/macabu/inamedparam v0.1.3 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/manifoldco/promptui v0.9.0 // indirect github.com/maratori/testableexamples v1.0.0 // indirect @@ -292,7 +297,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect github.com/mbilski/exhaustivestruct v1.2.0 // indirect - github.com/mgechev/revive v1.3.4 // indirect + github.com/mgechev/revive v1.3.7 // indirect github.com/minio/highwayhash v1.0.2 // indirect github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/mitchellh/go-testing-interface v1.14.1 // indirect @@ -301,23 +306,23 @@ require ( github.com/moricho/tparallel v0.3.1 // indirect github.com/mtibben/percent v0.2.1 // indirect github.com/nakabonne/nestif v0.3.1 // indirect - github.com/nishanths/exhaustive v0.11.0 // indirect + github.com/nishanths/exhaustive v0.12.0 // indirect github.com/nishanths/predeclared v0.2.2 // indirect - github.com/nunnatsa/ginkgolinter v0.14.1 // indirect + github.com/nunnatsa/ginkgolinter v0.15.2 // indirect github.com/nxadm/tail v1.4.8 // indirect github.com/oasisprotocol/curve25519-voi v0.0.0-20230904125328-1f23a7beb09a // indirect github.com/oklog/run v1.1.0 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/onsi/ginkgo v1.16.4 // indirect - github.com/onsi/gomega v1.28.1 // indirect + github.com/onsi/gomega v1.31.1 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2 // indirect - github.com/opencontainers/runc v1.1.5 // indirect + github.com/opencontainers/runc v1.1.12 // indirect github.com/pelletier/go-toml/v2 v2.1.1 // indirect github.com/petermattis/goid v0.0.0-20230904192822-1876fd5063bc // indirect 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.4.5 // indirect + github.com/polyfloyd/go-errorlint v1.4.8 // indirect github.com/prometheus/client_golang v1.18.0 // indirect github.com/prometheus/client_model v0.6.0 // indirect github.com/prometheus/common v0.47.0 // indirect @@ -338,8 +343,8 @@ require ( github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect github.com/sasha-s/go-deadlock v0.3.1 // indirect github.com/sashamelentyev/interfacebloat v1.1.0 // indirect - github.com/sashamelentyev/usestdlibvars v1.24.0 // indirect - github.com/securego/gosec/v2 v2.18.2 // indirect + github.com/sashamelentyev/usestdlibvars v1.25.0 // indirect + github.com/securego/gosec/v2 v2.19.0 // indirect github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect github.com/sirupsen/logrus v1.9.3 // indirect @@ -358,7 +363,7 @@ require ( github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect github.com/tdakkota/asciicheck v0.2.0 // indirect github.com/tendermint/go-amino v0.16.0 // indirect - github.com/tetafro/godot v1.4.15 // indirect + github.com/tetafro/godot v1.4.16 // indirect github.com/tidwall/btree v1.7.0 // indirect github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect github.com/timonwong/loggercheck v0.9.4 // indirect @@ -368,7 +373,7 @@ require ( github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect github.com/ulikunitz/xz v0.5.11 // indirect github.com/ultraware/funlen v0.1.0 // indirect - github.com/ultraware/whitespace v0.0.5 // indirect + github.com/ultraware/whitespace v0.1.0 // indirect github.com/uudashr/gocognit v1.1.2 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect @@ -376,18 +381,22 @@ require ( github.com/xen0n/gosmopolitan v1.2.2 // indirect github.com/yagipy/maintidx v1.0.0 // indirect github.com/yeya24/promlinter v0.2.0 // indirect - github.com/ykadowak/zerologlint v0.1.3 // indirect + github.com/ykadowak/zerologlint v0.1.5 // indirect github.com/zondax/hid v0.9.2 // indirect github.com/zondax/ledger-go v0.14.3 // indirect gitlab.com/bosi/decorder v0.4.1 // indirect - go-simpler.org/sloglint v0.1.2 // indirect + go-simpler.org/musttag v0.8.0 // indirect + go-simpler.org/sloglint v0.4.0 // indirect go.etcd.io/bbolt v1.3.8 // indirect go.opencensus.io v0.24.0 // indirect - go.tmz.dev/musttag v0.7.2 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 // indirect + go.opentelemetry.io/otel v1.21.0 // indirect + go.opentelemetry.io/otel/metric v1.21.0 // indirect + go.opentelemetry.io/otel/trace v1.21.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect golang.org/x/crypto v0.19.0 // indirect - golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 // indirect + golang.org/x/exp/typeparams v0.0.0-20231219180239-dc181d75b848 // indirect golang.org/x/mod v0.15.0 // indirect golang.org/x/net v0.21.0 // indirect golang.org/x/oauth2 v0.16.0 // indirect @@ -397,10 +406,9 @@ require ( golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.18.0 // indirect - golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect - google.golang.org/api v0.153.0 // indirect + google.golang.org/api v0.155.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect @@ -410,7 +418,7 @@ require ( mvdan.cc/gofumpt v0.6.0 // indirect mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect - mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d // indirect + mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14 // indirect nhooyr.io/websocket v1.8.10 // indirect pgregory.net/rapid v1.1.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect @@ -428,7 +436,7 @@ replace ( // Use dYdX fork of CometBFT github.com/cometbft/cometbft => github.com/dydxprotocol/cometbft v0.38.6-0.20240229050000-3b085f30b462 // Use dYdX fork of Cosmos SDK - github.com/cosmos/cosmos-sdk => github.com/dydxprotocol/cosmos-sdk v0.50.5-0.20240227194839-f4e166bc1057 + github.com/cosmos/cosmos-sdk => github.com/dydxprotocol/cosmos-sdk v0.50.5-0.20240320193634-44a06cd9428e ) replace ( diff --git a/protocol/go.sum b/protocol/go.sum index 91cafa0063..26879ce2f1 100644 --- a/protocol/go.sum +++ b/protocol/go.sum @@ -34,8 +34,8 @@ cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w9 cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= -cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= +cloud.google.com/go v0.112.0 h1:tpFCD7hpHFlQ8yPwT3x+QeXqc2T6+n6T+hmABHfDUSM= +cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4= cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= @@ -175,8 +175,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= -cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= -cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= +cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= +cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= @@ -200,8 +200,8 @@ cosmossdk.io/errors v1.0.1 h1:bzu+Kcr0kS/1DuPBtUFdWjzLqyUuCiyHjyJB6srBV/0= cosmossdk.io/errors v1.0.1/go.mod h1:MeelVSZThMi4bEakzhhhE/CKqVv3nOJDA25bIqRDu/U= cosmossdk.io/log v1.3.1 h1:UZx8nWIkfbbNEWusZqzAx3ZGvu54TZacWib3EzUYmGI= cosmossdk.io/log v1.3.1/go.mod h1:2/dIomt8mKdk6vl3OWJcPk2be3pGOS8OQaLUM/3/tCM= -cosmossdk.io/math v1.2.0 h1:8gudhTkkD3NxOP2YyyJIYYmt6dQ55ZfJkDOaxXpy7Ig= -cosmossdk.io/math v1.2.0/go.mod h1:l2Gnda87F0su8a/7FEKJfFdJrM0JZRXQaohlgJeyQh0= +cosmossdk.io/math v1.3.0 h1:RC+jryuKeytIiictDslBP9i1fhkVm6ZDmZEoNP316zE= +cosmossdk.io/math v1.3.0/go.mod h1:vnRTxewy+M7BtXBNFybkuhSH4WfedVAAnERHgVFhp3k= cosmossdk.io/tools/confix v0.1.1 h1:aexyRv9+y15veH3Qw16lxQwo+ki7r2I+g0yNTEFEQM8= cosmossdk.io/tools/confix v0.1.1/go.mod h1:nQVvP1tHsGXS83PonPVWJtSbddIqyjEw99L4M3rPJyQ= cosmossdk.io/x/circuit v0.1.0 h1:IAej8aRYeuOMritczqTlljbUVHq1E85CpBqaCTwYgXs= @@ -227,8 +227,8 @@ github.com/Antonboom/errname v0.1.12 h1:oh9ak2zUtsLp5oaEd/erjB4GPu9w19NyoIskZClD github.com/Antonboom/errname v0.1.12/go.mod h1:bK7todrzvlaZoQagP1orKzWXv59X/x0W0Io2XT1Ssro= github.com/Antonboom/nilnil v0.1.7 h1:ofgL+BA7vlA1K2wNQOsHzLJ2Pw5B5DpWRLdDAVvvTow= github.com/Antonboom/nilnil v0.1.7/go.mod h1:TP+ScQWVEq0eSIxqU8CbdT5DFWoHp0MbP+KMUO1BKYQ= -github.com/Antonboom/testifylint v0.2.3 h1:MFq9zyL+rIVpsvLX4vDPLojgN7qODzWsrnftNX2Qh60= -github.com/Antonboom/testifylint v0.2.3/go.mod h1:IYaXaOX9NbfAyO+Y04nfjGI8wDemC1rUyM/cYolz018= +github.com/Antonboom/testifylint v1.1.2 h1:IdLRermiLRogxY5AumBL4sP0A+qKHQM/AP1Xd7XOTKc= +github.com/Antonboom/testifylint v1.1.2/go.mod h1:9PFi+vWa8zzl4/B/kqmFJcw85ZUv8ReyBzuQCd30+WI= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -254,8 +254,8 @@ github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM= github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0 h1:3ZBs7LAezy8gh0uECsA6CGU43FF3zsx5f4eah5FxTMA= -github.com/GaijinEntertainment/go-exhaustruct/v3 v3.1.0/go.mod h1:rZLTje5A9kFBe0pzhpe2TdhRniBF++PRHQuRpR8esVc= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0 h1:sATXp1x6/axKxz2Gjxv8MALP0bXaNRfQinEwyfMcx8c= +github.com/GaijinEntertainment/go-exhaustruct/v3 v3.2.0/go.mod h1:Nl76DrGNJTA1KJ0LePKBw/vznBX1EHbAZX8mwjR82nI= github.com/IBM/sarama v1.40.1 h1:lL01NNg/iBeigUbT+wpPysuTYW6roHo6kc1QrffRf0k= github.com/IBM/sarama v1.40.1/go.mod h1:+5OFwA5Du9I6QrznhaMHsuwWdWZNMjaBSIxEWEgKOYE= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -268,8 +268,8 @@ github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEV github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/OpenPeeDeeP/depguard/v2 v2.1.0 h1:aQl70G173h/GZYhWf36aE5H0KaujXfVMnn/f1kSDVYY= -github.com/OpenPeeDeeP/depguard/v2 v2.1.0/go.mod h1:PUBgk35fX4i7JDmwzlJwJ+GMe6NfO1723wmJMgPThNQ= +github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA= +github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/sarama v1.37.2 h1:LoBbU0yJPte0cE5TZCGdlzZRmMgMtZU/XgnUKZg9Cv4= github.com/Shopify/sarama v1.37.2/go.mod h1:Nxye/E+YPru//Bpaorfhc3JsSGYwCaDDj+R4bK52U5o= @@ -286,12 +286,12 @@ github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/ github.com/adlio/schema v1.3.3 h1:oBJn8I02PyTB466pZO1UZEn1TV5XLlifBSyMrmHl/1I= github.com/adlio/schema v1.3.3/go.mod h1:1EsRssiv9/Ce2CMzq5DoL7RiMshhuigQxrR4DMV9fHg= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= -github.com/alecthomas/assert/v2 v2.5.0 h1:OJKYg53BQx06/bMRBSPDCO49CbCDNiUQXwdoNrt6x5w= -github.com/alecthomas/assert/v2 v2.5.0/go.mod h1:fw5suVxB+wfYJ3291t0hRTqtGzFYdSwstnRQdaQx2DM= -github.com/alecthomas/go-check-sumtype v0.1.3 h1:M+tqMxB68hcgccRXBMVCPI4UJ+QUfdSx0xdbypKCqA8= -github.com/alecthomas/go-check-sumtype v0.1.3/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= -github.com/alecthomas/repr v0.3.0 h1:NeYzUPfjjlqHY4KtzgKJiWd6sVq2eNUPTi34PiFGjY8= -github.com/alecthomas/repr v0.3.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= +github.com/alecthomas/assert/v2 v2.6.0 h1:o3WJwILtexrEUk3cUVal3oiQY2tfgr/FHWiz/v2n4FU= +github.com/alecthomas/assert/v2 v2.6.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k= +github.com/alecthomas/go-check-sumtype v0.1.4 h1:WCvlB3l5Vq5dZQTFmodqL2g68uHiSwwlWcT5a2FGK0c= +github.com/alecthomas/go-check-sumtype v0.1.4/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -339,8 +339,8 @@ github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJ github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM= github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M= github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k= -github.com/bombsimon/wsl/v3 v3.4.0 h1:RkSxjT3tmlptwfgEgTgU+KYKLI35p/tviNXNXiL2aNU= -github.com/bombsimon/wsl/v3 v3.4.0/go.mod h1:KkIB+TXkqy6MvK9BDZVbZxKNYsE1/oLRJbIFtf14qqo= +github.com/bombsimon/wsl/v4 v4.2.1 h1:Cxg6u+XDWff75SIFFmNsqnIOgob+Q9hG6y/ioKbRFiM= +github.com/bombsimon/wsl/v4 v4.2.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo= github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY= github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ= github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA= @@ -355,17 +355,17 @@ github.com/bufbuild/protocompile v0.6.0 h1:Uu7WiSQ6Yj9DbkdnOe7U4mNKp58y9WDMKDn28 github.com/bufbuild/protocompile v0.6.0/go.mod h1:YNP35qEYoYGme7QMtz5SBCoN4kL4g12jTtjuzRNdjpE= github.com/burdiyan/kafkautil v0.0.0-20190131162249-eaf83ed22d5b h1:gRFujk0F/KYFDEalhpaAbLIwmeiDH53ZgdllJ7UHxyQ= github.com/burdiyan/kafkautil v0.0.0-20190131162249-eaf83ed22d5b/go.mod h1:5hrpM9I1h0fZlTk8JhqaaBaCs76EbCGvFcPtm5SxcCU= -github.com/butuzov/ireturn v0.2.2 h1:jWI36dxXwVrI+RnXDwux2IZOewpmfv930OuIRfaBUJ0= -github.com/butuzov/ireturn v0.2.2/go.mod h1:RfGHUvvAuFFxoHKf4Z8Yxuh6OjlCw1KvR2zM1NFHeBk= +github.com/butuzov/ireturn v0.3.0 h1:hTjMqWw3y5JC3kpnC5vXmFJAWI/m31jaCYQqzkS6PL0= +github.com/butuzov/ireturn v0.3.0/go.mod h1:A09nIiwiqzN/IoVo9ogpa0Hzi9fex1kd9PSD6edP5ZA= github.com/butuzov/mirror v1.1.0 h1:ZqX54gBVMXu78QLoiqdwpl2mgmoOJTk7s4p4o+0avZI= github.com/butuzov/mirror v1.1.0/go.mod h1:8Q0BdQU6rC6WILDiBM60DBfvV78OLJmMmixe7GF45AE= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/catenacyber/perfsprint v0.2.0 h1:azOocHLscPjqXVJ7Mf14Zjlkn4uNua0+Hcg1wTR6vUo= -github.com/catenacyber/perfsprint v0.2.0/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50= -github.com/ccojocar/zxcvbn-go v1.0.1 h1:+sxrANSCj6CdadkcMnvde/GWU1vZiiXRbqYSCalV4/4= -github.com/ccojocar/zxcvbn-go v1.0.1/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= +github.com/catenacyber/perfsprint v0.6.0 h1:VSv95RRkk5+BxrU/YTPcnxuMEWar1iMK5Vyh3fWcBfs= +github.com/catenacyber/perfsprint v0.6.0/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50= +github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg= +github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= @@ -382,7 +382,6 @@ github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iy github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ= github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc= github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww= -github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/cheggaaa/pb v1.0.27/go.mod h1:pQciLPpbU0oxA0h+VJYYLxO+XeDQb5pZijXscXHm81s= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= @@ -397,7 +396,6 @@ github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObk github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= -github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE= @@ -411,6 +409,8 @@ github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa h1:jQCWAUqqlij9Pgj2i/PB79y4KOPYVyFYdROxgaCwdTQ= +github.com/cncf/xds/go v0.0.0-20231128003011-0fa0005c9caa/go.mod h1:x/1Gn8zydmfq8dk6e9PdstVsDgu9RuyIIJqAaF//0IM= github.com/cockroachdb/apd/v2 v2.0.2 h1:weh8u7Cneje73dDh+2tEVLUvyBc89iwepWCD8b8034E= github.com/cockroachdb/apd/v2 v2.0.2/go.mod h1:DDxRlzC2lo3/vSlmSoS7JkqbbrARPuFOGr0B9pvN3Gw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= @@ -431,20 +431,18 @@ github.com/coinbase/rosetta-sdk-go/types v1.0.0 h1:jpVIwLcPoOeCR6o1tU+Xv7r5bMONN github.com/coinbase/rosetta-sdk-go/types v1.0.0/go.mod h1:eq7W2TMRH22GTW0N0beDnN931DW0/WOI1R2sdHNHG4c= github.com/cometbft/cometbft-db v0.9.1 h1:MIhVX5ja5bXNHF8EYrThkG9F7r9kSfv8BX4LWaxWJ4M= github.com/cometbft/cometbft-db v0.9.1/go.mod h1:iliyWaoV0mRwBJoizElCwwRA9Tf7jZJOURcRZF9m60U= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= -github.com/cosmos/cosmos-db v1.0.0 h1:EVcQZ+qYag7W6uorBKFPvX6gRjw6Uq2hIh4hCWjuQ0E= -github.com/cosmos/cosmos-db v1.0.0/go.mod h1:iBvi1TtqaedwLdcrZVYRSSCb6eSy61NLj4UNmdIgs0U= +github.com/cosmos/cosmos-db v1.0.2 h1:hwMjozuY1OlJs/uh6vddqnk9j7VamLv+0DBlbEXbAKs= +github.com/cosmos/cosmos-db v1.0.2/go.mod h1:Z8IXcFJ9PqKK6BIsVOB3QXtkKoqUOp1vRvPT39kOXEA= github.com/cosmos/cosmos-proto v1.0.0-beta.4 h1:aEL7tU/rLOmxZQ9z4i7mzxcLbSCY48OdY7lIWTLG7oU= github.com/cosmos/cosmos-proto v1.0.0-beta.4/go.mod h1:oeB+FyVzG3XrQJbJng0EnV8Vljfk9XvTIpGILNU/9Co= github.com/cosmos/cosmos-sdk/client/v2 v2.0.0-beta.1.0.20240219091002-18ea4c520045 h1:UQEFOXQGeEmtQxj7P69x0pagoGtAtRCXB1Jh2A1c4oM= @@ -456,8 +454,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.4.11 h1:LZcMHrx4FjUgrqQSWeaGC1v/TeuVFqSLa43CC6aWR2g= github.com/cosmos/gogoproto v1.4.11/go.mod h1:/g39Mh8m17X8Q/GDEs5zYTSNaNnInBSohtaxzQnYq1Y= -github.com/cosmos/iavl v1.0.1 h1:D+mYbcRO2wptYzOM1Hxl9cpmmHU1ZEt9T2Wv5nZTeUw= -github.com/cosmos/iavl v1.0.1/go.mod h1:8xIUkgVvwvVrBu81scdPty+/Dx9GqwHnAvXz4cwF7RY= +github.com/cosmos/iavl v1.1.1-0.20240313174921-bfaf5fcda7b3 h1:1b7Bd9y4YC0SFFB9PQ4A9TVdc1vdlSLdSv2m+3mjS1Y= +github.com/cosmos/iavl v1.1.1-0.20240313174921-bfaf5fcda7b3/go.mod h1:8xIUkgVvwvVrBu81scdPty+/Dx9GqwHnAvXz4cwF7RY= github.com/cosmos/ibc-go/modules/capability v1.0.0 h1:r/l++byFtn7jHYa09zlAdSeevo8ci1mVZNO9+V0xsLE= github.com/cosmos/ibc-go/modules/capability v1.0.0/go.mod h1:D81ZxzjZAe0ZO5ambnvn1qedsFQ8lOwtqicG6liLBco= github.com/cosmos/ibc-go/v8 v8.0.0 h1:QKipnr/NGwc+9L7NZipURvmSIu+nw9jOIWTJuDBqOhg= @@ -487,9 +485,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/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/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= -github.com/daixiang0/gci v0.11.2 h1:Oji+oPsp3bQ6bNNgX30NBAVT18P4uBH4sRZnlOlTj7Y= -github.com/daixiang0/gci v0.11.2/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= +github.com/daixiang0/gci v0.12.1 h1:ugsG+KRYny1VK4oqrX4Vtj70bo4akYKa0tgT1DXMYiY= +github.com/daixiang0/gci v0.12.1/go.mod h1:xtHP9N7AHdNvtRNfcx9gwTDfw7FRJx4bZUsiEfiNNAI= github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -520,7 +517,6 @@ github.com/docker/docker v23.0.1+incompatible h1:vjgvJZxprTTE1A37nm+CLNAdwu6xZek github.com/docker/docker v23.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -531,8 +527,8 @@ 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.20240229050000-3b085f30b462 h1:ufl8wDrax5K/33NMX/2P54iJAb5LlHJA8CYIcdfeR+o= github.com/dydxprotocol/cometbft v0.38.6-0.20240229050000-3b085f30b462/go.mod h1:REQN+ObgfYxi39TcYR/Hv95C9bPxY3sYJCvghryj7vY= -github.com/dydxprotocol/cosmos-sdk v0.50.5-0.20240227194839-f4e166bc1057 h1:5Aq2oldr7T5+PEo6HYWA2cTcD4tY5ZM6755pKxhOvNg= -github.com/dydxprotocol/cosmos-sdk v0.50.5-0.20240227194839-f4e166bc1057/go.mod h1:I2uLntIWztPOXW0abkUb3SRphofiLL5DzrvHObyJVkM= +github.com/dydxprotocol/cosmos-sdk v0.50.5-0.20240320193634-44a06cd9428e h1:3HCMOMbqlBAK3dboZSKJjvDZ4eg3N1h7EdaMCpmRt9U= +github.com/dydxprotocol/cosmos-sdk v0.50.5-0.20240320193634-44a06cd9428e/go.mod h1:yQgIqbm+W0FODUwPIihPJOp3igupvNTbPcmyaRYZGTw= github.com/dydxprotocol/cosmos-sdk/store v1.0.3-0.20240227194839-f4e166bc1057 h1:rY2cckHMrpk3+9ggVqp3Zsvfn7AwEjjazvyp4HK3lgw= github.com/dydxprotocol/cosmos-sdk/store v1.0.3-0.20240227194839-f4e166bc1057/go.mod h1:M4hH9rTCui49tnujKeM+BWCiXGQwZ//3QCvl0HbR6Cs= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= @@ -557,12 +553,14 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.m github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/esimonov/ifshort v1.0.4 h1:6SID4yGWfRae/M7hkVDVVyppy8q/v9OuxNdmjLQStBA= github.com/esimonov/ifshort v1.0.4/go.mod h1:Pe8zjlRrJ80+q2CxHLfEOfTwxCZ4O+MuhcHcfgNWTk0= github.com/ethereum/go-ethereum v1.12.0 h1:bdnhLPtqETd4m3mS8BGMNvBTf36bO5bx/hxE2zljOa0= github.com/ethereum/go-ethereum v1.12.0/go.mod h1:/oo2X/dZLJjf2mJ6YT9wcWxa4nNJDBKDBU6sFIpx1Gs= -github.com/ettle/strcase v0.1.1 h1:htFueZyVeE1XNnMEfbqp5r67qAN/4r6ya1ysq8Q+Zcw= -github.com/ettle/strcase v0.1.1/go.mod h1:hzDLsPC7/lwKyBOywSHEP89nt2pDgdy+No1NBA9o9VY= +github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q= +github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= @@ -579,7 +577,6 @@ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8 github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= -github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= @@ -595,12 +592,12 @@ github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/ghostiam/protogetter v0.2.3 h1:qdv2pzo3BpLqezwqfGDLZ+nHEYmc5bUpIdsMbBVwMjw= -github.com/ghostiam/protogetter v0.2.3/go.mod h1:KmNLOsy1v04hKbvZs8EfGI1fk39AgTdRDxWNYPfXVc4= +github.com/ghostiam/protogetter v0.3.4 h1:5SZ+lZSNmNkSbGVSF9hUHhv/b7ELF9Rwchoq7btYo6c= +github.com/ghostiam/protogetter v0.3.4/go.mod h1:A0JgIhs0fgVnotGinjQiKaFVG3waItLJNwPmcMzDnvk= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= -github.com/go-critic/go-critic v0.9.0 h1:Pmys9qvU3pSML/3GEQ2Xd9RZ/ip+aXHKILuxczKGV/U= -github.com/go-critic/go-critic v0.9.0/go.mod h1:5P8tdXL7m/6qnyG6oRAlYLORvoXH0WDypYgAEmagT40= +github.com/go-critic/go-critic v0.11.1 h1:/zBseUSUMytnRqxjlsYNbDDxpu3R2yH8oLXo/FOE8b8= +github.com/go-critic/go-critic v0.11.1/go.mod h1:aZVQR7+gazH6aDEQx4356SD7d8ez8MipYjXbEl5JAKA= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -619,8 +616,11 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= @@ -645,8 +645,9 @@ github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4 github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s= github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw= github.com/go-toolsmith/astequal v1.0.3/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4= -github.com/go-toolsmith/astequal v1.1.0 h1:kHKm1AWqClYn15R0K1KKE4RG614D46n+nqUQ06E1dTw= github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ= +github.com/go-toolsmith/astequal v1.2.0 h1:3Fs3CYZ1k9Vo4FzFhwwewC3CHISHDnVUPC4x0bI2+Cw= +github.com/go-toolsmith/astequal v1.2.0/go.mod h1:c8NZ3+kSFtFY/8lPso4v8LuJjdJiUFVnSuU3s0qrrDY= github.com/go-toolsmith/astfmt v1.1.0 h1:iJVPDPp6/7AaeLJEruMsBUlOYCmvg0MoCfJprsOmcco= github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4= github.com/go-toolsmith/astp v1.1.0 h1:dXPuCl6u2llURjdPLLDxJeZInAeZ0/eZwFJmqZMnpQA= @@ -658,6 +659,8 @@ github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQi github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ= github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus= github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c= +github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U= github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= @@ -669,7 +672,6 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= @@ -737,8 +739,8 @@ github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe h1:6RGUuS7EGotKx6 github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe/go.mod h1:gjqyPShc/m8pEMpk0a3SeagVb0kaqvhscv+i9jI5ZhQ= github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e h1:ULcKCDV1LOZPFxGZaA6TlQbiM3J2GCPnkx/bGF6sX/g= github.com/golangci/gofmt v0.0.0-20231018234816-f50ced29576e/go.mod h1:Pm5KhLPA8gSnQwrQ6ukebRcapGb/BG9iUkdaiCcGHJM= -github.com/golangci/golangci-lint v1.55.2 h1:yllEIsSJ7MtlDBwDJ9IMBkyEUz2fYE0b5B8IUgO1oP8= -github.com/golangci/golangci-lint v1.55.2/go.mod h1:H60CZ0fuqoTwlTvnbyjhpZPWp7KmsjwV2yupIMiMXbM= +github.com/golangci/golangci-lint v1.56.2 h1:dgQzlWHgNbCqJjuxRJhFEnHDVrrjuTGQHJ3RIZMpp/o= +github.com/golangci/golangci-lint v1.56.2/go.mod h1:7CfNO675+EY7j84jihO4iAqDQ80s3HCjcc5M6B7SlZQ= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 h1:MfyDlzVjl1hoaPzPD4Gpb/QgoRfSBR0jdhwGyAWwMSA= github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0/go.mod h1:66R6K6P6VWk9I95jvqGxkqJxVWGFy9XlDwLwVz1RCFg= github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= @@ -808,8 +810,8 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= @@ -828,8 +830,8 @@ github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56 github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601 h1:mrEEilTAUmaAORhssPPkxj84TsHrPMLBGW2Z4SoTxm8= -github.com/gordonklaus/ineffassign v0.0.0-20230610083614-0e73809eb601/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= +github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s= +github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0= github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= @@ -969,8 +971,8 @@ github.com/jcmturner/gokrb5/v8 v8.4.3 h1:iTonLeSJOn7MVUtyMT+arAn5AKAPrkilzhGw8wE github.com/jcmturner/gokrb5/v8 v8.4.3/go.mod h1:dqRwJGXznQrzw6cWmyo6kH+E7jksEQG/CyVWsJEsJO0= github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY= github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= -github.com/jgautheron/goconst v1.6.0 h1:gbMLWKRMkzAc6kYsQL6/TxaoBUg3Jm9LSF/Ih1ADWGA= -github.com/jgautheron/goconst v1.6.0/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= +github.com/jgautheron/goconst v1.7.0 h1:cEqH+YBKLsECnRSd4F4TK5ri8t/aXtt/qoL0Ft252B0= +github.com/jgautheron/goconst v1.7.0/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4= github.com/jhump/protoreflect v1.15.3 h1:6SFRuqU45u9hIZPJAoZ8c28T3nK64BNdp9w6jFonzls= github.com/jhump/protoreflect v1.15.3/go.mod h1:4ORHmSBmlCW8fh3xHmJMGyul1zNqZK4Elxc8qKP+p1k= github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs= @@ -979,6 +981,8 @@ github.com/jinzhu/copier v0.4.0 h1:w3ciUoD19shMCRargcpm0cm91ytaBhDvuRpz1ODO/U8= github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48= github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0= +github.com/jjti/go-spancheck v0.5.2 h1:WXTZG3efY/ji1Vi8mkH+23O3bLeKR6hp3tI3YB7XwKk= +github.com/jjti/go-spancheck v0.5.2/go.mod h1:ARPNI1JRG1V2Rjnd6/2f2NEfghjSVDZGVmruNKlnXU0= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -1007,8 +1011,8 @@ github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSX github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= -github.com/kisielk/errcheck v1.6.3 h1:dEKh+GLHcWm2oN34nMvDzn1sqI0i0WxPvrgiJA5JuM8= -github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw= +github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0= +github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ= github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkHAIKE/contextcheck v1.1.4 h1:B6zAaLhOEEcjvUgIYEqystmnFk1Oemn8bvJhbt0GMb8= @@ -1017,15 +1021,14 @@ github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg= github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= -github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI= -github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= +github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= +github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -1034,8 +1037,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs= github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I= -github.com/kunwardeep/paralleltest v1.0.8 h1:Ul2KsqtzFxTlSU7IP0JusWlLiNqQaloB9vguyjbE558= -github.com/kunwardeep/paralleltest v1.0.8/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= +github.com/kunwardeep/paralleltest v1.0.9 h1:3Sr2IfFNcsMmlqPk1cjTUbJ4zofKPGyHxenwPebgTug= +github.com/kunwardeep/paralleltest v1.0.9/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ= @@ -1061,8 +1064,8 @@ github.com/lovoo/goka v1.1.9/go.mod h1:sWYQv64wibIpvD8bp7bv+4kS9qT0ZTvXavrbNGq0H github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM= github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= -github.com/macabu/inamedparam v0.1.2 h1:RR5cnayM6Q7cDhQol32DE2BGAPGMnffJ31LFE+UklaU= -github.com/macabu/inamedparam v0.1.2/go.mod h1:Xg25QvY7IBRl1KLPV9Rbml8JOMZtF/iAkNkmV7eQgjw= +github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk= +github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I= github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= @@ -1099,8 +1102,8 @@ github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mbilski/exhaustivestruct v1.2.0 h1:wCBmUnSYufAHO6J4AVWY6ff+oxWxsVFrwgOdMUQePUo= github.com/mbilski/exhaustivestruct v1.2.0/go.mod h1:OeTBVxQWoEmB2J2JCHmXWPJ0aksxSUOUy+nvtVEfzXc= -github.com/mgechev/revive v1.3.4 h1:k/tO3XTaWY4DEHal9tWBkkUMJYO/dLDVyMmAQxmIMDc= -github.com/mgechev/revive v1.3.4/go.mod h1:W+pZCMu9qj8Uhfs1iJMQsEFLRozUfvwFwqVvRbSNLVw= +github.com/mgechev/revive v1.3.7 h1:502QY0vQGe9KtYJ9FpxMz9rL+Fc/P13CI5POL4uHCcE= +github.com/mgechev/revive v1.3.7/go.mod h1:RJ16jUbF0OWC3co/+XTxmFNgEpUPwnnA0BRllX2aDNA= github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/minio/highwayhash v1.0.2/go.mod h1:BQskDq+xkJ12lmlUUi7U0M5Swg3EWR+dLTk+kldvVxY= @@ -1119,7 +1122,6 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= -github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/moby/term v0.0.0-20221205130635-1aeaba878587 h1:HfkjXDfhgVaN5rmueG8cL8KKeFNecRCXFhaJ2qZ5SKA= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -1129,7 +1131,6 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/moricho/tparallel v0.3.1 h1:fQKD4U1wRMAYNngDonW5XupoB/ZGJHdpzrWqgyg9krA= github.com/moricho/tparallel v0.3.1/go.mod h1:leENX2cUv7Sv2qDgdi0D0fCftN8fRC67Bcn8pqzeYNI= -github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -1148,12 +1149,12 @@ github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OS github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32 h1:W6apQkHrMkS0Muv8G/TipAy/FJl/rCYT0+EuS8+Z0z4= github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nishanths/exhaustive v0.11.0 h1:T3I8nUGhl/Cwu5Z2hfc92l0e04D2GEW6e0l8pzda2l0= -github.com/nishanths/exhaustive v0.11.0/go.mod h1:RqwDsZ1xY0dNdqHho2z6X+bgzizwbLYOWnZbbl2wLB4= +github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg= +github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs= github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk= github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c= -github.com/nunnatsa/ginkgolinter v0.14.1 h1:khx0CqR5U4ghsscjJ+lZVthp3zjIFytRXPTaQ/TMiyA= -github.com/nunnatsa/ginkgolinter v0.14.1/go.mod h1:nY0pafUSst7v7F637e7fymaMlQqI9c0Wka2fGsDkzWg= +github.com/nunnatsa/ginkgolinter v0.15.2 h1:N2ORxUxPU56R9gsfLIlVVvCv/V/VVou5qVI1oBKBNHg= +github.com/nunnatsa/ginkgolinter v0.15.2/go.mod h1:oYxE7dt1vZI8cK2rZOs3RgTaBN2vggkqnENmoJ8kVvc= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= @@ -1172,22 +1173,20 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= -github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= +github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY= +github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.28.1 h1:MijcGUbfYuznzK/5R4CPNoUP/9Xvuo20sXfEm6XxoTA= -github.com/onsi/gomega v1.28.1/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= +github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0-rc2 h1:2zx/Stx4Wc5pIPDvIxHXvXtQFW/7XWJGmnM7r3wg034= github.com/opencontainers/image-spec v1.1.0-rc2/go.mod h1:3OVijpioIKYWTqjiG0zfF6wvoJ4fAXGbjdZuI2NgsRQ= -github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= -github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= -github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= -github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= +github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss= +github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -1201,8 +1200,8 @@ github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnh github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= -github.com/otiai10/copy v1.11.0 h1:OKBD80J/mLBrwnzXqGtFCzprFSGioo30JcmR4APsNwc= -github.com/otiai10/copy v1.11.0/go.mod h1:rSaLseMUsZFFbsFGc7wCJnnkTAvdc5L6VWxPE4308Ww= +github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU= +github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w= github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE= github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= @@ -1239,8 +1238,8 @@ github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6J github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/polyfloyd/go-errorlint v1.4.5 h1:70YWmMy4FgRHehGNOUask3HtSFSOLKgmDn7ryNe7LqI= -github.com/polyfloyd/go-errorlint v1.4.5/go.mod h1:sIZEbFoDOCnTYYZoVkjc4hTnM459tuWA9H/EkdXwsKk= +github.com/polyfloyd/go-errorlint v1.4.8 h1:jiEjKDH33ouFktyez7sckv6pHWif9B7SuS8cutDXFHw= +github.com/polyfloyd/go-errorlint v1.4.8/go.mod h1:NNCxFcFjZcw3xNjVdCchERkEM6Oz7wta2XJVxRftwO4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= @@ -1330,14 +1329,13 @@ github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71e github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM= github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw= github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ= -github.com/sashamelentyev/usestdlibvars v1.24.0 h1:MKNzmXtGh5N0y74Z/CIaJh4GlB364l0K1RUT08WSWAc= -github.com/sashamelentyev/usestdlibvars v1.24.0/go.mod h1:9cYkq+gYJ+a5W2RPdhfaSCnTVUC1OQP/bSiiBhq3OZE= +github.com/sashamelentyev/usestdlibvars v1.25.0 h1:IK8SI2QyFzy/2OD2PYnhy84dpfNo9qADrRt6LH8vSzU= +github.com/sashamelentyev/usestdlibvars v1.25.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE= github.com/secure-systems-lab/go-securesystemslib v0.4.0/go.mod h1:FGBZgq2tXWICsxWQW1msNf49F0Pf2Op5Htayx335Qbs= -github.com/securego/gosec/v2 v2.18.2 h1:DkDt3wCiOtAHf1XkiXZBhQ6m6mK/b9T/wD257R3/c+I= -github.com/securego/gosec/v2 v2.18.2/go.mod h1:xUuqSF6i0So56Y2wwohWAmB07EdBkUN6crbLlHwbyJs= +github.com/securego/gosec/v2 v2.19.0 h1:gl5xMkOI0/E6Hxx0XCY2XujA3V7SNSefA8sC+3f1gnk= +github.com/securego/gosec/v2 v2.19.0/go.mod h1:hOkDcHz9J/XIgIlPDXalxjeVYsHxoWUc5zJSHxcB8YM= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= @@ -1351,7 +1349,6 @@ github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPx github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE= @@ -1360,8 +1357,10 @@ github.com/sivchari/nosnakecase v1.7.0 h1:7QkpWIRMe8x25gckkFd2A5Pi6Ymo0qgr4JrhGt github.com/sivchari/nosnakecase v1.7.0/go.mod h1:CwDzrzPea40/GB6uynrNLiorAlgFRvRbFSgJx2Gs+QY= github.com/sivchari/tenv v1.7.1 h1:PSpuD4bu6fSmtWMxSGWcvqUUgIn7k3yOJhOIzVWn8Ak= github.com/sivchari/tenv v1.7.1/go.mod h1:64yStXKSOxDfX47NlhVwND4dHwfZDdbp2Lyl018Icvg= -github.com/skip-mev/slinky v0.2.2 h1:LlJDfmr45l3G+3GJhNRyWS72+FKAWjQxDfhZkzOC0/w= -github.com/skip-mev/slinky v0.2.2/go.mod h1:HBzskXU/8d7cNthgmksHp+KZHQ7vZbGazfsnT2SqQhI= +github.com/skip-mev/chaintestutil v0.0.0-20240116134208-3e49bf514803 h1:VRRVYN3wsOIOqVT3e3nDh3vyUl6RvF9QwdK4BvgPP9c= +github.com/skip-mev/chaintestutil v0.0.0-20240116134208-3e49bf514803/go.mod h1:LF2koCTmygQnz11yjSfHvNP8axdyZ2lTEw0EwI+dnno= +github.com/skip-mev/slinky v0.2.1-0.20240301171048-53cc00fdca4e h1:bypym60EEfWm9N61w/YPKCVb1zmqf2gZRt/VIi2vKNo= +github.com/skip-mev/slinky v0.2.1-0.20240301171048-53cc00fdca4e/go.mod h1:7Ea2K3ttw8XwQRezfSnMYEafbiwV7QOeIVjLbCjSiM0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -1424,7 +1423,6 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c h1:+aPplBwWcHBo6q9xrfWdMrT9o4kltkmmvpemgIjep/8= @@ -1437,8 +1435,8 @@ github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag= github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY= -github.com/tetafro/godot v1.4.15 h1:QzdIs+XB8q+U1WmQEWKHQbKmCw06QuQM7gLx/dky2RM= -github.com/tetafro/godot v1.4.15/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio= +github.com/tetafro/godot v1.4.16 h1:4ChfhveiNLk4NveAZ9Pu2AN8QZ2nkUGFuadM9lrr5D0= +github.com/tetafro/godot v1.4.16/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/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M= @@ -1467,8 +1465,8 @@ github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI= github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4= -github.com/ultraware/whitespace v0.0.5 h1:hh+/cpIcopyMYbZNVov9iSxvJU3OYQg78Sfaqzi/CzI= -github.com/ultraware/whitespace v0.0.5/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= +github.com/ultraware/whitespace v0.1.0 h1:O1HKYoh0kIeqE8sFqZf1o0qbORXUCOQFrlaQyZsczZw= +github.com/ultraware/whitespace v0.1.0/go.mod h1:/se4r3beMFNmewJ4Xmz0nMQ941GJt+qmSHGP9emHYe0= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -1476,10 +1474,8 @@ github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa h1:5SqCsI/2Qya2bC github.com/urfave/cli/v2 v2.17.2-0.20221006022127-8f469abc00aa/go.mod h1:1CNUng3PtjQMtRzJO4FMXBQvkGtuYRxxiR9xMa7jMwI= github.com/uudashr/gocognit v1.1.2 h1:l6BAEKJqQH2UpKAPKdMfZf5kE4W/2xk8pfU1OVLvniI= github.com/uudashr/gocognit v1.1.2/go.mod h1:aAVdLURqcanke8h3vg35BC++eseDm66Z7KmchI5et4k= -github.com/vektra/mockery/v2 v2.40.1 h1:8D01rBqloDLDHKZGXkyUD9Yj5Z+oDXBqDZ+tRXYM/oA= -github.com/vektra/mockery/v2 v2.40.1/go.mod h1:dPzGtjT0/Uu4hqpF6QNHwz+GLago7lq1bxdj9wHbGKo= -github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= -github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vektra/mockery/v2 v2.42.0 h1:xnP1KXjpcc1GD8jHRjgdpRIW4LDK5MdSMrhbJizAmaI= +github.com/vektra/mockery/v2 v2.42.0/go.mod h1:XNTE9RIu3deGAGQRVjP1VZxGpQNm0YedZx4oDs3prr8= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= @@ -1496,8 +1492,8 @@ github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk= github.com/yeya24/promlinter v0.2.0 h1:xFKDQ82orCU5jQujdaD8stOHiv8UN68BSdn2a8u8Y3o= github.com/yeya24/promlinter v0.2.0/go.mod h1:u54lkmBOZrpEbQQ6gox2zWKKLKu2SGe+2KOiextY+IA= -github.com/ykadowak/zerologlint v0.1.3 h1:TLy1dTW3Nuc+YE3bYRPToG1Q9Ej78b5UUN6bjbGdxPE= -github.com/ykadowak/zerologlint v0.1.3/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= +github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw= +github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1513,10 +1509,12 @@ github.com/zyedidia/generic v1.0.0 h1:uZL4/2Pv014Cb8bJQuvh30toyaFZ9WpCPg6pIhPu47 github.com/zyedidia/generic v1.0.0/go.mod h1:ly2RBz4mnz1yeuVbQA/VFwGjK3mnHGRj1JuoG336Bis= gitlab.com/bosi/decorder v0.4.1 h1:VdsdfxhstabyhZovHafFw+9eJ6eU0d2CkFNJcZz/NU4= gitlab.com/bosi/decorder v0.4.1/go.mod h1:jecSqWUew6Yle1pCr2eLWTensJMmsxHsBwt+PVbkAqA= -go-simpler.org/assert v0.6.0 h1:QxSrXa4oRuo/1eHMXSBFHKvJIpWABayzKldqZyugG7E= -go-simpler.org/assert v0.6.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= -go-simpler.org/sloglint v0.1.2 h1:IjdhF8NPxyn0Ckn2+fuIof7ntSnVUAqBFcQRrnG9AiM= -go-simpler.org/sloglint v0.1.2/go.mod h1:2LL+QImPfTslD5muNPydAEYmpXIj6o/WYcqnJjLi4o4= +go-simpler.org/assert v0.7.0 h1:OzWWZqfNxt8cLS+MlUp6Tgk1HjPkmgdKBq9qvy8lZsA= +go-simpler.org/assert v0.7.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28= +go-simpler.org/musttag v0.8.0 h1:DR4UTgetNNhPRNo02rkK1hwDTRzAPotN+ZqYpdtEwWc= +go-simpler.org/musttag v0.8.0/go.mod h1:fiNdCkXt2S6je9Eblma3okjnlva9NT1Eg/WUt19rWu8= +go-simpler.org/sloglint v0.4.0 h1:UVJuUJo63iNQNFEOtZ6o1xAgagVg/giVLLvG9nNLobI= +go-simpler.org/sloglint v0.4.0/go.mod h1:v6zJ++j/thFPhefs2wEXoCKwT10yo5nkBDYRCXyqgNQ= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= @@ -1532,16 +1530,26 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1 h1:SpGay3w+nEwMpfVnbqOLH5gY52/foP8RE8UzTZ1pdSE= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.46.1/go.mod h1:4UoMYEZOC0yN/sPGH76KPkkU7zgiEWYWL9vwmbnTJPE= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1 h1:aFJWCqJMNjENlcleuuOkGAPH82y0yULBScfXcIEdS24= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.46.1/go.mod h1:sEGXWArGqc3tVa+ekntsN65DmVbVeW+7lTKTjZF3/Fo= +go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc= +go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo= +go.opentelemetry.io/otel/metric v1.21.0 h1:tlYWfeo+Bocx5kLEloTjbcDwBuELRrIFxwdQ36PlJu4= +go.opentelemetry.io/otel/metric v1.21.0/go.mod h1:o1p3CA8nNHW8j5yuQLdc1eeqEaPfzug24uvsyIEJRWM= +go.opentelemetry.io/otel/sdk v1.21.0 h1:FTt8qirL1EysG6sTQRZ5TokkU8d0ugCj8htOgThZXQ8= +go.opentelemetry.io/otel/sdk v1.21.0/go.mod h1:Nna6Yv7PWTdgJHVRD9hIYywQBRx7pbox6nwBnZIxl/E= +go.opentelemetry.io/otel/trace v1.21.0 h1:WD9i5gzvoUPuXIXH24ZNBudiarZDKuekPqi/E8fpfLc= +go.opentelemetry.io/otel/trace v1.21.0/go.mod h1:LGbsEB0f9LGjN+OZaQQ26sohbOmiMR+BaslueVtS/qQ= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.tmz.dev/musttag v0.7.2 h1:1J6S9ipDbalBSODNT5jCep8dhZyMr4ttnjQagmGYR5s= -go.tmz.dev/musttag v0.7.2/go.mod h1:m6q5NiiSKMnQYokefa2xGoyoXnrswCbJ0AWYzf4Zs28= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= -go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -1551,8 +1559,8 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE= go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA= go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4= @@ -1591,8 +1599,8 @@ golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5C golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc= golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= -golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833 h1:jWGQJV4niP+CCmFW9ekjA9Zx8vYORzOUH2/Nl5WPuLQ= -golang.org/x/exp/typeparams v0.0.0-20230307190834-24139beb5833/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= +golang.org/x/exp/typeparams v0.0.0-20231219180239-dc181d75b848 h1:UhRVJ0i7bF9n/Hd8YjW3eKjlPVBHzbQdxrBgjbSKl64= +golang.org/x/exp/typeparams v0.0.0-20231219180239-dc181d75b848/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -1667,7 +1675,6 @@ golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= @@ -1758,7 +1765,6 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1768,7 +1774,6 @@ golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191220142924-d4481acd189f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1817,13 +1822,11 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2031,8 +2034,8 @@ google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.153.0 h1:N1AwGhielyKFaUqH07/ZSIQR3uNPcV7NVw0vj+j4iR4= -google.golang.org/api v0.153.0/go.mod h1:3qNJX5eOmhiWYc67jRA/3GsDw97UFb5ivv7Y2PrriAY= +google.golang.org/api v0.155.0 h1:vBmGhCYs0djJttDNynWo44zosHlPvHmA0XiN2zP2DtA= +google.golang.org/api v0.155.0/go.mod h1:GI5qK5f40kCpHfPn6+YzGAByIKWv8ujFnmoWm7Igduk= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -2149,12 +2152,12 @@ google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqw google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= google.golang.org/genproto v0.0.0-20221025140454-527a21cfbd71/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= -google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3 h1:1hfbdAfFbkmpg41000wDVqr7jUpK/Yo+LPnIxxGzmkg= -google.golang.org/genproto v0.0.0-20231211222908-989df2bf70f3/go.mod h1:5RBcpGRxr25RbDzY5w+dmaqpSEvl8Gwl1x2CICf60ic= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0 h1:s1w3X6gQxwrLEpxnLd/qXTVLgQE2yXwaOaoa6IlY/+o= -google.golang.org/genproto/googleapis/api v0.0.0-20231212172506-995d672761c0/go.mod h1:CAny0tYF+0/9rmDB9fahA9YLzX3+AEVl1qXbv5hhj6c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0 h1:/jFB8jK5R3Sq3i/lmeZO0cATSzFfZaJq1J2Euan3XKU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231212172506-995d672761c0/go.mod h1:FUoWkonphQm3RhTS+kOEhF8h0iDpm4tdXolVCeZ9KKA= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= +google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80 h1:Lj5rbfG876hIAYFjqiJnPHfhXbv+nzTWfm04Fg/XSVU= +google.golang.org/genproto/googleapis/api v0.0.0-20240123012728-ef4313101c80/go.mod h1:4jWUdICTdgc3Ibxmr8nAJiiLHwQBY0UI0XZcEMaFKaA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80 h1:AjyfHzEPEFp/NpvfN5g+KDla3EMojjhRVZc1i7cj+oM= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240123012728-ef4313101c80/go.mod h1:PAREbraiVEVGVdTZsVWjSbbTtSyGbAgIIvni8a8CD5s= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM= @@ -2196,8 +2199,8 @@ google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACu google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= -google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/grpc v1.62.0 h1:HQKZ/fa1bXkX1oFOvSjmZEUL8wLSaZTjCcLAlmZRtdk= +google.golang.org/grpc v1.62.0/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= 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= @@ -2280,8 +2283,8 @@ mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wp mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= -mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d h1:3rvTIIM22r9pvXk+q3swxUQAQOxksVMGK7sml4nG57w= -mvdan.cc/unparam v0.0.0-20221223090309-7455f1af531d/go.mod h1:IeHQjmn6TOD+e4Z3RFiZMMsLVL+A96Nvptar8Fj71is= +mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14 h1:zCr3iRRgdk5eIikZNDphGcM6KGVTx3Yu+/Uu9Es254w= +mvdan.cc/unparam v0.0.0-20240104100049-c549a3470d14/go.mod h1:ZzZjEpJDOmx8TdVU6umamY3Xy0UAQUI2DHbf05USVbI= nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= nhooyr.io/websocket v1.8.10 h1:mv4p+MnGrLDcPlBoWsvPP7XCzTYMXP9F9eIGoKbgx7Q= nhooyr.io/websocket v1.8.10/go.mod h1:rN9OFWIUwuxg4fR5tELlYC04bXYowCP9GX47ivo2l+c= diff --git a/protocol/indexer/events/events.pb.go b/protocol/indexer/events/events.pb.go index 44d0cccdde..40208d144e 100644 --- a/protocol/indexer/events/events.pb.go +++ b/protocol/indexer/events/events.pb.go @@ -1630,6 +1630,8 @@ type PerpetualMarketCreateEventV1 struct { // The liquidity_tier that this perpetual is associated with. // Defined in perpetuals.perpetual LiquidityTier uint32 `protobuf:"varint,10,opt,name=liquidity_tier,json=liquidityTier,proto3" json:"liquidity_tier,omitempty"` + // Market type of the perpetual. + MarketType types.PerpetualMarketType `protobuf:"varint,11,opt,name=market_type,json=marketType,proto3,enum=dydxprotocol.indexer.protocol.v1.PerpetualMarketType" json:"market_type,omitempty"` } func (m *PerpetualMarketCreateEventV1) Reset() { *m = PerpetualMarketCreateEventV1{} } @@ -1735,6 +1737,13 @@ func (m *PerpetualMarketCreateEventV1) GetLiquidityTier() uint32 { return 0 } +func (m *PerpetualMarketCreateEventV1) GetMarketType() types.PerpetualMarketType { + if m != nil { + return m.MarketType + } + return types.PerpetualMarketType_PERPETUAL_MARKET_TYPE_UNSPECIFIED +} + // LiquidityTierUpsertEventV1 message contains all the information to // create/update a Liquidity Tier on the dYdX chain. type LiquidityTierUpsertEventV1 struct { @@ -2139,138 +2148,140 @@ func init() { } var fileDescriptor_6331dfb59c6fd2bb = []byte{ - // 2091 bytes of a gzipped FileDescriptorProto + // 2122 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xcb, 0x6f, 0x1b, 0xc9, - 0xd1, 0xd7, 0x90, 0x14, 0x25, 0x95, 0x44, 0x99, 0x6c, 0x4b, 0x32, 0x25, 0x7d, 0x9f, 0xec, 0x0c, + 0xd1, 0x17, 0xc9, 0x11, 0x25, 0x15, 0x45, 0x99, 0x6c, 0x4b, 0x32, 0x25, 0x7d, 0x9f, 0xec, 0x0c, 0x10, 0xc0, 0xf0, 0xee, 0x52, 0x96, 0xb3, 0x09, 0x16, 0x39, 0x04, 0x11, 0xf5, 0x58, 0xd1, 0xb0, - 0x64, 0xee, 0x90, 0xf2, 0xee, 0x3a, 0xc1, 0x4e, 0x5a, 0x33, 0x4d, 0xaa, 0xa1, 0x79, 0x79, 0xba, - 0x29, 0x5b, 0x06, 0x72, 0x4e, 0x0e, 0x01, 0x12, 0x20, 0xa7, 0x1c, 0x72, 0xc8, 0x21, 0x97, 0x00, - 0x39, 0x04, 0xc8, 0x75, 0x4f, 0xb9, 0xec, 0x2d, 0x8b, 0x5c, 0x12, 0xe4, 0xb0, 0x08, 0xec, 0x43, - 0xfe, 0x8d, 0xa0, 0x1f, 0x33, 0x24, 0xc5, 0x87, 0x69, 0x4b, 0x7b, 0x12, 0xa7, 0x7e, 0x5d, 0xbf, - 0xaa, 0xae, 0xaa, 0xee, 0xae, 0x6e, 0xc1, 0x5d, 0xf7, 0xc2, 0x7d, 0x11, 0xc5, 0x21, 0x0f, 0x9d, - 0xd0, 0xdb, 0xa4, 0x81, 0x4b, 0x5e, 0x90, 0x78, 0x93, 0x9c, 0x93, 0x80, 0x33, 0xfd, 0xa7, 0x22, - 0x61, 0xb4, 0xde, 0x3b, 0xb2, 0xa2, 0x47, 0x56, 0xd4, 0x90, 0xb5, 0x55, 0x27, 0x64, 0x7e, 0xc8, - 0x6c, 0x89, 0x6f, 0xaa, 0x0f, 0xa5, 0xb7, 0xb6, 0xd4, 0x0e, 0xdb, 0xa1, 0x92, 0x8b, 0x5f, 0x5a, - 0x7a, 0x7f, 0xa8, 0x5d, 0x76, 0x8a, 0x63, 0xe2, 0x6e, 0xc6, 0xc4, 0x0f, 0xcf, 0xb1, 0x67, 0xc7, - 0x04, 0xb3, 0x30, 0xd0, 0x1a, 0xef, 0x0d, 0xd5, 0x48, 0x05, 0xe7, 0x5b, 0x9b, 0x8e, 0x17, 0x9e, - 0xe8, 0xc1, 0x5b, 0x6f, 0x1c, 0xcc, 0x3a, 0x27, 0xd8, 0x71, 0xc2, 0x4e, 0xc0, 0x95, 0x8a, 0xf9, - 0x77, 0x03, 0x6e, 0xec, 0x77, 0x02, 0x97, 0x06, 0xed, 0xe3, 0xc8, 0xc5, 0x9c, 0x3c, 0xd9, 0x42, - 0xdf, 0x81, 0x85, 0x88, 0xc4, 0x11, 0xe1, 0x1d, 0xec, 0xd9, 0xd4, 0x2d, 0x1b, 0x77, 0x8c, 0xbb, - 0x05, 0x6b, 0x3e, 0x95, 0xd5, 0x5c, 0x74, 0x0f, 0x4a, 0x2d, 0xa5, 0x65, 0x9f, 0x63, 0xaf, 0x43, - 0xec, 0x28, 0xf2, 0xcb, 0x99, 0x3b, 0xc6, 0xdd, 0x69, 0xeb, 0x86, 0x06, 0x9e, 0x08, 0x79, 0x3d, - 0xf2, 0x91, 0x0f, 0x85, 0x64, 0xac, 0x74, 0xa9, 0x9c, 0xbd, 0x63, 0xdc, 0x5d, 0xa8, 0x1e, 0x7c, - 0xf5, 0xcd, 0xed, 0xa9, 0x7f, 0x7f, 0x73, 0xfb, 0xc7, 0x6d, 0xca, 0x4f, 0x3b, 0x27, 0x15, 0x27, - 0xf4, 0x37, 0xfb, 0xfc, 0x3f, 0xff, 0xf0, 0x03, 0xe7, 0x14, 0xd3, 0xa0, 0x3b, 0x01, 0x97, 0x5f, - 0x44, 0x84, 0x55, 0x1a, 0x24, 0xa6, 0xd8, 0xa3, 0x2f, 0xf1, 0x89, 0x47, 0x6a, 0x01, 0xb7, 0x16, - 0x34, 0x7d, 0x4d, 0xb0, 0x9b, 0xbf, 0xcd, 0xc0, 0xa2, 0x9e, 0xd1, 0x9e, 0x48, 0xd3, 0x93, 0x2d, - 0xf4, 0x08, 0x66, 0x3a, 0x72, 0x72, 0xac, 0x6c, 0xdc, 0xc9, 0xde, 0x9d, 0x7f, 0xf0, 0x7e, 0x65, - 0x4c, 0x5a, 0x2b, 0x97, 0xe2, 0x51, 0xcd, 0x09, 0x4f, 0xad, 0x84, 0x02, 0xed, 0x42, 0x4e, 0xf8, - 0x21, 0xa7, 0xbb, 0xf8, 0xe0, 0xfe, 0x24, 0x54, 0xda, 0x91, 0x4a, 0xf3, 0x22, 0x22, 0x96, 0xd4, - 0x36, 0x7d, 0xc8, 0x89, 0x2f, 0xb4, 0x04, 0xc5, 0xe6, 0xe7, 0xf5, 0x3d, 0xfb, 0xf8, 0xa8, 0x51, - 0xdf, 0xdb, 0xa9, 0xed, 0xd7, 0xf6, 0x76, 0x8b, 0x53, 0xe8, 0x16, 0xdc, 0x94, 0xd2, 0xba, 0xb5, - 0x77, 0x58, 0x3b, 0x3e, 0xb4, 0x1b, 0xdb, 0x87, 0xf5, 0x47, 0x7b, 0x45, 0x03, 0xdd, 0x86, 0x75, - 0x09, 0xec, 0x1f, 0x1f, 0xed, 0xd6, 0x8e, 0x3e, 0xb6, 0xad, 0xed, 0xe6, 0x9e, 0xbd, 0x7d, 0xb4, - 0x6b, 0xd7, 0x8e, 0x76, 0xf7, 0x3e, 0x2b, 0x66, 0xd0, 0x32, 0x94, 0xfa, 0x34, 0x9f, 0x3c, 0x6e, - 0xee, 0x15, 0xb3, 0xe6, 0xdf, 0x32, 0x50, 0x38, 0xc4, 0xf1, 0x19, 0xe1, 0x49, 0x50, 0xd6, 0x61, - 0xce, 0x97, 0x82, 0x6e, 0x8a, 0x67, 0x95, 0xa0, 0xe6, 0xa2, 0xa7, 0xb0, 0x10, 0xc5, 0xd4, 0x21, - 0xb6, 0x9a, 0xb4, 0x9c, 0xeb, 0xfc, 0x83, 0xef, 0x8f, 0x9d, 0xab, 0xa2, 0xaf, 0x0b, 0x35, 0x15, - 0x3a, 0x6d, 0xe9, 0x60, 0xca, 0x9a, 0x8f, 0xba, 0x52, 0xf4, 0x29, 0x14, 0xb4, 0x61, 0x27, 0x26, - 0x82, 0x3c, 0x2b, 0xc9, 0xef, 0x4f, 0x40, 0xbe, 0x23, 0x15, 0xba, 0xbc, 0x0b, 0x7e, 0x8f, 0xb8, - 0x87, 0xd8, 0x0f, 0x5d, 0xda, 0xba, 0x28, 0xe7, 0x26, 0x26, 0x3e, 0x94, 0x0a, 0x03, 0xc4, 0x4a, - 0x5c, 0x9d, 0x81, 0x69, 0x39, 0xda, 0x7c, 0x08, 0xe5, 0x51, 0xb3, 0x44, 0x15, 0xb8, 0xa9, 0x42, - 0xf6, 0x9c, 0xf2, 0x53, 0x9b, 0xbc, 0x88, 0xc2, 0x80, 0x04, 0x5c, 0x46, 0x36, 0x67, 0x95, 0x24, - 0xf4, 0x29, 0xe5, 0xa7, 0x7b, 0x1a, 0x30, 0x3f, 0x83, 0x92, 0xe2, 0xaa, 0x62, 0x96, 0x92, 0x20, - 0xc8, 0x45, 0x98, 0xc6, 0x52, 0x6b, 0xce, 0x92, 0xbf, 0xd1, 0x26, 0x2c, 0xf9, 0x34, 0xb0, 0x15, - 0xb9, 0x73, 0x8a, 0x83, 0x76, 0x77, 0xb9, 0x15, 0xac, 0x92, 0x4f, 0x03, 0xe9, 0xcd, 0x8e, 0x44, - 0xea, 0x91, 0x6f, 0x76, 0xe0, 0xe6, 0x90, 0x70, 0xa1, 0x2a, 0xe4, 0x4e, 0x30, 0x23, 0x92, 0x7b, - 0xfe, 0x41, 0x65, 0x82, 0xa8, 0xf4, 0x78, 0x66, 0x49, 0x5d, 0xb4, 0x06, 0xb3, 0xe9, 0xcc, 0x84, - 0xfd, 0x92, 0x95, 0x7e, 0x9b, 0x9f, 0x27, 0x66, 0xfb, 0x82, 0x79, 0x1d, 0x66, 0xcd, 0x3f, 0x1b, - 0x50, 0x68, 0x84, 0x9d, 0xd8, 0x21, 0x8f, 0x5b, 0x62, 0x49, 0x31, 0xf4, 0x53, 0x28, 0x74, 0xf7, - 0xb2, 0xa4, 0x82, 0x47, 0x56, 0x68, 0x2a, 0x38, 0xdf, 0xaa, 0xd4, 0x94, 0xac, 0x91, 0x6a, 0xd7, - 0x5c, 0x91, 0x70, 0xd6, 0xf3, 0x8d, 0x3e, 0x84, 0x19, 0xec, 0xba, 0x31, 0x61, 0x4c, 0xce, 0x72, - 0xae, 0x5a, 0xfe, 0xc7, 0x5f, 0x3f, 0x58, 0xd2, 0x1b, 0xfc, 0xb6, 0x42, 0x1a, 0x3c, 0xa6, 0x41, - 0xfb, 0x60, 0xca, 0x4a, 0x86, 0x56, 0x67, 0x21, 0xcf, 0xa4, 0x93, 0xe6, 0x9f, 0xb2, 0x70, 0xa3, - 0x19, 0xe3, 0x80, 0xb5, 0x48, 0x9c, 0xc4, 0xa1, 0x0d, 0x4b, 0x8c, 0x04, 0x2e, 0x89, 0xed, 0xeb, - 0x73, 0xdc, 0x42, 0x8a, 0xb2, 0x57, 0x86, 0x7c, 0xb8, 0x15, 0x13, 0x87, 0x46, 0x94, 0x04, 0xfc, - 0x92, 0xad, 0xcc, 0x55, 0x6c, 0x2d, 0xa7, 0xac, 0x7d, 0xe6, 0x56, 0x61, 0x16, 0x33, 0xa6, 0xb6, - 0x91, 0xac, 0x2c, 0xc9, 0x19, 0xf9, 0x5d, 0x73, 0xd1, 0x0a, 0xe4, 0xb1, 0x2f, 0x86, 0xc9, 0x95, - 0x98, 0xb3, 0xf4, 0x17, 0xaa, 0x42, 0x5e, 0xf9, 0x5d, 0x9e, 0x96, 0x0e, 0xdd, 0x1b, 0x5b, 0x14, - 0x7d, 0x89, 0xb7, 0xb4, 0x26, 0x3a, 0x80, 0xb9, 0xd4, 0x9f, 0x72, 0xfe, 0xad, 0x69, 0xba, 0xca, - 0xe6, 0x3f, 0xb3, 0x50, 0x7c, 0x1c, 0xbb, 0x24, 0xde, 0xa7, 0x9e, 0x97, 0x64, 0xeb, 0x18, 0xe6, - 0x7d, 0x7c, 0x46, 0x62, 0x3b, 0x14, 0xc8, 0xf8, 0xe2, 0x1d, 0x12, 0x38, 0xc9, 0xa7, 0x0f, 0x0e, - 0x90, 0x44, 0x52, 0x82, 0xf6, 0x61, 0x5a, 0x11, 0x66, 0xde, 0x85, 0xf0, 0x60, 0xca, 0x52, 0xea, - 0xe8, 0x0b, 0x28, 0x79, 0xf4, 0x59, 0x87, 0xba, 0x98, 0xd3, 0x30, 0xd0, 0x4e, 0xaa, 0xed, 0x6e, - 0x73, 0x6c, 0x14, 0x1e, 0x75, 0xb5, 0x24, 0xa5, 0xdc, 0xed, 0x8a, 0xde, 0x25, 0x29, 0xba, 0x0d, - 0xf3, 0x2d, 0xea, 0x79, 0xb6, 0x4e, 0x5f, 0x56, 0xa6, 0x0f, 0x84, 0x68, 0x5b, 0xa5, 0x50, 0x9e, - 0x1e, 0x22, 0x3e, 0x2d, 0x42, 0x64, 0x16, 0x91, 0x38, 0x3d, 0xce, 0x48, 0xbc, 0x4f, 0x88, 0x00, - 0x79, 0x0a, 0xe6, 0x15, 0xc8, 0x13, 0xf0, 0x7d, 0x40, 0x3c, 0xe4, 0xd8, 0xb3, 0x05, 0x1b, 0x71, - 0x6d, 0xa9, 0x55, 0x9e, 0x91, 0x16, 0x8a, 0x12, 0xd9, 0x97, 0xc0, 0xa1, 0x90, 0x0f, 0x8c, 0x96, - 0x34, 0xe5, 0xd9, 0x81, 0xd1, 0x4d, 0x21, 0xaf, 0x16, 0x60, 0x9e, 0x77, 0xb3, 0x66, 0xfe, 0x2a, - 0x0b, 0x37, 0x77, 0x89, 0x47, 0xce, 0x49, 0x8c, 0xdb, 0x3d, 0xfd, 0xc0, 0x4f, 0x00, 0x92, 0x19, - 0x93, 0xab, 0x2d, 0xc0, 0x24, 0xc5, 0x5d, 0x3a, 0x41, 0x1e, 0xb6, 0x5a, 0x8c, 0x70, 0x4e, 0x83, - 0xf6, 0x95, 0x56, 0x5c, 0x42, 0xde, 0xa5, 0x1b, 0x68, 0xcd, 0xb2, 0x83, 0xad, 0xd9, 0xa5, 0xd4, - 0xe5, 0x06, 0x52, 0x77, 0x1f, 0x96, 0x54, 0x48, 0x9f, 0x75, 0x42, 0x4e, 0xec, 0x67, 0x1d, 0x1c, - 0xf0, 0x8e, 0xcf, 0x64, 0x16, 0x73, 0x96, 0x0a, 0xf7, 0x27, 0x02, 0xfa, 0x44, 0x23, 0x68, 0x19, - 0xf2, 0x94, 0xd9, 0x27, 0x9d, 0x0b, 0x99, 0xcc, 0x59, 0x6b, 0x9a, 0xb2, 0x6a, 0xe7, 0x42, 0x9c, - 0x78, 0x94, 0xd9, 0x2d, 0x1a, 0x60, 0xcf, 0x16, 0x0e, 0x7a, 0xc4, 0x17, 0x8b, 0x71, 0x46, 0x8e, - 0x29, 0x51, 0xb6, 0x2f, 0x90, 0x46, 0x0a, 0x98, 0xbf, 0xcc, 0x00, 0x1a, 0xac, 0xbf, 0x6f, 0x37, - 0x1b, 0x77, 0x60, 0x41, 0x34, 0xc8, 0xb6, 0x38, 0x49, 0x93, 0x1d, 0xb0, 0x60, 0x81, 0x90, 0xd5, - 0x31, 0x8d, 0x6b, 0xee, 0x24, 0x21, 0xfd, 0x7f, 0x00, 0x15, 0x31, 0x46, 0x5f, 0x12, 0x1d, 0xd1, - 0x39, 0x29, 0x69, 0xd0, 0x97, 0xa4, 0x27, 0x3c, 0xd3, 0xbd, 0xe1, 0x59, 0x83, 0x59, 0xd6, 0x39, - 0xe1, 0xd4, 0x39, 0x63, 0x32, 0x6e, 0x39, 0x2b, 0xfd, 0x36, 0xff, 0x9b, 0x81, 0x5b, 0x5d, 0xcf, - 0xfb, 0x1b, 0x89, 0xa7, 0xd7, 0x79, 0xb4, 0x5d, 0x3a, 0xd8, 0x5e, 0xc2, 0xba, 0xea, 0xe8, 0x5c, - 0xbb, 0x3b, 0xe9, 0x28, 0x64, 0x54, 0x24, 0x84, 0x95, 0xb3, 0xb2, 0x3b, 0xfe, 0xe1, 0xc4, 0x96, - 0xea, 0x09, 0x47, 0x5d, 0x53, 0x58, 0xab, 0x9a, 0x7e, 0x00, 0x61, 0x28, 0x80, 0x5b, 0x89, 0x6d, - 0x75, 0x60, 0x74, 0xed, 0xe6, 0xa4, 0xdd, 0x1f, 0x4c, 0x6c, 0x77, 0x5b, 0xe8, 0xa7, 0x36, 0x97, - 0x35, 0x6d, 0x9f, 0x94, 0x3d, 0xcc, 0xcd, 0x66, 0x8a, 0x59, 0xf3, 0x0f, 0x00, 0x4b, 0x0d, 0x8e, - 0x39, 0x69, 0x75, 0x3c, 0x59, 0x71, 0x49, 0x98, 0x7d, 0x98, 0x97, 0xbb, 0x84, 0x1d, 0x79, 0xd8, - 0x49, 0xda, 0x93, 0x87, 0xe3, 0x8f, 0x90, 0x21, 0x3c, 0xfd, 0xc2, 0xba, 0xe0, 0xf2, 0x93, 0x2e, - 0x12, 0xc2, 0x54, 0x86, 0x42, 0x28, 0x28, 0x73, 0xfa, 0x9a, 0xa7, 0x77, 0xeb, 0x83, 0x2b, 0x1a, - 0xb4, 0x14, 0x9b, 0x6a, 0x5a, 0xc3, 0x1e, 0x09, 0xfa, 0xb5, 0x01, 0xeb, 0x4e, 0x18, 0xb8, 0x32, - 0x1a, 0xd8, 0xb3, 0x7b, 0x26, 0x2b, 0x97, 0xa9, 0x3a, 0x7a, 0x0f, 0xdf, 0xde, 0xfe, 0x4e, 0x97, - 0x74, 0xc8, 0x9c, 0x57, 0x9d, 0x51, 0xf0, 0x08, 0x8f, 0x78, 0x4c, 0xdb, 0x6d, 0x12, 0x13, 0x57, - 0x9f, 0xe2, 0xd7, 0xe0, 0x51, 0x33, 0xa1, 0x1c, 0xee, 0x51, 0x0a, 0xa3, 0x5f, 0x18, 0xb0, 0xea, - 0x85, 0x41, 0xdb, 0xe6, 0x24, 0xf6, 0x07, 0x22, 0x34, 0xf3, 0xae, 0x25, 0xf1, 0x28, 0x0c, 0xda, - 0x4d, 0x12, 0xfb, 0x43, 0xc2, 0xb3, 0xe2, 0x0d, 0xc5, 0xd6, 0x7e, 0x06, 0xe5, 0x51, 0x85, 0x84, - 0x76, 0x93, 0xa6, 0xe1, 0x9d, 0xba, 0x10, 0xdd, 0x32, 0xac, 0x7d, 0x69, 0xc0, 0xca, 0xf0, 0xd2, - 0x41, 0x4f, 0xa1, 0x28, 0xab, 0x92, 0xb8, 0x3a, 0x06, 0xe9, 0xa6, 0x73, 0xff, 0xed, 0x6c, 0xd5, - 0x5c, 0x6b, 0x51, 0x33, 0xe9, 0x6f, 0xf4, 0x31, 0xe4, 0xd5, 0x83, 0x86, 0xbe, 0x2f, 0x8f, 0x68, - 0x4f, 0xd4, 0x1b, 0x48, 0xa5, 0xd7, 0x31, 0x4b, 0xaa, 0x59, 0x5a, 0x7d, 0xcd, 0x81, 0xf5, 0x31, - 0x95, 0x77, 0x4d, 0x41, 0xfa, 0xf9, 0xa0, 0x91, 0x9e, 0x62, 0x42, 0x5f, 0x00, 0x4a, 0xcb, 0xf5, - 0xea, 0xa1, 0x2a, 0xa6, 0x5c, 0x5a, 0x22, 0xaa, 0x60, 0x54, 0xed, 0x5c, 0xcf, 0x04, 0xd3, 0xab, - 0xac, 0xda, 0x1d, 0x1f, 0xe6, 0x66, 0xb3, 0xc5, 0x9c, 0xf9, 0x47, 0x03, 0x90, 0xdc, 0x3c, 0xfb, - 0x2f, 0x8c, 0x8b, 0x90, 0x49, 0x9f, 0x06, 0x32, 0x54, 0xb6, 0xf3, 0xec, 0xc2, 0x3f, 0x09, 0x3d, - 0x75, 0x29, 0xb2, 0xf4, 0x97, 0x38, 0x1e, 0x4f, 0x31, 0xb3, 0xd5, 0x95, 0x59, 0x9e, 0x9f, 0xb3, - 0xd6, 0xdc, 0x29, 0x66, 0xea, 0x36, 0xd7, 0xff, 0xd0, 0x90, 0xbb, 0xf4, 0xd0, 0xf0, 0x1e, 0x94, - 0x30, 0x0f, 0x7d, 0xea, 0xd8, 0x31, 0x61, 0xa1, 0xd7, 0x11, 0x81, 0x97, 0x5b, 0x53, 0xc9, 0x2a, - 0x2a, 0xc0, 0x4a, 0xe5, 0xe6, 0x97, 0x59, 0xf8, 0xbf, 0xf4, 0x60, 0x19, 0x76, 0xc5, 0xbd, 0xec, - 0xf1, 0x9b, 0x4f, 0xff, 0x15, 0xc8, 0x8b, 0x13, 0x99, 0xc4, 0xd2, 0xef, 0x39, 0x4b, 0x7f, 0x8d, - 0x77, 0xfa, 0x00, 0xf2, 0x8c, 0x63, 0xde, 0x51, 0x3d, 0xd3, 0xe2, 0x24, 0xa9, 0xdf, 0xd1, 0x26, - 0x1b, 0x52, 0xcf, 0xd2, 0xfa, 0xe8, 0x47, 0xb0, 0xae, 0xfb, 0x2f, 0xdb, 0x09, 0x83, 0x73, 0x12, - 0x33, 0xd1, 0xce, 0xa7, 0x57, 0xec, 0xbc, 0x0c, 0xc4, 0xaa, 0x1e, 0xb2, 0x93, 0x8e, 0x48, 0x1e, - 0x11, 0x86, 0x87, 0x6f, 0x66, 0x78, 0xf8, 0xd0, 0x3d, 0x28, 0x25, 0x0d, 0x88, 0x38, 0xfd, 0x6d, - 0xf1, 0x4b, 0xb6, 0xd2, 0x05, 0xeb, 0x46, 0x02, 0xd4, 0x49, 0xdc, 0xa4, 0xce, 0x99, 0xe8, 0xbb, - 0x19, 0x27, 0x91, 0x2d, 0xae, 0xdf, 0xdd, 0x16, 0x71, 0x4e, 0xf5, 0xdd, 0x02, 0x11, 0x97, 0xf4, - 0xb4, 0x41, 0xfc, 0x2e, 0x2c, 0xaa, 0x9e, 0x8b, 0xf2, 0x0b, 0x9b, 0x53, 0x12, 0x97, 0x41, 0xd2, - 0x16, 0x52, 0x69, 0x93, 0x92, 0xd8, 0x7c, 0x65, 0xc0, 0xda, 0xa3, 0x5e, 0xc9, 0x71, 0xc4, 0x48, - 0xcc, 0x47, 0x65, 0x0f, 0x41, 0x2e, 0xc0, 0x3e, 0xd1, 0xd5, 0x26, 0x7f, 0x0b, 0xbf, 0x68, 0x40, - 0x39, 0xc5, 0x9e, 0xa8, 0xb7, 0x36, 0x0d, 0xe4, 0x53, 0x88, 0xea, 0xd9, 0x8a, 0x1a, 0x39, 0x94, - 0x40, 0x3d, 0xf2, 0xd1, 0x47, 0x50, 0xf6, 0x31, 0x0d, 0x38, 0x09, 0x70, 0xe0, 0x10, 0xbb, 0x15, - 0x63, 0x47, 0xde, 0x97, 0x84, 0x8e, 0x4a, 0xea, 0x4a, 0x0f, 0xbe, 0xaf, 0x61, 0xa5, 0xb9, 0x22, - 0xa7, 0x9e, 0xf4, 0x28, 0x76, 0x10, 0xaa, 0x3d, 0x41, 0xb5, 0xc9, 0xd5, 0x4c, 0xd9, 0xb0, 0x96, - 0xc4, 0x88, 0xa4, 0xdf, 0x38, 0xd2, 0xb8, 0xf9, 0xfb, 0x0c, 0x2c, 0xab, 0x86, 0x2e, 0xc9, 0x79, - 0x32, 0xbf, 0xcb, 0xd5, 0x68, 0x0c, 0x54, 0x63, 0xb7, 0xb0, 0x32, 0xdf, 0x6e, 0x61, 0x65, 0xdf, - 0x54, 0x58, 0x43, 0x6b, 0x25, 0xf7, 0x36, 0xb5, 0x32, 0x3d, 0xbc, 0x56, 0xcc, 0xbf, 0x18, 0xb0, - 0xa2, 0xe2, 0x93, 0x2e, 0xe5, 0x31, 0x1b, 0x8e, 0x5e, 0x9c, 0x99, 0xd1, 0x8b, 0x33, 0x3b, 0xc9, - 0x8e, 0x92, 0x1b, 0xb1, 0x24, 0x06, 0x0b, 0x77, 0x7a, 0x58, 0xe1, 0x32, 0x58, 0x6e, 0xc6, 0xd8, - 0xa5, 0x41, 0xdb, 0x22, 0xcf, 0x71, 0xec, 0xb2, 0x6e, 0xaf, 0x7e, 0x83, 0x2b, 0xc0, 0x8e, 0x15, - 0xa2, 0x5f, 0x98, 0xb7, 0xc6, 0x76, 0x0d, 0xfa, 0x09, 0xa9, 0x8f, 0xd3, 0x5a, 0xe4, 0x7d, 0x26, - 0xcc, 0xdf, 0x19, 0xb0, 0x34, 0x6c, 0x20, 0x5a, 0x82, 0xe9, 0xf0, 0x79, 0x40, 0x92, 0x57, 0x42, - 0xf5, 0x81, 0xce, 0x60, 0xc1, 0x25, 0x41, 0xe8, 0x27, 0x17, 0xbf, 0xcc, 0x35, 0xbf, 0xb2, 0xcf, - 0x4b, 0x76, 0x75, 0x87, 0xac, 0x5a, 0x5f, 0xbd, 0xda, 0x30, 0xbe, 0x7e, 0xb5, 0x61, 0xfc, 0xe7, - 0xd5, 0x86, 0xf1, 0x9b, 0xd7, 0x1b, 0x53, 0x5f, 0xbf, 0xde, 0x98, 0xfa, 0xd7, 0xeb, 0x8d, 0xa9, - 0xa7, 0x1f, 0x4d, 0x6e, 0xa8, 0xff, 0xff, 0x2e, 0x27, 0x79, 0x09, 0x7c, 0xef, 0x7f, 0x01, 0x00, - 0x00, 0xff, 0xff, 0xf5, 0xd4, 0x4f, 0x0e, 0x9d, 0x19, 0x00, 0x00, + 0x64, 0xee, 0x88, 0xf2, 0xee, 0x3a, 0xc1, 0x4e, 0x5a, 0x33, 0x4d, 0xaa, 0xa1, 0x79, 0x79, 0xba, + 0x29, 0x5b, 0x06, 0x72, 0x4e, 0x0e, 0x01, 0x12, 0x20, 0xc8, 0x21, 0x87, 0x1c, 0x72, 0xc8, 0x25, + 0x40, 0x0e, 0x01, 0x72, 0xcd, 0x29, 0x97, 0xbd, 0x65, 0x91, 0x4b, 0x82, 0x1c, 0x16, 0x81, 0x7d, + 0xc8, 0xbf, 0x11, 0xf4, 0x63, 0x86, 0xa4, 0xf8, 0x30, 0x6d, 0x69, 0x4f, 0x9c, 0xae, 0xea, 0xfa, + 0x55, 0x75, 0x55, 0x75, 0x57, 0x75, 0x13, 0xee, 0xba, 0x17, 0xee, 0x8b, 0x28, 0x0e, 0x79, 0xe8, + 0x84, 0xde, 0x06, 0x0d, 0x5c, 0xf2, 0x82, 0xc4, 0x1b, 0xe4, 0x9c, 0x04, 0x9c, 0xe9, 0x9f, 0xaa, + 0x64, 0xa3, 0xb5, 0xde, 0x99, 0x55, 0x3d, 0xb3, 0xaa, 0xa6, 0xac, 0xae, 0x38, 0x21, 0xf3, 0x43, + 0x66, 0x4b, 0xfe, 0x86, 0x1a, 0x28, 0xb9, 0xd5, 0xc5, 0x76, 0xd8, 0x0e, 0x15, 0x5d, 0x7c, 0x69, + 0xea, 0xfd, 0xa1, 0x7a, 0xd9, 0x29, 0x8e, 0x89, 0xbb, 0x11, 0x13, 0x3f, 0x3c, 0xc7, 0x9e, 0x1d, + 0x13, 0xcc, 0xc2, 0x40, 0x4b, 0xbc, 0x37, 0x54, 0x22, 0x25, 0x9c, 0x6f, 0x6e, 0x38, 0x5e, 0x78, + 0x32, 0x16, 0xbe, 0x77, 0x72, 0x44, 0xe2, 0x88, 0xf0, 0x0e, 0xf6, 0xb4, 0xc4, 0xe6, 0x1b, 0x25, + 0x58, 0xe7, 0x04, 0x3b, 0x4e, 0xd8, 0x09, 0xb8, 0x12, 0x31, 0xff, 0x9e, 0x81, 0x1b, 0x7b, 0x9d, + 0xc0, 0xa5, 0x41, 0xfb, 0x38, 0x72, 0x31, 0x27, 0x4f, 0x36, 0xd1, 0xb7, 0x60, 0x3e, 0x45, 0xb6, + 0xa9, 0x5b, 0xc9, 0xdc, 0xc9, 0xdc, 0x2d, 0x5a, 0x85, 0x94, 0x56, 0x77, 0xd1, 0x3d, 0x28, 0xb7, + 0x94, 0x94, 0x7d, 0x8e, 0xbd, 0x0e, 0xb1, 0xa3, 0xc8, 0xaf, 0x64, 0xef, 0x64, 0xee, 0x4e, 0x5b, + 0x37, 0x34, 0xe3, 0x89, 0xa0, 0x37, 0x22, 0x1f, 0xf9, 0x50, 0x4c, 0xe6, 0x4a, 0x93, 0x2a, 0xb9, + 0x3b, 0x99, 0xbb, 0xf3, 0xb5, 0xfd, 0x2f, 0xbf, 0xbe, 0x3d, 0xf5, 0xef, 0xaf, 0x6f, 0xff, 0xb0, + 0x4d, 0xf9, 0x69, 0xe7, 0xa4, 0xea, 0x84, 0xfe, 0x46, 0x9f, 0xfd, 0xe7, 0x1f, 0x7e, 0xe0, 0x9c, + 0x62, 0x1a, 0x74, 0x17, 0xe0, 0xf2, 0x8b, 0x88, 0xb0, 0xea, 0x11, 0x89, 0x29, 0xf6, 0xe8, 0x4b, + 0x7c, 0xe2, 0x91, 0x7a, 0xc0, 0xad, 0x79, 0x0d, 0x5f, 0x17, 0xe8, 0xe6, 0xaf, 0xb3, 0xb0, 0xa0, + 0x57, 0xb4, 0x2b, 0x02, 0xfb, 0x64, 0x13, 0x3d, 0x82, 0x99, 0x8e, 0x5c, 0x1c, 0xab, 0x64, 0xee, + 0xe4, 0xee, 0x16, 0x1e, 0xbc, 0x5f, 0x1d, 0x93, 0x08, 0xd5, 0x4b, 0xfe, 0xa8, 0x19, 0xc2, 0x52, + 0x2b, 0x81, 0x40, 0x3b, 0x60, 0x08, 0x3b, 0xe4, 0x72, 0x17, 0x1e, 0xdc, 0x9f, 0x04, 0x4a, 0x1b, + 0x52, 0x6d, 0x5e, 0x44, 0xc4, 0x92, 0xd2, 0xa6, 0x0f, 0x86, 0x18, 0xa1, 0x45, 0x28, 0x35, 0x3f, + 0x6f, 0xec, 0xda, 0xc7, 0x87, 0x47, 0x8d, 0xdd, 0xed, 0xfa, 0x5e, 0x7d, 0x77, 0xa7, 0x34, 0x85, + 0x6e, 0xc1, 0x4d, 0x49, 0x6d, 0x58, 0xbb, 0x07, 0xf5, 0xe3, 0x03, 0xfb, 0x68, 0xeb, 0xa0, 0xf1, + 0x68, 0xb7, 0x94, 0x41, 0xb7, 0x61, 0x4d, 0x32, 0xf6, 0x8e, 0x0f, 0x77, 0xea, 0x87, 0x1f, 0xdb, + 0xd6, 0x56, 0x73, 0xd7, 0xde, 0x3a, 0xdc, 0xb1, 0xeb, 0x87, 0x3b, 0xbb, 0x9f, 0x95, 0xb2, 0x68, + 0x09, 0xca, 0x7d, 0x92, 0x4f, 0x1e, 0x37, 0x77, 0x4b, 0x39, 0xf3, 0x6f, 0x59, 0x28, 0x1e, 0xe0, + 0xf8, 0x8c, 0xf0, 0xc4, 0x29, 0x6b, 0x30, 0xe7, 0x4b, 0x42, 0x37, 0xc4, 0xb3, 0x8a, 0x50, 0x77, + 0xd1, 0x53, 0x98, 0x8f, 0x62, 0xea, 0x10, 0x5b, 0x2d, 0x5a, 0xae, 0xb5, 0xf0, 0xe0, 0xbb, 0x63, + 0xd7, 0xaa, 0xe0, 0x1b, 0x42, 0x4c, 0xb9, 0x4e, 0x6b, 0xda, 0x9f, 0xb2, 0x0a, 0x51, 0x97, 0x8a, + 0x3e, 0x85, 0xa2, 0x56, 0xec, 0xc4, 0x44, 0x80, 0xe7, 0x24, 0xf8, 0xfd, 0x09, 0xc0, 0xb7, 0xa5, + 0x40, 0x17, 0x77, 0xde, 0xef, 0x21, 0xf7, 0x00, 0xfb, 0xa1, 0x4b, 0x5b, 0x17, 0x15, 0x63, 0x62, + 0xe0, 0x03, 0x29, 0x30, 0x00, 0xac, 0xc8, 0xb5, 0x19, 0x98, 0x96, 0xb3, 0xcd, 0x87, 0x50, 0x19, + 0xb5, 0x4a, 0x54, 0x85, 0x9b, 0xca, 0x65, 0xcf, 0x29, 0x3f, 0xb5, 0xc9, 0x8b, 0x28, 0x0c, 0x48, + 0xc0, 0xa5, 0x67, 0x0d, 0xab, 0x2c, 0x59, 0x9f, 0x52, 0x7e, 0xba, 0xab, 0x19, 0xe6, 0x67, 0x50, + 0x56, 0x58, 0x35, 0xcc, 0x52, 0x10, 0x04, 0x46, 0x84, 0x69, 0x2c, 0xa5, 0xe6, 0x2c, 0xf9, 0x8d, + 0x36, 0x60, 0xd1, 0xa7, 0x81, 0xad, 0xc0, 0x9d, 0x53, 0x1c, 0xb4, 0xbb, 0xdb, 0xad, 0x68, 0x95, + 0x7d, 0x1a, 0x48, 0x6b, 0xb6, 0x25, 0xa7, 0x11, 0xf9, 0x66, 0x07, 0x6e, 0x0e, 0x71, 0x17, 0xaa, + 0x81, 0x71, 0x82, 0x19, 0x91, 0xd8, 0x85, 0x07, 0xd5, 0x09, 0xbc, 0xd2, 0x63, 0x99, 0x25, 0x65, + 0xd1, 0x2a, 0xcc, 0xa6, 0x2b, 0x13, 0xfa, 0xcb, 0x56, 0x3a, 0x36, 0x3f, 0x4f, 0xd4, 0xf6, 0x39, + 0xf3, 0x3a, 0xd4, 0x9a, 0x7f, 0xca, 0x40, 0xf1, 0x28, 0xec, 0xc4, 0x0e, 0x79, 0xdc, 0x12, 0x5b, + 0x8a, 0xa1, 0x1f, 0x43, 0xb1, 0x7b, 0x96, 0x25, 0x19, 0x3c, 0x32, 0x43, 0x53, 0xc2, 0xf9, 0x66, + 0xb5, 0xae, 0x68, 0x47, 0xa9, 0x74, 0xdd, 0x15, 0x01, 0x67, 0x3d, 0x63, 0xf4, 0x21, 0xcc, 0x60, + 0xd7, 0x8d, 0x09, 0x63, 0x72, 0x95, 0x73, 0xb5, 0xca, 0x3f, 0xfe, 0xf2, 0xc1, 0xa2, 0x2e, 0x09, + 0x5b, 0x8a, 0x73, 0xc4, 0x63, 0x1a, 0xb4, 0xf7, 0xa7, 0xac, 0x64, 0x6a, 0x6d, 0x16, 0xf2, 0x4c, + 0x1a, 0x69, 0xfe, 0x31, 0x07, 0x37, 0x9a, 0x31, 0x0e, 0x58, 0x8b, 0xc4, 0x89, 0x1f, 0xda, 0xb0, + 0xc8, 0x48, 0xe0, 0x92, 0xd8, 0xbe, 0x3e, 0xc3, 0x2d, 0xa4, 0x20, 0x7b, 0x69, 0xc8, 0x87, 0x5b, + 0x31, 0x71, 0x68, 0x44, 0x49, 0xc0, 0x2f, 0xe9, 0xca, 0x5e, 0x45, 0xd7, 0x52, 0x8a, 0xda, 0xa7, + 0x6e, 0x05, 0x66, 0x31, 0x63, 0xea, 0x18, 0xc9, 0xc9, 0x94, 0x9c, 0x91, 0xe3, 0xba, 0x8b, 0x96, + 0x21, 0x8f, 0x7d, 0x31, 0x4d, 0xee, 0x44, 0xc3, 0xd2, 0x23, 0x54, 0x83, 0xbc, 0xb2, 0xbb, 0x32, + 0x2d, 0x0d, 0xba, 0x37, 0x36, 0x29, 0xfa, 0x02, 0x6f, 0x69, 0x49, 0xb4, 0x0f, 0x73, 0xa9, 0x3d, + 0x95, 0xfc, 0x5b, 0xc3, 0x74, 0x85, 0xcd, 0x7f, 0xe6, 0xa0, 0xf4, 0x38, 0x76, 0x49, 0xbc, 0x47, + 0x3d, 0x2f, 0x89, 0xd6, 0x31, 0x14, 0x7c, 0x7c, 0x46, 0x62, 0x3b, 0x14, 0x9c, 0xf1, 0xc9, 0x3b, + 0xc4, 0x71, 0x12, 0x4f, 0x17, 0x0e, 0x90, 0x40, 0x92, 0x82, 0xf6, 0x60, 0x5a, 0x01, 0x66, 0xdf, + 0x05, 0x70, 0x7f, 0xca, 0x52, 0xe2, 0xe8, 0x0b, 0x28, 0x7b, 0xf4, 0x59, 0x87, 0xba, 0x98, 0xd3, + 0x30, 0xd0, 0x46, 0xaa, 0xe3, 0x6e, 0x63, 0xac, 0x17, 0x1e, 0x75, 0xa5, 0x24, 0xa4, 0x3c, 0xed, + 0x4a, 0xde, 0x25, 0x2a, 0xba, 0x0d, 0x85, 0x16, 0xf5, 0x3c, 0x5b, 0x87, 0x2f, 0x27, 0xc3, 0x07, + 0x82, 0xb4, 0xa5, 0x42, 0x28, 0xab, 0x87, 0xf0, 0x4f, 0x8b, 0x10, 0x19, 0x45, 0x24, 0xaa, 0xc7, + 0x19, 0x89, 0xf7, 0x08, 0x11, 0x4c, 0x9e, 0x32, 0xf3, 0x8a, 0xc9, 0x13, 0xe6, 0xfb, 0x80, 0x78, + 0xc8, 0xb1, 0x67, 0x0b, 0x34, 0xe2, 0xda, 0x52, 0xaa, 0x32, 0x23, 0x35, 0x94, 0x24, 0x67, 0x4f, + 0x32, 0x0e, 0x04, 0x7d, 0x60, 0xb6, 0x84, 0xa9, 0xcc, 0x0e, 0xcc, 0x6e, 0x0a, 0x7a, 0xad, 0x08, + 0x05, 0xde, 0x8d, 0x9a, 0xf9, 0x8b, 0x1c, 0xdc, 0xdc, 0x21, 0x1e, 0x39, 0x27, 0x31, 0x6e, 0xf7, + 0xf4, 0x03, 0x3f, 0x02, 0x48, 0x56, 0x4c, 0xae, 0xb6, 0x01, 0x93, 0x10, 0x77, 0xe1, 0x04, 0x78, + 0xd8, 0x6a, 0x31, 0xc2, 0x39, 0x0d, 0xda, 0x57, 0xda, 0x71, 0x09, 0x78, 0x17, 0x6e, 0xa0, 0x35, + 0xcb, 0x0d, 0xb6, 0x66, 0x97, 0x42, 0x67, 0x0c, 0x84, 0xee, 0x3e, 0x2c, 0x2a, 0x97, 0x3e, 0xeb, + 0x84, 0x9c, 0xd8, 0xcf, 0x3a, 0x38, 0xe0, 0x1d, 0x9f, 0xc9, 0x28, 0x1a, 0x96, 0x72, 0xf7, 0x27, + 0x82, 0xf5, 0x89, 0xe6, 0xa0, 0x25, 0xc8, 0x53, 0x66, 0x9f, 0x74, 0x2e, 0x64, 0x30, 0x67, 0xad, + 0x69, 0xca, 0x6a, 0x9d, 0x0b, 0x51, 0xf1, 0x28, 0xb3, 0x5b, 0x34, 0xc0, 0x9e, 0x2d, 0x0c, 0xf4, + 0x88, 0x2f, 0x36, 0xe3, 0x8c, 0x9c, 0x53, 0xa6, 0x6c, 0x4f, 0x70, 0x8e, 0x52, 0x86, 0xf9, 0xf3, + 0x2c, 0xa0, 0xc1, 0xfc, 0xfb, 0x66, 0xa3, 0x71, 0x07, 0xe6, 0x45, 0x4b, 0x6d, 0x8b, 0x4a, 0x9a, + 0x9c, 0x80, 0x45, 0x0b, 0x04, 0xad, 0x81, 0x69, 0x5c, 0x77, 0x27, 0x71, 0xe9, 0xff, 0x03, 0x28, + 0x8f, 0x31, 0xfa, 0x92, 0x68, 0x8f, 0xce, 0x49, 0xca, 0x11, 0x7d, 0x49, 0x7a, 0xdc, 0x33, 0xdd, + 0xeb, 0x9e, 0x55, 0x98, 0x65, 0x9d, 0x13, 0x4e, 0x9d, 0x33, 0x26, 0xfd, 0x66, 0x58, 0xe9, 0xd8, + 0xfc, 0x6f, 0x16, 0x6e, 0x75, 0x2d, 0xef, 0x6f, 0x24, 0x9e, 0x5e, 0x67, 0x69, 0xbb, 0x54, 0xd8, + 0x5e, 0xc2, 0x9a, 0xea, 0xe8, 0x5c, 0xbb, 0xbb, 0xe8, 0x28, 0x64, 0x54, 0x04, 0x84, 0x55, 0x72, + 0xb2, 0x3b, 0xfe, 0xfe, 0xc4, 0x9a, 0x1a, 0x09, 0x46, 0x43, 0x43, 0x58, 0x2b, 0x1a, 0x7e, 0x80, + 0xc3, 0x50, 0x00, 0xb7, 0x12, 0xdd, 0xaa, 0x60, 0x74, 0xf5, 0x1a, 0x52, 0xef, 0xf7, 0x26, 0xd6, + 0xbb, 0x25, 0xe4, 0x53, 0x9d, 0x4b, 0x1a, 0xb6, 0x8f, 0xca, 0x1e, 0x1a, 0xb3, 0xd9, 0x52, 0xce, + 0xfc, 0x3d, 0xc0, 0xe2, 0x11, 0xc7, 0x9c, 0xb4, 0x3a, 0x9e, 0xcc, 0xb8, 0xc4, 0xcd, 0x3e, 0x14, + 0xe4, 0x29, 0x61, 0x47, 0x1e, 0x76, 0x92, 0xf6, 0xe4, 0xe1, 0xf8, 0x12, 0x32, 0x04, 0xa7, 0x9f, + 0xd8, 0x10, 0x58, 0x7e, 0xd2, 0x45, 0x42, 0x98, 0xd2, 0x50, 0x08, 0x45, 0xa5, 0x4e, 0x5f, 0x0c, + 0xf5, 0x69, 0xbd, 0x7f, 0x45, 0x85, 0x96, 0x42, 0x53, 0x4d, 0x6b, 0xd8, 0x43, 0x41, 0xbf, 0xcc, + 0xc0, 0x9a, 0x13, 0x06, 0xae, 0xf4, 0x06, 0xf6, 0xec, 0x9e, 0xc5, 0xca, 0x6d, 0xaa, 0x4a, 0xef, + 0xc1, 0xdb, 0xeb, 0xdf, 0xee, 0x82, 0x0e, 0x59, 0xf3, 0x8a, 0x33, 0x8a, 0x3d, 0xc2, 0x22, 0x1e, + 0xd3, 0x76, 0x9b, 0xc4, 0xc4, 0xd5, 0x55, 0xfc, 0x1a, 0x2c, 0x6a, 0x26, 0x90, 0xc3, 0x2d, 0x4a, + 0xd9, 0xe8, 0x67, 0x19, 0x58, 0xf1, 0xc2, 0xa0, 0x6d, 0x73, 0x12, 0xfb, 0x03, 0x1e, 0x9a, 0x79, + 0xd7, 0x94, 0x78, 0x14, 0x06, 0xed, 0x26, 0x89, 0xfd, 0x21, 0xee, 0x59, 0xf6, 0x86, 0xf2, 0x56, + 0x7f, 0x02, 0x95, 0x51, 0x89, 0x84, 0x76, 0x92, 0xa6, 0xe1, 0x9d, 0xba, 0x10, 0xdd, 0x32, 0xac, + 0xfe, 0x35, 0x03, 0xcb, 0xc3, 0x53, 0x07, 0x3d, 0x85, 0x92, 0xcc, 0x4a, 0xe2, 0x6a, 0x1f, 0xa4, + 0x87, 0xce, 0xfd, 0xb7, 0xd3, 0x55, 0x77, 0xad, 0x05, 0x8d, 0xa4, 0xc7, 0xe8, 0x63, 0xc8, 0xab, + 0x27, 0x10, 0x7d, 0x5f, 0x1e, 0xd1, 0x9e, 0xa8, 0x57, 0x93, 0x6a, 0xaf, 0x61, 0x96, 0x14, 0xb3, + 0xb4, 0xf8, 0xaa, 0x03, 0x6b, 0x63, 0x32, 0xef, 0x9a, 0x9c, 0xf4, 0xd3, 0x41, 0x25, 0x3d, 0xc9, + 0x84, 0xbe, 0x00, 0x94, 0xa6, 0xeb, 0xd5, 0x5d, 0x55, 0x4a, 0xb1, 0x34, 0x45, 0x64, 0xc1, 0xa8, + 0xdc, 0xb9, 0x9e, 0x05, 0xa6, 0x57, 0x59, 0x75, 0x3a, 0x3e, 0x34, 0x66, 0x73, 0x25, 0xc3, 0xfc, + 0x43, 0x06, 0x90, 0x3c, 0x3c, 0xfb, 0x2f, 0x8c, 0x0b, 0x90, 0x4d, 0x9f, 0x06, 0xb2, 0x54, 0xb6, + 0xf3, 0xec, 0xc2, 0x3f, 0x09, 0x3d, 0x75, 0x29, 0xb2, 0xf4, 0x48, 0x94, 0xc7, 0x53, 0xcc, 0x6c, + 0x75, 0x65, 0x96, 0xf5, 0x73, 0xd6, 0x9a, 0x3b, 0xc5, 0x4c, 0xdd, 0xe6, 0xfa, 0x1f, 0x1a, 0x8c, + 0x4b, 0x0f, 0x0d, 0xef, 0x41, 0x19, 0xf3, 0xd0, 0xa7, 0x8e, 0x1d, 0x13, 0x16, 0x7a, 0x1d, 0xe1, + 0x78, 0x79, 0x34, 0x95, 0xad, 0x92, 0x62, 0x58, 0x29, 0xdd, 0xfc, 0x8d, 0x01, 0xff, 0x97, 0x16, + 0x96, 0x61, 0x57, 0xdc, 0xcb, 0x16, 0xbf, 0xb9, 0xfa, 0x2f, 0x43, 0x5e, 0x54, 0x64, 0x12, 0x4b, + 0xbb, 0xe7, 0x2c, 0x3d, 0x1a, 0x6f, 0xf4, 0x3e, 0xe4, 0x19, 0xc7, 0xbc, 0xa3, 0x7a, 0xa6, 0x85, + 0x49, 0x42, 0xbf, 0xad, 0x55, 0x1e, 0x49, 0x39, 0x4b, 0xcb, 0xa3, 0x1f, 0xc0, 0x9a, 0xee, 0xbf, + 0x6c, 0x27, 0x0c, 0xce, 0x49, 0xcc, 0x44, 0x3b, 0x9f, 0x5e, 0xb1, 0xf3, 0xd2, 0x11, 0x2b, 0x7a, + 0xca, 0x76, 0x3a, 0x23, 0x79, 0x44, 0x18, 0xee, 0xbe, 0x99, 0xe1, 0xee, 0x43, 0xf7, 0xa0, 0x9c, + 0x34, 0x20, 0xa2, 0xfa, 0xdb, 0xe2, 0x4b, 0xb6, 0xd2, 0x45, 0xeb, 0x46, 0xc2, 0x68, 0x90, 0xb8, + 0x49, 0x9d, 0x33, 0xd1, 0x77, 0x33, 0x4e, 0x22, 0x5b, 0x5c, 0xbf, 0xbb, 0x2d, 0xe2, 0x9c, 0xea, + 0xbb, 0x05, 0x47, 0x5c, 0xd2, 0xd3, 0x06, 0xf1, 0xdb, 0xb0, 0xa0, 0x7a, 0x2e, 0xca, 0x2f, 0x6c, + 0x4e, 0x49, 0x5c, 0x01, 0x09, 0x5b, 0x4c, 0xa9, 0x4d, 0x4a, 0x62, 0xf4, 0x44, 0x5c, 0xaa, 0xa4, + 0x53, 0xe5, 0x03, 0x5a, 0x41, 0x3a, 0x6f, 0x82, 0xbe, 0xe6, 0x52, 0xcc, 0xe5, 0x2b, 0x1a, 0xf8, + 0xe9, 0xb7, 0xf9, 0x2a, 0x03, 0xab, 0x8f, 0x7a, 0x35, 0x1d, 0x47, 0x8c, 0xc4, 0x7c, 0x54, 0x56, + 0x20, 0x30, 0x02, 0xec, 0x13, 0x9d, 0xc5, 0xf2, 0x5b, 0xac, 0x97, 0x06, 0x94, 0x53, 0xec, 0x89, + 0x3c, 0x6e, 0xd3, 0x40, 0x3e, 0xb1, 0xa8, 0x5e, 0xb0, 0xa4, 0x39, 0x07, 0x92, 0xd1, 0x88, 0x7c, + 0xf4, 0x11, 0x54, 0x7c, 0x4c, 0x03, 0x4e, 0x02, 0x1c, 0x38, 0xc4, 0x6e, 0xc5, 0xd8, 0x91, 0xf7, + 0x30, 0x21, 0xa3, 0x92, 0x65, 0xb9, 0x87, 0xbf, 0xa7, 0xd9, 0x4a, 0x72, 0x59, 0xba, 0x34, 0xe9, + 0x7d, 0xec, 0x20, 0x54, 0x67, 0x8d, 0x6a, 0xbf, 0x6b, 0xd9, 0x4a, 0xc6, 0x5a, 0x14, 0x33, 0x92, + 0x3e, 0xe6, 0x50, 0xf3, 0xcd, 0xdf, 0x65, 0x61, 0x49, 0x35, 0x8a, 0x49, 0x2e, 0x25, 0xeb, 0xbb, + 0x9c, 0xe5, 0x99, 0x81, 0x2c, 0xef, 0x26, 0x6c, 0xf6, 0x9b, 0x4d, 0xd8, 0xdc, 0x9b, 0x12, 0x76, + 0x68, 0x0e, 0x1a, 0x6f, 0x93, 0x83, 0xd3, 0xc3, 0x73, 0xd0, 0xfc, 0x73, 0x06, 0x96, 0x95, 0x7f, + 0xd2, 0x74, 0x19, 0x73, 0x90, 0xe9, 0x4d, 0x9f, 0x1d, 0xbd, 0xe9, 0x73, 0x93, 0x9c, 0x54, 0xc6, + 0x88, 0xad, 0x36, 0xb8, 0x21, 0xa6, 0x87, 0x6c, 0x08, 0x93, 0xc1, 0x52, 0x33, 0xc6, 0x2e, 0x0d, + 0xda, 0x16, 0x79, 0x8e, 0x63, 0x97, 0x75, 0xef, 0x00, 0x37, 0xb8, 0x62, 0xd8, 0xb1, 0xe2, 0xe8, + 0x97, 0xeb, 0xcd, 0xb1, 0xdd, 0x88, 0x7e, 0x9a, 0xea, 0xc3, 0xb4, 0x16, 0x78, 0x9f, 0x0a, 0xf3, + 0xb7, 0x19, 0x58, 0x1c, 0x36, 0x11, 0x2d, 0xc2, 0x74, 0xf8, 0x3c, 0x20, 0xc9, 0xeb, 0xa3, 0x1a, + 0xa0, 0x33, 0x98, 0x77, 0x49, 0x10, 0xfa, 0xc9, 0x85, 0x32, 0x7b, 0xcd, 0xaf, 0xf7, 0x05, 0x89, + 0xae, 0xee, 0xa6, 0x35, 0xeb, 0xcb, 0x57, 0xeb, 0x99, 0xaf, 0x5e, 0xad, 0x67, 0xfe, 0xf3, 0x6a, + 0x3d, 0xf3, 0xab, 0xd7, 0xeb, 0x53, 0x5f, 0xbd, 0x5e, 0x9f, 0xfa, 0xd7, 0xeb, 0xf5, 0xa9, 0xa7, + 0x1f, 0x4d, 0xae, 0xa8, 0xff, 0x1f, 0xa0, 0x93, 0xbc, 0x64, 0x7c, 0xe7, 0x7f, 0x01, 0x00, 0x00, + 0xff, 0xff, 0xd1, 0x69, 0xc7, 0x1d, 0x27, 0x1a, 0x00, 0x00, } func (m *FundingUpdateV1) Marshal() (dAtA []byte, err error) { @@ -3449,6 +3460,11 @@ func (m *PerpetualMarketCreateEventV1) MarshalToSizedBuffer(dAtA []byte) (int, e _ = i var l int _ = l + if m.MarketType != 0 { + i = encodeVarintEvents(dAtA, i, uint64(m.MarketType)) + i-- + dAtA[i] = 0x58 + } if m.LiquidityTier != 0 { i = encodeVarintEvents(dAtA, i, uint64(m.LiquidityTier)) i-- @@ -4282,6 +4298,9 @@ func (m *PerpetualMarketCreateEventV1) Size() (n int) { if m.LiquidityTier != 0 { n += 1 + sovEvents(uint64(m.LiquidityTier)) } + if m.MarketType != 0 { + n += 1 + sovEvents(uint64(m.MarketType)) + } return n } @@ -7374,6 +7393,25 @@ func (m *PerpetualMarketCreateEventV1) Unmarshal(dAtA []byte) error { break } } + case 11: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field MarketType", wireType) + } + m.MarketType = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowEvents + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.MarketType |= types.PerpetualMarketType(b&0x7F) << shift + if b < 0x80 { + break + } + } default: iNdEx = preIndex skippy, err := skipEvents(dAtA[iNdEx:]) diff --git a/protocol/indexer/events/perpetual_market_create.go b/protocol/indexer/events/perpetual_market_create.go index 752e3bc15f..a061829764 100644 --- a/protocol/indexer/events/perpetual_market_create.go +++ b/protocol/indexer/events/perpetual_market_create.go @@ -2,7 +2,8 @@ package events import ( v1 "github.com/dydxprotocol/v4-chain/protocol/indexer/protocol/v1" - "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" ) // NewPerpetualMarketCreateEvent creates a PerpetualMarketCreateEvent @@ -12,12 +13,13 @@ func NewPerpetualMarketCreateEvent( clobPairId uint32, ticker string, marketId uint32, - status types.ClobPair_Status, + status clobtypes.ClobPair_Status, quantumConversionExponent int32, atomicResolution int32, subticksPerTick uint32, stepBaseQuantums uint64, liquidityTier uint32, + marketType perptypes.PerpetualMarketType, ) *PerpetualMarketCreateEventV1 { return &PerpetualMarketCreateEventV1{ Id: id, @@ -30,5 +32,6 @@ func NewPerpetualMarketCreateEvent( SubticksPerTick: subticksPerTick, StepBaseQuantums: stepBaseQuantums, LiquidityTier: liquidityTier, + MarketType: v1.ConvertToPerpetualMarketType(marketType), } } diff --git a/protocol/indexer/events/perpetual_market_create_test.go b/protocol/indexer/events/perpetual_market_create_test.go index 75461321f2..2398aa4a9b 100644 --- a/protocol/indexer/events/perpetual_market_create_test.go +++ b/protocol/indexer/events/perpetual_market_create_test.go @@ -5,6 +5,7 @@ import ( v1types "github.com/dydxprotocol/v4-chain/protocol/indexer/protocol/v1/types" clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" "github.com/stretchr/testify/require" ) @@ -21,6 +22,7 @@ func TestNewPerpetualMarketCreateEvent_Success(t *testing.T) { 5, 5, 0, + perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS, ) expectedPerpetualMarketCreateEventProto := &PerpetualMarketCreateEventV1{ Id: 0, @@ -33,6 +35,7 @@ func TestNewPerpetualMarketCreateEvent_Success(t *testing.T) { SubticksPerTick: 5, StepBaseQuantums: 5, LiquidityTier: 0, + MarketType: v1types.PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS, } require.Equal(t, expectedPerpetualMarketCreateEventProto, perpetualMarketCreateEvent) } diff --git a/protocol/indexer/protocol/v1/types/perpetual.pb.go b/protocol/indexer/protocol/v1/types/perpetual.pb.go new file mode 100644 index 0000000000..d57c3b4229 --- /dev/null +++ b/protocol/indexer/protocol/v1/types/perpetual.pb.go @@ -0,0 +1,81 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dydxprotocol/indexer/protocol/v1/perpetual.proto + +package types + +import ( + fmt "fmt" + proto "github.com/cosmos/gogoproto/proto" + math "math" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// Market type of perpetual. +// Defined in perpetual. +type PerpetualMarketType int32 + +const ( + // Unspecified market type. + PerpetualMarketType_PERPETUAL_MARKET_TYPE_UNSPECIFIED PerpetualMarketType = 0 + // Market type for cross margin perpetual markets. + PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS PerpetualMarketType = 1 + // Market type for isolated margin perpetual markets. + PerpetualMarketType_PERPETUAL_MARKET_TYPE_ISOLATED PerpetualMarketType = 2 +) + +var PerpetualMarketType_name = map[int32]string{ + 0: "PERPETUAL_MARKET_TYPE_UNSPECIFIED", + 1: "PERPETUAL_MARKET_TYPE_CROSS", + 2: "PERPETUAL_MARKET_TYPE_ISOLATED", +} + +var PerpetualMarketType_value = map[string]int32{ + "PERPETUAL_MARKET_TYPE_UNSPECIFIED": 0, + "PERPETUAL_MARKET_TYPE_CROSS": 1, + "PERPETUAL_MARKET_TYPE_ISOLATED": 2, +} + +func (x PerpetualMarketType) String() string { + return proto.EnumName(PerpetualMarketType_name, int32(x)) +} + +func (PerpetualMarketType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_ebf0c6c8fa38d8c8, []int{0} +} + +func init() { + proto.RegisterEnum("dydxprotocol.indexer.protocol.v1.PerpetualMarketType", PerpetualMarketType_name, PerpetualMarketType_value) +} + +func init() { + proto.RegisterFile("dydxprotocol/indexer/protocol/v1/perpetual.proto", fileDescriptor_ebf0c6c8fa38d8c8) +} + +var fileDescriptor_ebf0c6c8fa38d8c8 = []byte{ + // 227 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x32, 0x48, 0xa9, 0x4c, 0xa9, + 0x28, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0xce, 0xcf, 0xd1, 0xcf, 0xcc, 0x4b, 0x49, 0xad, 0x48, 0x2d, + 0xd2, 0x87, 0x0b, 0x94, 0x19, 0xea, 0x17, 0xa4, 0x16, 0x15, 0xa4, 0x96, 0x94, 0x26, 0xe6, 0xe8, + 0x81, 0x45, 0x85, 0x14, 0x90, 0x75, 0xe8, 0x41, 0x75, 0xe8, 0xc1, 0x05, 0xca, 0x0c, 0xb5, 0x1a, + 0x19, 0xb9, 0x84, 0x03, 0x60, 0xba, 0x7c, 0x13, 0x8b, 0xb2, 0x53, 0x4b, 0x42, 0x2a, 0x0b, 0x52, + 0x85, 0x54, 0xb9, 0x14, 0x03, 0x5c, 0x83, 0x02, 0x5c, 0x43, 0x42, 0x1d, 0x7d, 0xe2, 0x7d, 0x1d, + 0x83, 0xbc, 0x5d, 0x43, 0xe2, 0x43, 0x22, 0x03, 0x5c, 0xe3, 0x43, 0xfd, 0x82, 0x03, 0x5c, 0x9d, + 0x3d, 0xdd, 0x3c, 0x5d, 0x5d, 0x04, 0x18, 0x84, 0xe4, 0xb9, 0xa4, 0xb1, 0x2b, 0x73, 0x0e, 0xf2, + 0x0f, 0x0e, 0x16, 0x60, 0x14, 0x52, 0xe2, 0x92, 0xc3, 0xae, 0xc0, 0x33, 0xd8, 0xdf, 0xc7, 0x31, + 0xc4, 0xd5, 0x45, 0x80, 0xc9, 0x29, 0xf6, 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, 0x9c, 0xd3, 0x33, 0x4b, 0x32, 0x4a, 0x93, 0xf4, 0x92, 0xf3, 0x73, 0xf5, 0x51, 0x3c, 0x5f, + 0x66, 0xa2, 0x9b, 0x9c, 0x91, 0x98, 0x99, 0xa7, 0x8f, 0x37, 0x38, 0x4a, 0x2a, 0x0b, 0x52, 0x8b, + 0x93, 0xd8, 0xc0, 0x42, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xba, 0xc4, 0x7a, 0xc4, 0x3f, + 0x01, 0x00, 0x00, +} diff --git a/protocol/indexer/protocol/v1/v1_mappers.go b/protocol/indexer/protocol/v1/v1_mappers.go index bf04b5d1cc..917ed71b89 100644 --- a/protocol/indexer/protocol/v1/v1_mappers.go +++ b/protocol/indexer/protocol/v1/v1_mappers.go @@ -6,6 +6,7 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/dtypes" v1types "github.com/dydxprotocol/v4-chain/protocol/indexer/protocol/v1/types" clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" ) @@ -188,3 +189,19 @@ func ConvertToClobPairStatus(status clobtypes.ClobPair_Status) v1types.ClobPairS ) } } + +func ConvertToPerpetualMarketType(marketType perptypes.PerpetualMarketType) v1types.PerpetualMarketType { + switch marketType { + case perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS: + return v1types.PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS + case perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_ISOLATED: + return v1types.PerpetualMarketType_PERPETUAL_MARKET_TYPE_ISOLATED + default: + panic( + fmt.Sprintf( + "ConvertToPerpetualMarketType: invalid perpetual market type: %+v", + marketType, + ), + ) + } +} diff --git a/protocol/indexer/protocol/v1/v1_mappers_test.go b/protocol/indexer/protocol/v1/v1_mappers_test.go index e94fd0f885..26e8312b61 100644 --- a/protocol/indexer/protocol/v1/v1_mappers_test.go +++ b/protocol/indexer/protocol/v1/v1_mappers_test.go @@ -9,6 +9,7 @@ import ( v1types "github.com/dydxprotocol/v4-chain/protocol/indexer/protocol/v1/types" "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" "github.com/stretchr/testify/require" ) @@ -427,3 +428,48 @@ func TestConvertToClobPairStatus(t *testing.T) { }) } } + +func TestConvertToPerpetualMarketType(t *testing.T) { + type convertToPerpetualMarketTypeTestCase struct { + status perptypes.PerpetualMarketType + expectedStatus v1types.PerpetualMarketType + expectedPanic string + } + + tests := make(map[string]convertToPerpetualMarketTypeTestCase) + // Iterate through all the values for PerpetualMarketType to create test cases. + for name, value := range perptypes.PerpetualMarketType_value { + testName := fmt.Sprintf("Converts PerpetualMarketType %s to v1.PerpetualMarketType", name) + testCase := convertToPerpetualMarketTypeTestCase{ + status: perptypes.PerpetualMarketType(value), + expectedStatus: v1types.PerpetualMarketType(perptypes.PerpetualMarketType_value[name]), + } + if value == int32(perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_UNSPECIFIED) { + testCase.expectedPanic = fmt.Sprintf( + "ConvertToPerpetualMarketType: invalid perpetual market type: %+v", + perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_UNSPECIFIED, + ) + } + tests[testName] = testCase + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + if tc.expectedPanic != "" { + require.PanicsWithValue( + t, + tc.expectedPanic, + func() { + v1.ConvertToPerpetualMarketType(tc.status) + }, + ) + } else { + require.Equal( + t, + tc.expectedStatus, + v1.ConvertToPerpetualMarketType(tc.status), + ) + } + }) + } +} diff --git a/protocol/lib/big_math.go b/protocol/lib/big_math.go index 513d154d6c..6b4075c607 100644 --- a/protocol/lib/big_math.go +++ b/protocol/lib/big_math.go @@ -265,3 +265,27 @@ func warmCache() map[uint64]*big.Int { return bigExponentValues } + +// BigRatRoundToNearestMultiple rounds `value` up/down to the nearest multiple of `base`. +// Returns 0 if `base` is 0. +func BigRatRoundToNearestMultiple( + value *big.Rat, + base uint32, + up bool, +) uint64 { + if base == 0 { + return 0 + } + + quotient := new(big.Rat).Quo( + value, + new(big.Rat).SetUint64(uint64(base)), + ) + quotientFloored := new(big.Int).Div(quotient.Num(), quotient.Denom()) + + if up && quotientFloored.Cmp(quotient.Num()) != 0 { + return (quotientFloored.Uint64() + 1) * uint64(base) + } + + return quotientFloored.Uint64() * uint64(base) +} diff --git a/protocol/lib/big_math_test.go b/protocol/lib/big_math_test.go index 4a9c6f3e06..4d83dd7d33 100644 --- a/protocol/lib/big_math_test.go +++ b/protocol/lib/big_math_test.go @@ -869,3 +869,95 @@ func TestMustConvertBigIntToInt32(t *testing.T) { }) } } + +func TestBigRatRoundToNearestMultiple(t *testing.T) { + tests := map[string]struct { + value *big.Rat + base uint32 + up bool + expectedResult uint64 + }{ + "Round 5 down to a multiple of 2": { + value: big.NewRat(5, 1), + base: 2, + up: false, + expectedResult: 4, + }, + "Round 5 up to a multiple of 2": { + value: big.NewRat(5, 1), + base: 2, + up: true, + expectedResult: 6, + }, + "Round 7 down to a multiple of 14": { + value: big.NewRat(7, 1), + base: 14, + up: false, + expectedResult: 0, + }, + "Round 7 up to a multiple of 14": { + value: big.NewRat(7, 1), + base: 14, + up: true, + expectedResult: 14, + }, + "Round 123 down to a multiple of 123": { + value: big.NewRat(123, 1), + base: 123, + up: false, + expectedResult: 123, + }, + "Round 123 up to a multiple of 123": { + value: big.NewRat(123, 1), + base: 123, + up: true, + expectedResult: 123, + }, + "Round 100/6 down to a multiple of 3": { + value: big.NewRat(100, 6), + base: 3, + up: false, + expectedResult: 15, + }, + "Round 100/6 up to a multiple of 3": { + value: big.NewRat(100, 6), + base: 3, + up: true, + expectedResult: 18, + }, + "Round 7/2 down to a multiple of 1": { + value: big.NewRat(7, 2), + base: 1, + up: false, + expectedResult: 3, + }, + "Round 7/2 up to a multiple of 1": { + value: big.NewRat(7, 2), + base: 1, + up: true, + expectedResult: 4, + }, + "Round 10 down to a multiple of 0": { + value: big.NewRat(10, 1), + base: 0, + up: false, + expectedResult: 0, + }, + "Round 10 up to a multiple of 0": { + value: big.NewRat(10, 1), + base: 0, + up: true, + expectedResult: 0, + }, + } + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + result := lib.BigRatRoundToNearestMultiple( + tc.value, + tc.base, + tc.up, + ) + require.Equal(t, tc.expectedResult, result) + }) + } +} diff --git a/protocol/lib/metrics/constants.go b/protocol/lib/metrics/constants.go index 3e2644d7a4..2c04b963d1 100644 --- a/protocol/lib/metrics/constants.go +++ b/protocol/lib/metrics/constants.go @@ -92,6 +92,7 @@ const ( // CLOB. AddPerpetualFillAmount = "add_perpetual_fill_amount" BaseQuantums = "base_quantums" + BatchCancel = "batch_cancel" BestAsk = "best_ask" BestAskClobPair = "best_ask_clob_pair" BestBid = "best_bid" @@ -254,26 +255,22 @@ const ( BlockTimeMs = "block_time_ms" // Prices. - CreateOracleMarket = "create_oracle_market" - CurrentMarketPrices = "current_market_prices" - GetValidMarketPriceUpdates = "get_valid_market_price_updates" - IndexPriceDoesNotExist = "index_price_does_not_exist" - IndexPriceIsZero = "index_price_is_zero" - IndexPriceNotAccurate = "index_price_not_accurate" - IndexPriceNotAvailForAccuracyCheck = "index_price_not_available_for_accuracy_check" - LastPriceUpdateForMarketBlock = "last_price_update_for_market_block" - MissingPriceUpdates = "missing_price_updates" - NumMarketPricesToUpdate = "num_market_prices_to_update" - PriceChangeRate = "price_change_rate" - ProposedPriceChangesPriceUpdateDecision = "proposed_price_changes_price_update_decision" - ProposedPriceCrossesOraclePrice = "proposed_price_crosses_oracle_price" - ProposedPriceDoesNotMeetMinPriceChange = "proposed_price_does_not_meet_min_price_change" - RecentSmoothedPriceDoesNotMeetMinPriceChange = "recent_smoothed_price_doesnt_meet_min_price_change" - RecentSmoothedPriceCrossesOraclePrice = "recent_smoothed_price_crosses_old_price" - StatefulPriceUpdateValidation = "stateful_price_update_validation" - UpdateMarketParam = "update_market_param" - UpdateMarketPrices = "update_market_prices" - UpdateSmoothedPrices = "update_smoothed_prices" + CreateOracleMarket = "create_oracle_market" + CurrentMarketPrices = "current_market_prices" + GetValidMarketPriceUpdates = "get_valid_market_price_updates" + IndexPriceDoesNotExist = "index_price_does_not_exist" + IndexPriceIsZero = "index_price_is_zero" + IndexPriceNotAccurate = "index_price_not_accurate" + IndexPriceNotAvailForAccuracyCheck = "index_price_not_available_for_accuracy_check" + LastPriceUpdateForMarketBlock = "last_price_update_for_market_block" + MissingPriceUpdates = "missing_price_updates" + NumMarketPricesToUpdate = "num_market_prices_to_update" + PriceChangeRate = "price_change_rate" + ProposedPriceChangesPriceUpdateDecision = "proposed_price_changes_price_update_decision" + ProposedPriceDoesNotMeetMinPriceChange = "proposed_price_does_not_meet_min_price_change" + StatefulPriceUpdateValidation = "stateful_price_update_validation" + UpdateMarketParam = "update_market_param" + UpdateMarketPrices = "update_market_prices" // Sending. Account = "account" diff --git a/protocol/lib/slinky/utils.go b/protocol/lib/slinky/utils.go index 5b5c6a5eff..1a3417a9cc 100644 --- a/protocol/lib/slinky/utils.go +++ b/protocol/lib/slinky/utils.go @@ -4,7 +4,7 @@ import ( "fmt" "strings" - "github.com/skip-mev/slinky/x/oracle/types" + "github.com/skip-mev/slinky/pkg/types" ) /* diff --git a/protocol/lib/slinky/utils_test.go b/protocol/lib/slinky/utils_test.go index 7756cacc39..a2ee405e4b 100644 --- a/protocol/lib/slinky/utils_test.go +++ b/protocol/lib/slinky/utils_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/skip-mev/slinky/x/oracle/types" + "github.com/skip-mev/slinky/pkg/types" "github.com/stretchr/testify/require" "github.com/dydxprotocol/v4-chain/protocol/lib/slinky" diff --git a/protocol/mocks/AnteDecorator.go b/protocol/mocks/AnteDecorator.go index 05815bd427..6ec6c6919e 100644 --- a/protocol/mocks/AnteDecorator.go +++ b/protocol/mocks/AnteDecorator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type AnteDecorator struct { func (_m *AnteDecorator) AnteHandle(ctx types.Context, tx types.Tx, simulate bool, next types.AnteHandler) (types.Context, error) { ret := _m.Called(ctx, tx, simulate, next) + if len(ret) == 0 { + panic("no return value specified for AnteHandle") + } + var r0 types.Context var r1 error if rf, ok := ret.Get(0).(func(types.Context, types.Tx, bool, types.AnteHandler) (types.Context, error)); ok { @@ -36,13 +40,12 @@ func (_m *AnteDecorator) AnteHandle(ctx types.Context, tx types.Tx, simulate boo return r0, r1 } -type mockConstructorTestingTNewAnteDecorator interface { +// NewAnteDecorator creates a new instance of AnteDecorator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewAnteDecorator(t interface { mock.TestingT Cleanup(func()) -} - -// NewAnteDecorator creates a new instance of AnteDecorator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewAnteDecorator(t mockConstructorTestingTNewAnteDecorator) *AnteDecorator { +}) *AnteDecorator { mock := &AnteDecorator{} mock.Mock.Test(t) diff --git a/protocol/mocks/AppOptions.go b/protocol/mocks/AppOptions.go index 535895b872..e7c57a0f26 100644 --- a/protocol/mocks/AppOptions.go +++ b/protocol/mocks/AppOptions.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -13,6 +13,10 @@ type AppOptions struct { func (_m *AppOptions) Get(_a0 string) interface{} { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 interface{} if rf, ok := ret.Get(0).(func(string) interface{}); ok { r0 = rf(_a0) @@ -25,13 +29,12 @@ func (_m *AppOptions) Get(_a0 string) interface{} { return r0 } -type mockConstructorTestingTNewAppOptions interface { +// NewAppOptions creates a new instance of AppOptions. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewAppOptions(t interface { mock.TestingT Cleanup(func()) -} - -// NewAppOptions creates a new instance of AppOptions. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewAppOptions(t mockConstructorTestingTNewAppOptions) *AppOptions { +}) *AppOptions { mock := &AppOptions{} mock.Mock.Test(t) diff --git a/protocol/mocks/BankKeeper.go b/protocol/mocks/BankKeeper.go index af0f0b8511..f08615c86b 100644 --- a/protocol/mocks/BankKeeper.go +++ b/protocol/mocks/BankKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -24,6 +24,10 @@ type BankKeeper struct { func (_m *BankKeeper) AllBalances(_a0 context.Context, _a1 *types.QueryAllBalancesRequest) (*types.QueryAllBalancesResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for AllBalances") + } + var r0 *types.QueryAllBalancesResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryAllBalancesRequest) (*types.QueryAllBalancesResponse, error)); ok { @@ -55,6 +59,10 @@ func (_m *BankKeeper) AppendSendRestriction(restriction types.SendRestrictionFn) func (_m *BankKeeper) Balance(_a0 context.Context, _a1 *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for Balance") + } + var r0 *types.QueryBalanceResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error)); ok { @@ -81,6 +89,10 @@ func (_m *BankKeeper) Balance(_a0 context.Context, _a1 *types.QueryBalanceReques func (_m *BankKeeper) BlockedAddr(addr cosmos_sdktypes.AccAddress) bool { ret := _m.Called(addr) + if len(ret) == 0 { + panic("no return value specified for BlockedAddr") + } + var r0 bool if rf, ok := ret.Get(0).(func(cosmos_sdktypes.AccAddress) bool); ok { r0 = rf(addr) @@ -95,6 +107,10 @@ func (_m *BankKeeper) BlockedAddr(addr cosmos_sdktypes.AccAddress) bool { func (_m *BankKeeper) BurnCoins(ctx context.Context, moduleName string, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, moduleName, amt) + if len(ret) == 0 { + panic("no return value specified for BurnCoins") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, moduleName, amt) @@ -114,6 +130,10 @@ func (_m *BankKeeper) ClearSendRestriction() { func (_m *BankKeeper) DelegateCoins(ctx context.Context, delegatorAddr cosmos_sdktypes.AccAddress, moduleAccAddr cosmos_sdktypes.AccAddress, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, delegatorAddr, moduleAccAddr, amt) + if len(ret) == 0 { + panic("no return value specified for DelegateCoins") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, delegatorAddr, moduleAccAddr, amt) @@ -128,6 +148,10 @@ func (_m *BankKeeper) DelegateCoins(ctx context.Context, delegatorAddr cosmos_sd func (_m *BankKeeper) DelegateCoinsFromAccountToModule(ctx context.Context, senderAddr cosmos_sdktypes.AccAddress, recipientModule string, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, senderAddr, recipientModule, amt) + if len(ret) == 0 { + panic("no return value specified for DelegateCoinsFromAccountToModule") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, string, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, senderAddr, recipientModule, amt) @@ -154,6 +178,10 @@ func (_m *BankKeeper) DeleteSendEnabled(ctx context.Context, denoms ...string) { func (_m *BankKeeper) DenomMetadata(_a0 context.Context, _a1 *types.QueryDenomMetadataRequest) (*types.QueryDenomMetadataResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for DenomMetadata") + } + var r0 *types.QueryDenomMetadataResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomMetadataRequest) (*types.QueryDenomMetadataResponse, error)); ok { @@ -180,6 +208,10 @@ func (_m *BankKeeper) DenomMetadata(_a0 context.Context, _a1 *types.QueryDenomMe func (_m *BankKeeper) DenomMetadataByQueryString(_a0 context.Context, _a1 *types.QueryDenomMetadataByQueryStringRequest) (*types.QueryDenomMetadataByQueryStringResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for DenomMetadataByQueryString") + } + var r0 *types.QueryDenomMetadataByQueryStringResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomMetadataByQueryStringRequest) (*types.QueryDenomMetadataByQueryStringResponse, error)); ok { @@ -206,6 +238,10 @@ func (_m *BankKeeper) DenomMetadataByQueryString(_a0 context.Context, _a1 *types func (_m *BankKeeper) DenomOwners(_a0 context.Context, _a1 *types.QueryDenomOwnersRequest) (*types.QueryDenomOwnersResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for DenomOwners") + } + var r0 *types.QueryDenomOwnersResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomOwnersRequest) (*types.QueryDenomOwnersResponse, error)); ok { @@ -232,6 +268,10 @@ func (_m *BankKeeper) DenomOwners(_a0 context.Context, _a1 *types.QueryDenomOwne func (_m *BankKeeper) DenomOwnersByQuery(_a0 context.Context, _a1 *types.QueryDenomOwnersByQueryRequest) (*types.QueryDenomOwnersByQueryResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for DenomOwnersByQuery") + } + var r0 *types.QueryDenomOwnersByQueryResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomOwnersByQueryRequest) (*types.QueryDenomOwnersByQueryResponse, error)); ok { @@ -258,6 +298,10 @@ func (_m *BankKeeper) DenomOwnersByQuery(_a0 context.Context, _a1 *types.QueryDe func (_m *BankKeeper) DenomsMetadata(_a0 context.Context, _a1 *types.QueryDenomsMetadataRequest) (*types.QueryDenomsMetadataResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for DenomsMetadata") + } + var r0 *types.QueryDenomsMetadataResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDenomsMetadataRequest) (*types.QueryDenomsMetadataResponse, error)); ok { @@ -284,6 +328,10 @@ func (_m *BankKeeper) DenomsMetadata(_a0 context.Context, _a1 *types.QueryDenoms func (_m *BankKeeper) ExportGenesis(_a0 context.Context) *types.GenesisState { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for ExportGenesis") + } + var r0 *types.GenesisState if rf, ok := ret.Get(0).(func(context.Context) *types.GenesisState); ok { r0 = rf(_a0) @@ -300,6 +348,10 @@ func (_m *BankKeeper) ExportGenesis(_a0 context.Context) *types.GenesisState { func (_m *BankKeeper) GetAccountsBalances(ctx context.Context) []types.Balance { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAccountsBalances") + } + var r0 []types.Balance if rf, ok := ret.Get(0).(func(context.Context) []types.Balance); ok { r0 = rf(ctx) @@ -316,6 +368,10 @@ func (_m *BankKeeper) GetAccountsBalances(ctx context.Context) []types.Balance { func (_m *BankKeeper) GetAllBalances(ctx context.Context, addr cosmos_sdktypes.AccAddress) cosmos_sdktypes.Coins { ret := _m.Called(ctx, addr) + if len(ret) == 0 { + panic("no return value specified for GetAllBalances") + } + var r0 cosmos_sdktypes.Coins if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress) cosmos_sdktypes.Coins); ok { r0 = rf(ctx, addr) @@ -332,6 +388,10 @@ func (_m *BankKeeper) GetAllBalances(ctx context.Context, addr cosmos_sdktypes.A func (_m *BankKeeper) GetAllDenomMetaData(ctx context.Context) []types.Metadata { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAllDenomMetaData") + } + var r0 []types.Metadata if rf, ok := ret.Get(0).(func(context.Context) []types.Metadata); ok { r0 = rf(ctx) @@ -348,6 +408,10 @@ func (_m *BankKeeper) GetAllDenomMetaData(ctx context.Context) []types.Metadata func (_m *BankKeeper) GetAllSendEnabledEntries(ctx context.Context) []types.SendEnabled { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAllSendEnabledEntries") + } + var r0 []types.SendEnabled if rf, ok := ret.Get(0).(func(context.Context) []types.SendEnabled); ok { r0 = rf(ctx) @@ -364,6 +428,10 @@ func (_m *BankKeeper) GetAllSendEnabledEntries(ctx context.Context) []types.Send func (_m *BankKeeper) GetAuthority() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAuthority") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -378,6 +446,10 @@ func (_m *BankKeeper) GetAuthority() string { func (_m *BankKeeper) GetBalance(ctx context.Context, addr cosmos_sdktypes.AccAddress, denom string) cosmos_sdktypes.Coin { ret := _m.Called(ctx, addr, denom) + if len(ret) == 0 { + panic("no return value specified for GetBalance") + } + var r0 cosmos_sdktypes.Coin if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, string) cosmos_sdktypes.Coin); ok { r0 = rf(ctx, addr, denom) @@ -392,6 +464,10 @@ func (_m *BankKeeper) GetBalance(ctx context.Context, addr cosmos_sdktypes.AccAd func (_m *BankKeeper) GetBlockedAddresses() map[string]bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetBlockedAddresses") + } + var r0 map[string]bool if rf, ok := ret.Get(0).(func() map[string]bool); ok { r0 = rf() @@ -408,6 +484,10 @@ func (_m *BankKeeper) GetBlockedAddresses() map[string]bool { func (_m *BankKeeper) GetDenomMetaData(ctx context.Context, denom string) (types.Metadata, bool) { ret := _m.Called(ctx, denom) + if len(ret) == 0 { + panic("no return value specified for GetDenomMetaData") + } + var r0 types.Metadata var r1 bool if rf, ok := ret.Get(0).(func(context.Context, string) (types.Metadata, bool)); ok { @@ -432,6 +512,10 @@ func (_m *BankKeeper) GetDenomMetaData(ctx context.Context, denom string) (types func (_m *BankKeeper) GetPaginatedTotalSupply(ctx context.Context, pagination *query.PageRequest) (cosmos_sdktypes.Coins, *query.PageResponse, error) { ret := _m.Called(ctx, pagination) + if len(ret) == 0 { + panic("no return value specified for GetPaginatedTotalSupply") + } + var r0 cosmos_sdktypes.Coins var r1 *query.PageResponse var r2 error @@ -467,6 +551,10 @@ func (_m *BankKeeper) GetPaginatedTotalSupply(ctx context.Context, pagination *q func (_m *BankKeeper) GetParams(ctx context.Context) types.Params { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetParams") + } + var r0 types.Params if rf, ok := ret.Get(0).(func(context.Context) types.Params); ok { r0 = rf(ctx) @@ -481,6 +569,10 @@ func (_m *BankKeeper) GetParams(ctx context.Context) types.Params { func (_m *BankKeeper) GetSendEnabledEntry(ctx context.Context, denom string) (types.SendEnabled, bool) { ret := _m.Called(ctx, denom) + if len(ret) == 0 { + panic("no return value specified for GetSendEnabledEntry") + } + var r0 types.SendEnabled var r1 bool if rf, ok := ret.Get(0).(func(context.Context, string) (types.SendEnabled, bool)); ok { @@ -505,6 +597,10 @@ func (_m *BankKeeper) GetSendEnabledEntry(ctx context.Context, denom string) (ty func (_m *BankKeeper) GetSupply(ctx context.Context, denom string) cosmos_sdktypes.Coin { ret := _m.Called(ctx, denom) + if len(ret) == 0 { + panic("no return value specified for GetSupply") + } + var r0 cosmos_sdktypes.Coin if rf, ok := ret.Get(0).(func(context.Context, string) cosmos_sdktypes.Coin); ok { r0 = rf(ctx, denom) @@ -519,6 +615,10 @@ func (_m *BankKeeper) GetSupply(ctx context.Context, denom string) cosmos_sdktyp func (_m *BankKeeper) HasBalance(ctx context.Context, addr cosmos_sdktypes.AccAddress, amt cosmos_sdktypes.Coin) bool { ret := _m.Called(ctx, addr, amt) + if len(ret) == 0 { + panic("no return value specified for HasBalance") + } + var r0 bool if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coin) bool); ok { r0 = rf(ctx, addr, amt) @@ -533,6 +633,10 @@ func (_m *BankKeeper) HasBalance(ctx context.Context, addr cosmos_sdktypes.AccAd func (_m *BankKeeper) HasDenomMetaData(ctx context.Context, denom string) bool { ret := _m.Called(ctx, denom) + if len(ret) == 0 { + panic("no return value specified for HasDenomMetaData") + } + var r0 bool if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { r0 = rf(ctx, denom) @@ -547,6 +651,10 @@ func (_m *BankKeeper) HasDenomMetaData(ctx context.Context, denom string) bool { func (_m *BankKeeper) HasSupply(ctx context.Context, denom string) bool { ret := _m.Called(ctx, denom) + if len(ret) == 0 { + panic("no return value specified for HasSupply") + } + var r0 bool if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { r0 = rf(ctx, denom) @@ -566,6 +674,10 @@ func (_m *BankKeeper) InitGenesis(_a0 context.Context, _a1 *types.GenesisState) func (_m *BankKeeper) InputOutputCoins(ctx context.Context, input types.Input, outputs []types.Output) error { ret := _m.Called(ctx, input, outputs) + if len(ret) == 0 { + panic("no return value specified for InputOutputCoins") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, types.Input, []types.Output) error); ok { r0 = rf(ctx, input, outputs) @@ -580,6 +692,10 @@ func (_m *BankKeeper) InputOutputCoins(ctx context.Context, input types.Input, o func (_m *BankKeeper) IsSendEnabledCoin(ctx context.Context, coin cosmos_sdktypes.Coin) bool { ret := _m.Called(ctx, coin) + if len(ret) == 0 { + panic("no return value specified for IsSendEnabledCoin") + } + var r0 bool if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.Coin) bool); ok { r0 = rf(ctx, coin) @@ -601,6 +717,10 @@ func (_m *BankKeeper) IsSendEnabledCoins(ctx context.Context, coins ...cosmos_sd _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for IsSendEnabledCoins") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, ...cosmos_sdktypes.Coin) error); ok { r0 = rf(ctx, coins...) @@ -615,6 +735,10 @@ func (_m *BankKeeper) IsSendEnabledCoins(ctx context.Context, coins ...cosmos_sd func (_m *BankKeeper) IsSendEnabledDenom(ctx context.Context, denom string) bool { ret := _m.Called(ctx, denom) + if len(ret) == 0 { + panic("no return value specified for IsSendEnabledDenom") + } + var r0 bool if rf, ok := ret.Get(0).(func(context.Context, string) bool); ok { r0 = rf(ctx, denom) @@ -654,6 +778,10 @@ func (_m *BankKeeper) IterateTotalSupply(ctx context.Context, cb func(cosmos_sdk func (_m *BankKeeper) LockedCoins(ctx context.Context, addr cosmos_sdktypes.AccAddress) cosmos_sdktypes.Coins { ret := _m.Called(ctx, addr) + if len(ret) == 0 { + panic("no return value specified for LockedCoins") + } + var r0 cosmos_sdktypes.Coins if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress) cosmos_sdktypes.Coins); ok { r0 = rf(ctx, addr) @@ -670,6 +798,10 @@ func (_m *BankKeeper) LockedCoins(ctx context.Context, addr cosmos_sdktypes.AccA func (_m *BankKeeper) MintCoins(ctx context.Context, moduleName string, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, moduleName, amt) + if len(ret) == 0 { + panic("no return value specified for MintCoins") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, moduleName, amt) @@ -684,6 +816,10 @@ func (_m *BankKeeper) MintCoins(ctx context.Context, moduleName string, amt cosm func (_m *BankKeeper) Params(_a0 context.Context, _a1 *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for Params") + } + var r0 *types.QueryParamsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryParamsRequest) (*types.QueryParamsResponse, error)); ok { @@ -715,6 +851,10 @@ func (_m *BankKeeper) PrependSendRestriction(restriction types.SendRestrictionFn func (_m *BankKeeper) SendCoins(ctx context.Context, fromAddr cosmos_sdktypes.AccAddress, toAddr cosmos_sdktypes.AccAddress, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, fromAddr, toAddr, amt) + if len(ret) == 0 { + panic("no return value specified for SendCoins") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, fromAddr, toAddr, amt) @@ -729,6 +869,10 @@ func (_m *BankKeeper) SendCoins(ctx context.Context, fromAddr cosmos_sdktypes.Ac func (_m *BankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAddr cosmos_sdktypes.AccAddress, recipientModule string, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, senderAddr, recipientModule, amt) + if len(ret) == 0 { + panic("no return value specified for SendCoinsFromAccountToModule") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, string, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, senderAddr, recipientModule, amt) @@ -743,6 +887,10 @@ func (_m *BankKeeper) SendCoinsFromAccountToModule(ctx context.Context, senderAd func (_m *BankKeeper) SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr cosmos_sdktypes.AccAddress, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, senderModule, recipientAddr, amt) + if len(ret) == 0 { + panic("no return value specified for SendCoinsFromModuleToAccount") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, senderModule, recipientAddr, amt) @@ -757,6 +905,10 @@ func (_m *BankKeeper) SendCoinsFromModuleToAccount(ctx context.Context, senderMo func (_m *BankKeeper) SendCoinsFromModuleToModule(ctx context.Context, senderModule string, recipientModule string, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, senderModule, recipientModule, amt) + if len(ret) == 0 { + panic("no return value specified for SendCoinsFromModuleToModule") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, senderModule, recipientModule, amt) @@ -771,6 +923,10 @@ func (_m *BankKeeper) SendCoinsFromModuleToModule(ctx context.Context, senderMod func (_m *BankKeeper) SendEnabled(_a0 context.Context, _a1 *types.QuerySendEnabledRequest) (*types.QuerySendEnabledResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for SendEnabled") + } + var r0 *types.QuerySendEnabledResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySendEnabledRequest) (*types.QuerySendEnabledResponse, error)); ok { @@ -807,6 +963,10 @@ func (_m *BankKeeper) SetDenomMetaData(ctx context.Context, denomMetaData types. func (_m *BankKeeper) SetParams(ctx context.Context, params types.Params) error { ret := _m.Called(ctx, params) + if len(ret) == 0 { + panic("no return value specified for SetParams") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, types.Params) error); ok { r0 = rf(ctx, params) @@ -826,6 +986,10 @@ func (_m *BankKeeper) SetSendEnabled(ctx context.Context, denom string, value bo func (_m *BankKeeper) SpendableBalanceByDenom(_a0 context.Context, _a1 *types.QuerySpendableBalanceByDenomRequest) (*types.QuerySpendableBalanceByDenomResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for SpendableBalanceByDenom") + } + var r0 *types.QuerySpendableBalanceByDenomResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySpendableBalanceByDenomRequest) (*types.QuerySpendableBalanceByDenomResponse, error)); ok { @@ -852,6 +1016,10 @@ func (_m *BankKeeper) SpendableBalanceByDenom(_a0 context.Context, _a1 *types.Qu func (_m *BankKeeper) SpendableBalances(_a0 context.Context, _a1 *types.QuerySpendableBalancesRequest) (*types.QuerySpendableBalancesResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for SpendableBalances") + } + var r0 *types.QuerySpendableBalancesResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySpendableBalancesRequest) (*types.QuerySpendableBalancesResponse, error)); ok { @@ -878,6 +1046,10 @@ func (_m *BankKeeper) SpendableBalances(_a0 context.Context, _a1 *types.QuerySpe func (_m *BankKeeper) SpendableCoin(ctx context.Context, addr cosmos_sdktypes.AccAddress, denom string) cosmos_sdktypes.Coin { ret := _m.Called(ctx, addr, denom) + if len(ret) == 0 { + panic("no return value specified for SpendableCoin") + } + var r0 cosmos_sdktypes.Coin if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, string) cosmos_sdktypes.Coin); ok { r0 = rf(ctx, addr, denom) @@ -892,6 +1064,10 @@ func (_m *BankKeeper) SpendableCoin(ctx context.Context, addr cosmos_sdktypes.Ac func (_m *BankKeeper) SpendableCoins(ctx context.Context, addr cosmos_sdktypes.AccAddress) cosmos_sdktypes.Coins { ret := _m.Called(ctx, addr) + if len(ret) == 0 { + panic("no return value specified for SpendableCoins") + } + var r0 cosmos_sdktypes.Coins if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress) cosmos_sdktypes.Coins); ok { r0 = rf(ctx, addr) @@ -908,6 +1084,10 @@ func (_m *BankKeeper) SpendableCoins(ctx context.Context, addr cosmos_sdktypes.A func (_m *BankKeeper) SupplyOf(_a0 context.Context, _a1 *types.QuerySupplyOfRequest) (*types.QuerySupplyOfResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for SupplyOf") + } + var r0 *types.QuerySupplyOfResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySupplyOfRequest) (*types.QuerySupplyOfResponse, error)); ok { @@ -934,6 +1114,10 @@ func (_m *BankKeeper) SupplyOf(_a0 context.Context, _a1 *types.QuerySupplyOfRequ func (_m *BankKeeper) TotalSupply(_a0 context.Context, _a1 *types.QueryTotalSupplyRequest) (*types.QueryTotalSupplyResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for TotalSupply") + } + var r0 *types.QueryTotalSupplyResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryTotalSupplyRequest) (*types.QueryTotalSupplyResponse, error)); ok { @@ -960,6 +1144,10 @@ func (_m *BankKeeper) TotalSupply(_a0 context.Context, _a1 *types.QueryTotalSupp func (_m *BankKeeper) UndelegateCoins(ctx context.Context, moduleAccAddr cosmos_sdktypes.AccAddress, delegatorAddr cosmos_sdktypes.AccAddress, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, moduleAccAddr, delegatorAddr, amt) + if len(ret) == 0 { + panic("no return value specified for UndelegateCoins") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress, cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, moduleAccAddr, delegatorAddr, amt) @@ -974,6 +1162,10 @@ func (_m *BankKeeper) UndelegateCoins(ctx context.Context, moduleAccAddr cosmos_ func (_m *BankKeeper) UndelegateCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr cosmos_sdktypes.AccAddress, amt cosmos_sdktypes.Coins) error { ret := _m.Called(ctx, senderModule, recipientAddr, amt) + if len(ret) == 0 { + panic("no return value specified for UndelegateCoinsFromModuleToAccount") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, cosmos_sdktypes.AccAddress, cosmos_sdktypes.Coins) error); ok { r0 = rf(ctx, senderModule, recipientAddr, amt) @@ -988,6 +1180,10 @@ func (_m *BankKeeper) UndelegateCoinsFromModuleToAccount(ctx context.Context, se func (_m *BankKeeper) ValidateBalance(ctx context.Context, addr cosmos_sdktypes.AccAddress) error { ret := _m.Called(ctx, addr) + if len(ret) == 0 { + panic("no return value specified for ValidateBalance") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, cosmos_sdktypes.AccAddress) error); ok { r0 = rf(ctx, addr) @@ -1002,6 +1198,10 @@ func (_m *BankKeeper) ValidateBalance(ctx context.Context, addr cosmos_sdktypes. func (_m *BankKeeper) WithMintCoinsRestriction(_a0 types.MintingRestrictionFn) keeper.BaseKeeper { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for WithMintCoinsRestriction") + } + var r0 keeper.BaseKeeper if rf, ok := ret.Get(0).(func(types.MintingRestrictionFn) keeper.BaseKeeper); ok { r0 = rf(_a0) @@ -1012,13 +1212,12 @@ func (_m *BankKeeper) WithMintCoinsRestriction(_a0 types.MintingRestrictionFn) k return r0 } -type mockConstructorTestingTNewBankKeeper interface { +// NewBankKeeper creates a new instance of BankKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewBankKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewBankKeeper creates a new instance of BankKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewBankKeeper(t mockConstructorTestingTNewBankKeeper) *BankKeeper { +}) *BankKeeper { mock := &BankKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/BridgeKeeper.go b/protocol/mocks/BridgeKeeper.go index 16eaeca555..6b96784cac 100644 --- a/protocol/mocks/BridgeKeeper.go +++ b/protocol/mocks/BridgeKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type BridgeKeeper struct { func (_m *BridgeKeeper) AcknowledgeBridges(ctx types.Context, bridges []bridgetypes.BridgeEvent) error { ret := _m.Called(ctx, bridges) + if len(ret) == 0 { + panic("no return value specified for AcknowledgeBridges") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, []bridgetypes.BridgeEvent) error); ok { r0 = rf(ctx, bridges) @@ -32,6 +36,10 @@ func (_m *BridgeKeeper) AcknowledgeBridges(ctx types.Context, bridges []bridgety func (_m *BridgeKeeper) CompleteBridge(ctx types.Context, bridges bridgetypes.BridgeEvent) error { ret := _m.Called(ctx, bridges) + if len(ret) == 0 { + panic("no return value specified for CompleteBridge") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, bridgetypes.BridgeEvent) error); ok { r0 = rf(ctx, bridges) @@ -46,6 +54,10 @@ func (_m *BridgeKeeper) CompleteBridge(ctx types.Context, bridges bridgetypes.Br func (_m *BridgeKeeper) GetAcknowledgedEventInfo(ctx types.Context) bridgetypes.BridgeEventInfo { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAcknowledgedEventInfo") + } + var r0 bridgetypes.BridgeEventInfo if rf, ok := ret.Get(0).(func(types.Context) bridgetypes.BridgeEventInfo); ok { r0 = rf(ctx) @@ -60,6 +72,10 @@ func (_m *BridgeKeeper) GetAcknowledgedEventInfo(ctx types.Context) bridgetypes. func (_m *BridgeKeeper) GetEventParams(ctx types.Context) bridgetypes.EventParams { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetEventParams") + } + var r0 bridgetypes.EventParams if rf, ok := ret.Get(0).(func(types.Context) bridgetypes.EventParams); ok { r0 = rf(ctx) @@ -74,6 +90,10 @@ func (_m *BridgeKeeper) GetEventParams(ctx types.Context) bridgetypes.EventParam func (_m *BridgeKeeper) GetProposeParams(ctx types.Context) bridgetypes.ProposeParams { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetProposeParams") + } + var r0 bridgetypes.ProposeParams if rf, ok := ret.Get(0).(func(types.Context) bridgetypes.ProposeParams); ok { r0 = rf(ctx) @@ -88,6 +108,10 @@ func (_m *BridgeKeeper) GetProposeParams(ctx types.Context) bridgetypes.ProposeP func (_m *BridgeKeeper) GetRecognizedEventInfo(ctx types.Context) bridgetypes.BridgeEventInfo { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetRecognizedEventInfo") + } + var r0 bridgetypes.BridgeEventInfo if rf, ok := ret.Get(0).(func(types.Context) bridgetypes.BridgeEventInfo); ok { r0 = rf(ctx) @@ -102,6 +126,10 @@ func (_m *BridgeKeeper) GetRecognizedEventInfo(ctx types.Context) bridgetypes.Br func (_m *BridgeKeeper) GetSafetyParams(ctx types.Context) bridgetypes.SafetyParams { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetSafetyParams") + } + var r0 bridgetypes.SafetyParams if rf, ok := ret.Get(0).(func(types.Context) bridgetypes.SafetyParams); ok { r0 = rf(ctx) @@ -116,6 +144,10 @@ func (_m *BridgeKeeper) GetSafetyParams(ctx types.Context) bridgetypes.SafetyPar func (_m *BridgeKeeper) HasAuthority(authority string) bool { ret := _m.Called(authority) + if len(ret) == 0 { + panic("no return value specified for HasAuthority") + } + var r0 bool if rf, ok := ret.Get(0).(func(string) bool); ok { r0 = rf(authority) @@ -130,6 +162,10 @@ func (_m *BridgeKeeper) HasAuthority(authority string) bool { func (_m *BridgeKeeper) UpdateEventParams(ctx types.Context, params bridgetypes.EventParams) error { ret := _m.Called(ctx, params) + if len(ret) == 0 { + panic("no return value specified for UpdateEventParams") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, bridgetypes.EventParams) error); ok { r0 = rf(ctx, params) @@ -144,6 +180,10 @@ func (_m *BridgeKeeper) UpdateEventParams(ctx types.Context, params bridgetypes. func (_m *BridgeKeeper) UpdateProposeParams(ctx types.Context, params bridgetypes.ProposeParams) error { ret := _m.Called(ctx, params) + if len(ret) == 0 { + panic("no return value specified for UpdateProposeParams") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, bridgetypes.ProposeParams) error); ok { r0 = rf(ctx, params) @@ -158,6 +198,10 @@ func (_m *BridgeKeeper) UpdateProposeParams(ctx types.Context, params bridgetype func (_m *BridgeKeeper) UpdateSafetyParams(ctx types.Context, params bridgetypes.SafetyParams) error { ret := _m.Called(ctx, params) + if len(ret) == 0 { + panic("no return value specified for UpdateSafetyParams") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, bridgetypes.SafetyParams) error); ok { r0 = rf(ctx, params) @@ -168,13 +212,12 @@ func (_m *BridgeKeeper) UpdateSafetyParams(ctx types.Context, params bridgetypes return r0 } -type mockConstructorTestingTNewBridgeKeeper interface { +// NewBridgeKeeper creates a new instance of BridgeKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewBridgeKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewBridgeKeeper creates a new instance of BridgeKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewBridgeKeeper(t mockConstructorTestingTNewBridgeKeeper) *BridgeKeeper { +}) *BridgeKeeper { mock := &BridgeKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/BridgeQueryClient.go b/protocol/mocks/BridgeQueryClient.go index ab615c533b..1f41a80fe8 100644 --- a/protocol/mocks/BridgeQueryClient.go +++ b/protocol/mocks/BridgeQueryClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -28,6 +28,10 @@ func (_m *BridgeQueryClient) AcknowledgedEventInfo(ctx context.Context, in *type _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for AcknowledgedEventInfo") + } + var r0 *types.QueryAcknowledgedEventInfoResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryAcknowledgedEventInfoRequest, ...grpc.CallOption) (*types.QueryAcknowledgedEventInfoResponse, error)); ok { @@ -61,6 +65,10 @@ func (_m *BridgeQueryClient) DelayedCompleteBridgeMessages(ctx context.Context, _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for DelayedCompleteBridgeMessages") + } + var r0 *types.QueryDelayedCompleteBridgeMessagesResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDelayedCompleteBridgeMessagesRequest, ...grpc.CallOption) (*types.QueryDelayedCompleteBridgeMessagesResponse, error)); ok { @@ -94,6 +102,10 @@ func (_m *BridgeQueryClient) EventParams(ctx context.Context, in *types.QueryEve _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for EventParams") + } + var r0 *types.QueryEventParamsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryEventParamsRequest, ...grpc.CallOption) (*types.QueryEventParamsResponse, error)); ok { @@ -127,6 +139,10 @@ func (_m *BridgeQueryClient) ProposeParams(ctx context.Context, in *types.QueryP _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for ProposeParams") + } + var r0 *types.QueryProposeParamsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryProposeParamsRequest, ...grpc.CallOption) (*types.QueryProposeParamsResponse, error)); ok { @@ -160,6 +176,10 @@ func (_m *BridgeQueryClient) RecognizedEventInfo(ctx context.Context, in *types. _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for RecognizedEventInfo") + } + var r0 *types.QueryRecognizedEventInfoResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryRecognizedEventInfoRequest, ...grpc.CallOption) (*types.QueryRecognizedEventInfoResponse, error)); ok { @@ -193,6 +213,10 @@ func (_m *BridgeQueryClient) SafetyParams(ctx context.Context, in *types.QuerySa _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for SafetyParams") + } + var r0 *types.QuerySafetyParamsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QuerySafetyParamsRequest, ...grpc.CallOption) (*types.QuerySafetyParamsResponse, error)); ok { @@ -215,13 +239,12 @@ func (_m *BridgeQueryClient) SafetyParams(ctx context.Context, in *types.QuerySa return r0, r1 } -type mockConstructorTestingTNewBridgeQueryClient interface { +// NewBridgeQueryClient creates a new instance of BridgeQueryClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewBridgeQueryClient(t interface { mock.TestingT Cleanup(func()) -} - -// NewBridgeQueryClient creates a new instance of BridgeQueryClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewBridgeQueryClient(t mockConstructorTestingTNewBridgeQueryClient) *BridgeQueryClient { +}) *BridgeQueryClient { mock := &BridgeQueryClient{} mock.Mock.Test(t) diff --git a/protocol/mocks/BridgeServiceClient.go b/protocol/mocks/BridgeServiceClient.go index 517f729d4e..392d2eb50b 100644 --- a/protocol/mocks/BridgeServiceClient.go +++ b/protocol/mocks/BridgeServiceClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -28,6 +28,10 @@ func (_m *BridgeServiceClient) AddBridgeEvents(ctx context.Context, in *api.AddB _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for AddBridgeEvents") + } + var r0 *api.AddBridgeEventsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *api.AddBridgeEventsRequest, ...grpc.CallOption) (*api.AddBridgeEventsResponse, error)); ok { @@ -50,13 +54,12 @@ func (_m *BridgeServiceClient) AddBridgeEvents(ctx context.Context, in *api.AddB return r0, r1 } -type mockConstructorTestingTNewBridgeServiceClient interface { +// NewBridgeServiceClient creates a new instance of BridgeServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewBridgeServiceClient(t interface { mock.TestingT Cleanup(func()) -} - -// NewBridgeServiceClient creates a new instance of BridgeServiceClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewBridgeServiceClient(t mockConstructorTestingTNewBridgeServiceClient) *BridgeServiceClient { +}) *BridgeServiceClient { mock := &BridgeServiceClient{} mock.Mock.Test(t) diff --git a/protocol/mocks/CacheMultiStore.go b/protocol/mocks/CacheMultiStore.go index 4197ba0380..b53a7d1d00 100644 --- a/protocol/mocks/CacheMultiStore.go +++ b/protocol/mocks/CacheMultiStore.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type CacheMultiStore struct { func (_m *CacheMultiStore) CacheMultiStore() types.CacheMultiStore { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CacheMultiStore") + } + var r0 types.CacheMultiStore if rf, ok := ret.Get(0).(func() types.CacheMultiStore); ok { r0 = rf() @@ -34,6 +38,10 @@ func (_m *CacheMultiStore) CacheMultiStore() types.CacheMultiStore { func (_m *CacheMultiStore) CacheMultiStoreWithVersion(version int64) (types.CacheMultiStore, error) { ret := _m.Called(version) + if len(ret) == 0 { + panic("no return value specified for CacheMultiStoreWithVersion") + } + var r0 types.CacheMultiStore var r1 error if rf, ok := ret.Get(0).(func(int64) (types.CacheMultiStore, error)); ok { @@ -60,6 +68,10 @@ func (_m *CacheMultiStore) CacheMultiStoreWithVersion(version int64) (types.Cach func (_m *CacheMultiStore) CacheWrap() types.CacheWrap { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CacheWrap") + } + var r0 types.CacheWrap if rf, ok := ret.Get(0).(func() types.CacheWrap); ok { r0 = rf() @@ -76,6 +88,10 @@ func (_m *CacheMultiStore) CacheWrap() types.CacheWrap { func (_m *CacheMultiStore) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { ret := _m.Called(w, tc) + if len(ret) == 0 { + panic("no return value specified for CacheWrapWithTrace") + } + var r0 types.CacheWrap if rf, ok := ret.Get(0).(func(io.Writer, types.TraceContext) types.CacheWrap); ok { r0 = rf(w, tc) @@ -92,6 +108,10 @@ func (_m *CacheMultiStore) CacheWrapWithTrace(w io.Writer, tc types.TraceContext func (_m *CacheMultiStore) GetKVStore(_a0 types.StoreKey) types.KVStore { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetKVStore") + } + var r0 types.KVStore if rf, ok := ret.Get(0).(func(types.StoreKey) types.KVStore); ok { r0 = rf(_a0) @@ -108,6 +128,10 @@ func (_m *CacheMultiStore) GetKVStore(_a0 types.StoreKey) types.KVStore { func (_m *CacheMultiStore) GetStore(_a0 types.StoreKey) types.Store { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetStore") + } + var r0 types.Store if rf, ok := ret.Get(0).(func(types.StoreKey) types.Store); ok { r0 = rf(_a0) @@ -124,6 +148,10 @@ func (_m *CacheMultiStore) GetStore(_a0 types.StoreKey) types.Store { func (_m *CacheMultiStore) GetStoreType() types.StoreType { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetStoreType") + } + var r0 types.StoreType if rf, ok := ret.Get(0).(func() types.StoreType); ok { r0 = rf() @@ -138,6 +166,10 @@ func (_m *CacheMultiStore) GetStoreType() types.StoreType { func (_m *CacheMultiStore) LatestVersion() int64 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LatestVersion") + } + var r0 int64 if rf, ok := ret.Get(0).(func() int64); ok { r0 = rf() @@ -152,6 +184,10 @@ func (_m *CacheMultiStore) LatestVersion() int64 { func (_m *CacheMultiStore) SetTracer(w io.Writer) types.MultiStore { ret := _m.Called(w) + if len(ret) == 0 { + panic("no return value specified for SetTracer") + } + var r0 types.MultiStore if rf, ok := ret.Get(0).(func(io.Writer) types.MultiStore); ok { r0 = rf(w) @@ -168,6 +204,10 @@ func (_m *CacheMultiStore) SetTracer(w io.Writer) types.MultiStore { func (_m *CacheMultiStore) SetTracingContext(_a0 types.TraceContext) types.MultiStore { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for SetTracingContext") + } + var r0 types.MultiStore if rf, ok := ret.Get(0).(func(types.TraceContext) types.MultiStore); ok { r0 = rf(_a0) @@ -184,6 +224,10 @@ func (_m *CacheMultiStore) SetTracingContext(_a0 types.TraceContext) types.Multi func (_m *CacheMultiStore) TracingEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TracingEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -199,13 +243,12 @@ func (_m *CacheMultiStore) Write() { _m.Called() } -type mockConstructorTestingTNewCacheMultiStore interface { +// NewCacheMultiStore creates a new instance of CacheMultiStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCacheMultiStore(t interface { mock.TestingT Cleanup(func()) -} - -// NewCacheMultiStore creates a new instance of CacheMultiStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewCacheMultiStore(t mockConstructorTestingTNewCacheMultiStore) *CacheMultiStore { +}) *CacheMultiStore { mock := &CacheMultiStore{} mock.Mock.Test(t) diff --git a/protocol/mocks/ClobKeeper.go b/protocol/mocks/ClobKeeper.go index 94ac934d53..1b2fd17fa4 100644 --- a/protocol/mocks/ClobKeeper.go +++ b/protocol/mocks/ClobKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -28,6 +28,10 @@ type ClobKeeper struct { func (_m *ClobKeeper) AddOrderToOrderbookCollatCheck(ctx types.Context, clobPairId clobtypes.ClobPairId, subaccountOpenOrders map[subaccountstypes.SubaccountId][]clobtypes.PendingOpenOrder) (bool, map[subaccountstypes.SubaccountId]subaccountstypes.UpdateResult) { ret := _m.Called(ctx, clobPairId, subaccountOpenOrders) + if len(ret) == 0 { + panic("no return value specified for AddOrderToOrderbookCollatCheck") + } + var r0 bool var r1 map[subaccountstypes.SubaccountId]subaccountstypes.UpdateResult if rf, ok := ret.Get(0).(func(types.Context, clobtypes.ClobPairId, map[subaccountstypes.SubaccountId][]clobtypes.PendingOpenOrder) (bool, map[subaccountstypes.SubaccountId]subaccountstypes.UpdateResult)); ok { @@ -54,6 +58,10 @@ func (_m *ClobKeeper) AddOrderToOrderbookCollatCheck(ctx types.Context, clobPair func (_m *ClobKeeper) BatchCancelShortTermOrder(ctx types.Context, msg *clobtypes.MsgBatchCancel) ([]uint32, []uint32, error) { ret := _m.Called(ctx, msg) + if len(ret) == 0 { + panic("no return value specified for BatchCancelShortTermOrder") + } + var r0 []uint32 var r1 []uint32 var r2 error @@ -89,6 +97,10 @@ func (_m *ClobKeeper) BatchCancelShortTermOrder(ctx types.Context, msg *clobtype func (_m *ClobKeeper) CancelShortTermOrder(ctx types.Context, msg *clobtypes.MsgCancelOrder) error { ret := _m.Called(ctx, msg) + if len(ret) == 0 { + panic("no return value specified for CancelShortTermOrder") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgCancelOrder) error); ok { r0 = rf(ctx, msg) @@ -103,6 +115,10 @@ func (_m *ClobKeeper) CancelShortTermOrder(ctx types.Context, msg *clobtypes.Msg func (_m *ClobKeeper) CancelStatefulOrder(ctx types.Context, msg *clobtypes.MsgCancelOrder) error { ret := _m.Called(ctx, msg) + if len(ret) == 0 { + panic("no return value specified for CancelStatefulOrder") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgCancelOrder) error); ok { r0 = rf(ctx, msg) @@ -117,6 +133,10 @@ func (_m *ClobKeeper) CancelStatefulOrder(ctx types.Context, msg *clobtypes.MsgC func (_m *ClobKeeper) ConvertFillablePriceToSubticks(ctx types.Context, fillablePrice *big.Rat, isLiquidatingLong bool, clobPair clobtypes.ClobPair) clobtypes.Subticks { ret := _m.Called(ctx, fillablePrice, isLiquidatingLong, clobPair) + if len(ret) == 0 { + panic("no return value specified for ConvertFillablePriceToSubticks") + } + var r0 clobtypes.Subticks if rf, ok := ret.Get(0).(func(types.Context, *big.Rat, bool, clobtypes.ClobPair) clobtypes.Subticks); ok { r0 = rf(ctx, fillablePrice, isLiquidatingLong, clobPair) @@ -131,6 +151,10 @@ func (_m *ClobKeeper) ConvertFillablePriceToSubticks(ctx types.Context, fillable func (_m *ClobKeeper) CreatePerpetualClobPair(ctx types.Context, clobPairId uint32, perpetualId uint32, stepSizeInBaseQuantums subaccountstypes.BaseQuantums, quantumConversionExponent int32, subticksPerTick uint32, status clobtypes.ClobPair_Status) (clobtypes.ClobPair, error) { ret := _m.Called(ctx, clobPairId, perpetualId, stepSizeInBaseQuantums, quantumConversionExponent, subticksPerTick, status) + if len(ret) == 0 { + panic("no return value specified for CreatePerpetualClobPair") + } + var r0 clobtypes.ClobPair var r1 error if rf, ok := ret.Get(0).(func(types.Context, uint32, uint32, subaccountstypes.BaseQuantums, int32, uint32, clobtypes.ClobPair_Status) (clobtypes.ClobPair, error)); ok { @@ -160,6 +184,10 @@ func (_m *ClobKeeper) DeleteLongTermOrderPlacement(ctx types.Context, orderId cl func (_m *ClobKeeper) GetAllClobPairs(ctx types.Context) []clobtypes.ClobPair { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAllClobPairs") + } + var r0 []clobtypes.ClobPair if rf, ok := ret.Get(0).(func(types.Context) []clobtypes.ClobPair); ok { r0 = rf(ctx) @@ -176,6 +204,10 @@ func (_m *ClobKeeper) GetAllClobPairs(ctx types.Context) []clobtypes.ClobPair { func (_m *ClobKeeper) GetBankruptcyPriceInQuoteQuantums(ctx types.Context, subaccountId subaccountstypes.SubaccountId, perpetualId uint32, deltaQuantums *big.Int) (*big.Int, error) { ret := _m.Called(ctx, subaccountId, perpetualId, deltaQuantums) + if len(ret) == 0 { + panic("no return value specified for GetBankruptcyPriceInQuoteQuantums") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId, uint32, *big.Int) (*big.Int, error)); ok { @@ -198,10 +230,32 @@ func (_m *ClobKeeper) GetBankruptcyPriceInQuoteQuantums(ctx types.Context, subac return r0, r1 } +// GetBlockRateLimitConfiguration provides a mock function with given fields: ctx +func (_m *ClobKeeper) GetBlockRateLimitConfiguration(ctx types.Context) clobtypes.BlockRateLimitConfiguration { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetBlockRateLimitConfiguration") + } + + var r0 clobtypes.BlockRateLimitConfiguration + if rf, ok := ret.Get(0).(func(types.Context) clobtypes.BlockRateLimitConfiguration); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(clobtypes.BlockRateLimitConfiguration) + } + + return r0 +} + // GetClobPair provides a mock function with given fields: ctx, id func (_m *ClobKeeper) GetClobPair(ctx types.Context, id clobtypes.ClobPairId) (clobtypes.ClobPair, bool) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetClobPair") + } + var r0 clobtypes.ClobPair var r1 bool if rf, ok := ret.Get(0).(func(types.Context, clobtypes.ClobPairId) (clobtypes.ClobPair, bool)); ok { @@ -226,6 +280,10 @@ func (_m *ClobKeeper) GetClobPair(ctx types.Context, id clobtypes.ClobPairId) (c func (_m *ClobKeeper) GetFillablePrice(ctx types.Context, subaccountId subaccountstypes.SubaccountId, perpetualId uint32, deltaQuantums *big.Int) (*big.Rat, error) { ret := _m.Called(ctx, subaccountId, perpetualId, deltaQuantums) + if len(ret) == 0 { + panic("no return value specified for GetFillablePrice") + } + var r0 *big.Rat var r1 error if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId, uint32, *big.Int) (*big.Rat, error)); ok { @@ -252,6 +310,10 @@ func (_m *ClobKeeper) GetFillablePrice(ctx types.Context, subaccountId subaccoun func (_m *ClobKeeper) GetIndexerEventManager() indexer_manager.IndexerEventManager { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetIndexerEventManager") + } + var r0 indexer_manager.IndexerEventManager if rf, ok := ret.Get(0).(func() indexer_manager.IndexerEventManager); ok { r0 = rf() @@ -268,6 +330,10 @@ func (_m *ClobKeeper) GetIndexerEventManager() indexer_manager.IndexerEventManag func (_m *ClobKeeper) GetInsuranceFundBalance(ctx types.Context, perpetualId uint32) *big.Int { ret := _m.Called(ctx, perpetualId) + if len(ret) == 0 { + panic("no return value specified for GetInsuranceFundBalance") + } + var r0 *big.Int if rf, ok := ret.Get(0).(func(types.Context, uint32) *big.Int); ok { r0 = rf(ctx, perpetualId) @@ -284,6 +350,10 @@ func (_m *ClobKeeper) GetInsuranceFundBalance(ctx types.Context, perpetualId uin func (_m *ClobKeeper) GetLiquidationInsuranceFundDelta(ctx types.Context, subaccountId subaccountstypes.SubaccountId, perpetualId uint32, isBuy bool, fillAmount uint64, subticks clobtypes.Subticks) (*big.Int, error) { ret := _m.Called(ctx, subaccountId, perpetualId, isBuy, fillAmount, subticks) + if len(ret) == 0 { + panic("no return value specified for GetLiquidationInsuranceFundDelta") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId, uint32, bool, uint64, clobtypes.Subticks) (*big.Int, error)); ok { @@ -310,6 +380,10 @@ func (_m *ClobKeeper) GetLiquidationInsuranceFundDelta(ctx types.Context, subacc func (_m *ClobKeeper) GetLiquidationsConfig(ctx types.Context) clobtypes.LiquidationsConfig { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetLiquidationsConfig") + } + var r0 clobtypes.LiquidationsConfig if rf, ok := ret.Get(0).(func(types.Context) clobtypes.LiquidationsConfig); ok { r0 = rf(ctx) @@ -324,6 +398,10 @@ func (_m *ClobKeeper) GetLiquidationsConfig(ctx types.Context) clobtypes.Liquida func (_m *ClobKeeper) GetLongTermOrderPlacement(ctx types.Context, orderId clobtypes.OrderId) (clobtypes.LongTermOrderPlacement, bool) { ret := _m.Called(ctx, orderId) + if len(ret) == 0 { + panic("no return value specified for GetLongTermOrderPlacement") + } + var r0 clobtypes.LongTermOrderPlacement var r1 bool if rf, ok := ret.Get(0).(func(types.Context, clobtypes.OrderId) (clobtypes.LongTermOrderPlacement, bool)); ok { @@ -348,6 +426,10 @@ func (_m *ClobKeeper) GetLongTermOrderPlacement(ctx types.Context, orderId clobt func (_m *ClobKeeper) GetMaxAndMinPositionNotionalLiquidatable(ctx types.Context, positionToLiquidate *subaccountstypes.PerpetualPosition) (*big.Int, *big.Int, error) { ret := _m.Called(ctx, positionToLiquidate) + if len(ret) == 0 { + panic("no return value specified for GetMaxAndMinPositionNotionalLiquidatable") + } + var r0 *big.Int var r1 *big.Int var r2 error @@ -383,6 +465,10 @@ func (_m *ClobKeeper) GetMaxAndMinPositionNotionalLiquidatable(ctx types.Context func (_m *ClobKeeper) GetPerpetualPositionToLiquidate(ctx types.Context, subaccountId subaccountstypes.SubaccountId) (uint32, error) { ret := _m.Called(ctx, subaccountId) + if len(ret) == 0 { + panic("no return value specified for GetPerpetualPositionToLiquidate") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId) (uint32, error)); ok { @@ -407,6 +493,10 @@ func (_m *ClobKeeper) GetPerpetualPositionToLiquidate(ctx types.Context, subacco func (_m *ClobKeeper) GetProcessProposerMatchesEvents(ctx types.Context) clobtypes.ProcessProposerMatchesEvents { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetProcessProposerMatchesEvents") + } + var r0 clobtypes.ProcessProposerMatchesEvents if rf, ok := ret.Get(0).(func(types.Context) clobtypes.ProcessProposerMatchesEvents); ok { r0 = rf(ctx) @@ -421,6 +511,10 @@ func (_m *ClobKeeper) GetProcessProposerMatchesEvents(ctx types.Context) clobtyp func (_m *ClobKeeper) GetStatePosition(ctx types.Context, subaccountId subaccountstypes.SubaccountId, clobPairId clobtypes.ClobPairId) *big.Int { ret := _m.Called(ctx, subaccountId, clobPairId) + if len(ret) == 0 { + panic("no return value specified for GetStatePosition") + } + var r0 *big.Int if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId, clobtypes.ClobPairId) *big.Int); ok { r0 = rf(ctx, subaccountId, clobPairId) @@ -437,6 +531,10 @@ func (_m *ClobKeeper) GetStatePosition(ctx types.Context, subaccountId subaccoun func (_m *ClobKeeper) GetStatefulOrdersTimeSlice(ctx types.Context, goodTilBlockTime time.Time) []clobtypes.OrderId { ret := _m.Called(ctx, goodTilBlockTime) + if len(ret) == 0 { + panic("no return value specified for GetStatefulOrdersTimeSlice") + } + var r0 []clobtypes.OrderId if rf, ok := ret.Get(0).(func(types.Context, time.Time) []clobtypes.OrderId); ok { r0 = rf(ctx, goodTilBlockTime) @@ -453,6 +551,10 @@ func (_m *ClobKeeper) GetStatefulOrdersTimeSlice(ctx types.Context, goodTilBlock func (_m *ClobKeeper) GetSubaccountLiquidationInfo(ctx types.Context, subaccountId subaccountstypes.SubaccountId) clobtypes.SubaccountLiquidationInfo { ret := _m.Called(ctx, subaccountId) + if len(ret) == 0 { + panic("no return value specified for GetSubaccountLiquidationInfo") + } + var r0 clobtypes.SubaccountLiquidationInfo if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId) clobtypes.SubaccountLiquidationInfo); ok { r0 = rf(ctx, subaccountId) @@ -467,6 +569,10 @@ func (_m *ClobKeeper) GetSubaccountLiquidationInfo(ctx types.Context, subaccount func (_m *ClobKeeper) GetSubaccountMaxInsuranceLost(ctx types.Context, subaccountId subaccountstypes.SubaccountId, perpetualId uint32) (*big.Int, error) { ret := _m.Called(ctx, subaccountId, perpetualId) + if len(ret) == 0 { + panic("no return value specified for GetSubaccountMaxInsuranceLost") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId, uint32) (*big.Int, error)); ok { @@ -493,6 +599,10 @@ func (_m *ClobKeeper) GetSubaccountMaxInsuranceLost(ctx types.Context, subaccoun func (_m *ClobKeeper) GetSubaccountMaxNotionalLiquidatable(ctx types.Context, subaccountId subaccountstypes.SubaccountId, perpetualId uint32) (*big.Int, error) { ret := _m.Called(ctx, subaccountId, perpetualId) + if len(ret) == 0 { + panic("no return value specified for GetSubaccountMaxNotionalLiquidatable") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId, uint32) (*big.Int, error)); ok { @@ -515,10 +625,50 @@ func (_m *ClobKeeper) GetSubaccountMaxNotionalLiquidatable(ctx types.Context, su return r0, r1 } +// HandleMsgCancelOrder provides a mock function with given fields: ctx, msg +func (_m *ClobKeeper) HandleMsgCancelOrder(ctx types.Context, msg *clobtypes.MsgCancelOrder) error { + ret := _m.Called(ctx, msg) + + if len(ret) == 0 { + panic("no return value specified for HandleMsgCancelOrder") + } + + var r0 error + if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgCancelOrder) error); ok { + r0 = rf(ctx, msg) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// HandleMsgPlaceOrder provides a mock function with given fields: ctx, msg +func (_m *ClobKeeper) HandleMsgPlaceOrder(ctx types.Context, msg *clobtypes.MsgPlaceOrder) error { + ret := _m.Called(ctx, msg) + + if len(ret) == 0 { + panic("no return value specified for HandleMsgPlaceOrder") + } + + var r0 error + if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgPlaceOrder) error); ok { + r0 = rf(ctx, msg) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // HasAuthority provides a mock function with given fields: authority func (_m *ClobKeeper) HasAuthority(authority string) bool { ret := _m.Called(authority) + if len(ret) == 0 { + panic("no return value specified for HasAuthority") + } + var r0 bool if rf, ok := ret.Get(0).(func(string) bool); ok { r0 = rf(authority) @@ -533,6 +683,10 @@ func (_m *ClobKeeper) HasAuthority(authority string) bool { func (_m *ClobKeeper) InitializeBlockRateLimit(ctx types.Context, config clobtypes.BlockRateLimitConfiguration) error { ret := _m.Called(ctx, config) + if len(ret) == 0 { + panic("no return value specified for InitializeBlockRateLimit") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, clobtypes.BlockRateLimitConfiguration) error); ok { r0 = rf(ctx, config) @@ -547,6 +701,10 @@ func (_m *ClobKeeper) InitializeBlockRateLimit(ctx types.Context, config clobtyp func (_m *ClobKeeper) InitializeEquityTierLimit(ctx types.Context, config clobtypes.EquityTierLimitConfiguration) error { ret := _m.Called(ctx, config) + if len(ret) == 0 { + panic("no return value specified for InitializeEquityTierLimit") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, clobtypes.EquityTierLimitConfiguration) error); ok { r0 = rf(ctx, config) @@ -566,6 +724,10 @@ func (_m *ClobKeeper) InitializeNewGrpcStreams(ctx types.Context) { func (_m *ClobKeeper) IsLiquidatable(ctx types.Context, subaccountId subaccountstypes.SubaccountId) (bool, error) { ret := _m.Called(ctx, subaccountId) + if len(ret) == 0 { + panic("no return value specified for IsLiquidatable") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId) (bool, error)); ok { @@ -590,6 +752,10 @@ func (_m *ClobKeeper) IsLiquidatable(ctx types.Context, subaccountId subaccounts func (_m *ClobKeeper) Logger(ctx types.Context) log.Logger { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Logger") + } + var r0 log.Logger if rf, ok := ret.Get(0).(func(types.Context) log.Logger); ok { r0 = rf(ctx) @@ -606,6 +772,10 @@ func (_m *ClobKeeper) Logger(ctx types.Context) log.Logger { func (_m *ClobKeeper) MaybeDeleverageSubaccount(ctx types.Context, subaccountId subaccountstypes.SubaccountId, perpetualId uint32) (*big.Int, error) { ret := _m.Called(ctx, subaccountId, perpetualId) + if len(ret) == 0 { + panic("no return value specified for MaybeDeleverageSubaccount") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId, uint32) (*big.Int, error)); ok { @@ -632,6 +802,10 @@ func (_m *ClobKeeper) MaybeDeleverageSubaccount(ctx types.Context, subaccountId func (_m *ClobKeeper) MaybeGetLiquidationOrder(ctx types.Context, subaccountId subaccountstypes.SubaccountId) (*clobtypes.LiquidationOrder, error) { ret := _m.Called(ctx, subaccountId) + if len(ret) == 0 { + panic("no return value specified for MaybeGetLiquidationOrder") + } + var r0 *clobtypes.LiquidationOrder var r1 error if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId) (*clobtypes.LiquidationOrder, error)); ok { @@ -678,6 +852,10 @@ func (_m *ClobKeeper) MustUpdateSubaccountPerpetualLiquidated(ctx types.Context, func (_m *ClobKeeper) PerformOrderCancellationStatefulValidation(ctx types.Context, msgCancelOrder *clobtypes.MsgCancelOrder, blockHeight uint32) error { ret := _m.Called(ctx, msgCancelOrder, blockHeight) + if len(ret) == 0 { + panic("no return value specified for PerformOrderCancellationStatefulValidation") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgCancelOrder, uint32) error); ok { r0 = rf(ctx, msgCancelOrder, blockHeight) @@ -692,6 +870,10 @@ func (_m *ClobKeeper) PerformOrderCancellationStatefulValidation(ctx types.Conte func (_m *ClobKeeper) PerformStatefulOrderValidation(ctx types.Context, order *clobtypes.Order, blockHeight uint32, isPreexistingStatefulOrder bool) error { ret := _m.Called(ctx, order, blockHeight, isPreexistingStatefulOrder) + if len(ret) == 0 { + panic("no return value specified for PerformStatefulOrderValidation") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.Order, uint32, bool) error); ok { r0 = rf(ctx, order, blockHeight, isPreexistingStatefulOrder) @@ -706,6 +888,10 @@ func (_m *ClobKeeper) PerformStatefulOrderValidation(ctx types.Context, order *c func (_m *ClobKeeper) PlacePerpetualLiquidation(ctx types.Context, liquidationOrder clobtypes.LiquidationOrder) (subaccountstypes.BaseQuantums, clobtypes.OrderStatus, error) { ret := _m.Called(ctx, liquidationOrder) + if len(ret) == 0 { + panic("no return value specified for PlacePerpetualLiquidation") + } + var r0 subaccountstypes.BaseQuantums var r1 clobtypes.OrderStatus var r2 error @@ -737,6 +923,10 @@ func (_m *ClobKeeper) PlacePerpetualLiquidation(ctx types.Context, liquidationOr func (_m *ClobKeeper) PlaceShortTermOrder(ctx types.Context, msg *clobtypes.MsgPlaceOrder) (subaccountstypes.BaseQuantums, clobtypes.OrderStatus, error) { ret := _m.Called(ctx, msg) + if len(ret) == 0 { + panic("no return value specified for PlaceShortTermOrder") + } + var r0 subaccountstypes.BaseQuantums var r1 clobtypes.OrderStatus var r2 error @@ -768,6 +958,10 @@ func (_m *ClobKeeper) PlaceShortTermOrder(ctx types.Context, msg *clobtypes.MsgP func (_m *ClobKeeper) PlaceStatefulOrder(ctx types.Context, msg *clobtypes.MsgPlaceOrder) error { ret := _m.Called(ctx, msg) + if len(ret) == 0 { + panic("no return value specified for PlaceStatefulOrder") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgPlaceOrder) error); ok { r0 = rf(ctx, msg) @@ -782,6 +976,10 @@ func (_m *ClobKeeper) PlaceStatefulOrder(ctx types.Context, msg *clobtypes.MsgPl func (_m *ClobKeeper) ProcessProposerOperations(ctx types.Context, operations []clobtypes.OperationRaw) error { ret := _m.Called(ctx, operations) + if len(ret) == 0 { + panic("no return value specified for ProcessProposerOperations") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, []clobtypes.OperationRaw) error); ok { r0 = rf(ctx, operations) @@ -796,6 +994,10 @@ func (_m *ClobKeeper) ProcessProposerOperations(ctx types.Context, operations [] func (_m *ClobKeeper) ProcessSingleMatch(ctx types.Context, matchWithOrders *clobtypes.MatchWithOrders) (bool, subaccountstypes.UpdateResult, subaccountstypes.UpdateResult, *clobtypes.OffchainUpdates, error) { ret := _m.Called(ctx, matchWithOrders) + if len(ret) == 0 { + panic("no return value specified for ProcessSingleMatch") + } + var r0 bool var r1 subaccountstypes.UpdateResult var r2 subaccountstypes.UpdateResult @@ -844,10 +1046,32 @@ func (_m *ClobKeeper) PruneStateFillAmountsForShortTermOrders(ctx types.Context) _m.Called(ctx) } +// RateLimitBatchCancel provides a mock function with given fields: ctx, order +func (_m *ClobKeeper) RateLimitBatchCancel(ctx types.Context, order *clobtypes.MsgBatchCancel) error { + ret := _m.Called(ctx, order) + + if len(ret) == 0 { + panic("no return value specified for RateLimitBatchCancel") + } + + var r0 error + if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgBatchCancel) error); ok { + r0 = rf(ctx, order) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // RateLimitCancelOrder provides a mock function with given fields: ctx, order func (_m *ClobKeeper) RateLimitCancelOrder(ctx types.Context, order *clobtypes.MsgCancelOrder) error { ret := _m.Called(ctx, order) + if len(ret) == 0 { + panic("no return value specified for RateLimitCancelOrder") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgCancelOrder) error); ok { r0 = rf(ctx, order) @@ -862,6 +1086,10 @@ func (_m *ClobKeeper) RateLimitCancelOrder(ctx types.Context, order *clobtypes.M func (_m *ClobKeeper) RateLimitPlaceOrder(ctx types.Context, order *clobtypes.MsgPlaceOrder) error { ret := _m.Called(ctx, order) + if len(ret) == 0 { + panic("no return value specified for RateLimitPlaceOrder") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgPlaceOrder) error); ok { r0 = rf(ctx, order) @@ -881,6 +1109,10 @@ func (_m *ClobKeeper) RemoveClobPair(ctx types.Context, id clobtypes.ClobPairId) func (_m *ClobKeeper) RemoveExpiredStatefulOrdersTimeSlices(ctx types.Context, blockTime time.Time) []clobtypes.OrderId { ret := _m.Called(ctx, blockTime) + if len(ret) == 0 { + panic("no return value specified for RemoveExpiredStatefulOrdersTimeSlices") + } + var r0 []clobtypes.OrderId if rf, ok := ret.Get(0).(func(types.Context, time.Time) []clobtypes.OrderId); ok { r0 = rf(ctx, blockTime) @@ -898,6 +1130,11 @@ func (_m *ClobKeeper) RemoveOrderFillAmount(ctx types.Context, orderId clobtypes _m.Called(ctx, orderId) } +// SendOrderbookUpdates provides a mock function with given fields: offchainUpdates, snapshot +func (_m *ClobKeeper) SendOrderbookUpdates(offchainUpdates *clobtypes.OffchainUpdates, snapshot bool) { + _m.Called(offchainUpdates, snapshot) +} + // SetLongTermOrderPlacement provides a mock function with given fields: ctx, order, blockHeight func (_m *ClobKeeper) SetLongTermOrderPlacement(ctx types.Context, order clobtypes.Order, blockHeight uint32) { _m.Called(ctx, order, blockHeight) @@ -907,6 +1144,10 @@ func (_m *ClobKeeper) SetLongTermOrderPlacement(ctx types.Context, order clobtyp func (_m *ClobKeeper) UpdateClobPair(ctx types.Context, clobPair clobtypes.ClobPair) error { ret := _m.Called(ctx, clobPair) + if len(ret) == 0 { + panic("no return value specified for UpdateClobPair") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, clobtypes.ClobPair) error); ok { r0 = rf(ctx, clobPair) @@ -921,6 +1162,10 @@ func (_m *ClobKeeper) UpdateClobPair(ctx types.Context, clobPair clobtypes.ClobP func (_m *ClobKeeper) UpdateLiquidationsConfig(ctx types.Context, config clobtypes.LiquidationsConfig) error { ret := _m.Called(ctx, config) + if len(ret) == 0 { + panic("no return value specified for UpdateLiquidationsConfig") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, clobtypes.LiquidationsConfig) error); ok { r0 = rf(ctx, config) @@ -936,13 +1181,12 @@ func (_m *ClobKeeper) UpdateSubaccountLiquidationInfo(ctx types.Context, subacco _m.Called(ctx, subaccountId, notionalLiquidatedQuoteQuantums, insuranceFundDeltaQuoteQuantums) } -type mockConstructorTestingTNewClobKeeper interface { +// NewClobKeeper creates a new instance of ClobKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewClobKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewClobKeeper creates a new instance of ClobKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewClobKeeper(t mockConstructorTestingTNewClobKeeper) *ClobKeeper { +}) *ClobKeeper { mock := &ClobKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/Configurator.go b/protocol/mocks/Configurator.go index fc800e9033..a72313f0b9 100644 --- a/protocol/mocks/Configurator.go +++ b/protocol/mocks/Configurator.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type Configurator struct { func (_m *Configurator) Error() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Error") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -34,6 +38,10 @@ func (_m *Configurator) Error() error { func (_m *Configurator) MsgServer() grpc.Server { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for MsgServer") + } + var r0 grpc.Server if rf, ok := ret.Get(0).(func() grpc.Server); ok { r0 = rf() @@ -50,6 +58,10 @@ func (_m *Configurator) MsgServer() grpc.Server { func (_m *Configurator) QueryServer() grpc.Server { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for QueryServer") + } + var r0 grpc.Server if rf, ok := ret.Get(0).(func() grpc.Server); ok { r0 = rf() @@ -66,6 +78,10 @@ func (_m *Configurator) QueryServer() grpc.Server { func (_m *Configurator) RegisterMigration(moduleName string, fromVersion uint64, handler module.MigrationHandler) error { ret := _m.Called(moduleName, fromVersion, handler) + if len(ret) == 0 { + panic("no return value specified for RegisterMigration") + } + var r0 error if rf, ok := ret.Get(0).(func(string, uint64, module.MigrationHandler) error); ok { r0 = rf(moduleName, fromVersion, handler) @@ -81,13 +97,12 @@ func (_m *Configurator) RegisterService(sd *google_golang_orggrpc.ServiceDesc, s _m.Called(sd, ss) } -type mockConstructorTestingTNewConfigurator interface { +// NewConfigurator creates a new instance of Configurator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewConfigurator(t interface { mock.TestingT Cleanup(func()) -} - -// NewConfigurator creates a new instance of Configurator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewConfigurator(t mockConstructorTestingTNewConfigurator) *Configurator { +}) *Configurator { mock := &Configurator{} mock.Mock.Test(t) diff --git a/protocol/mocks/DelayMsgKeeper.go b/protocol/mocks/DelayMsgKeeper.go index a05a4feb7e..0dfba22d72 100644 --- a/protocol/mocks/DelayMsgKeeper.go +++ b/protocol/mocks/DelayMsgKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -24,6 +24,10 @@ type DelayMsgKeeper struct { func (_m *DelayMsgKeeper) DelayMessageByBlocks(ctx types.Context, msg proto.Message, blockDelay uint32) (uint32, error) { ret := _m.Called(ctx, msg, blockDelay) + if len(ret) == 0 { + panic("no return value specified for DelayMessageByBlocks") + } + var r0 uint32 var r1 error if rf, ok := ret.Get(0).(func(types.Context, proto.Message, uint32) (uint32, error)); ok { @@ -48,6 +52,10 @@ func (_m *DelayMsgKeeper) DelayMessageByBlocks(ctx types.Context, msg proto.Mess func (_m *DelayMsgKeeper) DeleteMessage(ctx types.Context, id uint32) error { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for DeleteMessage") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, uint32) error); ok { r0 = rf(ctx, id) @@ -62,6 +70,10 @@ func (_m *DelayMsgKeeper) DeleteMessage(ctx types.Context, id uint32) error { func (_m *DelayMsgKeeper) GetAllDelayedMessages(ctx types.Context) []*delaymsgtypes.DelayedMessage { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAllDelayedMessages") + } + var r0 []*delaymsgtypes.DelayedMessage if rf, ok := ret.Get(0).(func(types.Context) []*delaymsgtypes.DelayedMessage); ok { r0 = rf(ctx) @@ -78,6 +90,10 @@ func (_m *DelayMsgKeeper) GetAllDelayedMessages(ctx types.Context) []*delaymsgty func (_m *DelayMsgKeeper) GetBlockMessageIds(ctx types.Context, blockHeight uint32) (delaymsgtypes.BlockMessageIds, bool) { ret := _m.Called(ctx, blockHeight) + if len(ret) == 0 { + panic("no return value specified for GetBlockMessageIds") + } + var r0 delaymsgtypes.BlockMessageIds var r1 bool if rf, ok := ret.Get(0).(func(types.Context, uint32) (delaymsgtypes.BlockMessageIds, bool)); ok { @@ -102,6 +118,10 @@ func (_m *DelayMsgKeeper) GetBlockMessageIds(ctx types.Context, blockHeight uint func (_m *DelayMsgKeeper) GetMessage(ctx types.Context, id uint32) (delaymsgtypes.DelayedMessage, bool) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetMessage") + } + var r0 delaymsgtypes.DelayedMessage var r1 bool if rf, ok := ret.Get(0).(func(types.Context, uint32) (delaymsgtypes.DelayedMessage, bool)); ok { @@ -126,6 +146,10 @@ func (_m *DelayMsgKeeper) GetMessage(ctx types.Context, id uint32) (delaymsgtype func (_m *DelayMsgKeeper) HasAuthority(authority string) bool { ret := _m.Called(authority) + if len(ret) == 0 { + panic("no return value specified for HasAuthority") + } + var r0 bool if rf, ok := ret.Get(0).(func(string) bool); ok { r0 = rf(authority) @@ -140,6 +164,10 @@ func (_m *DelayMsgKeeper) HasAuthority(authority string) bool { func (_m *DelayMsgKeeper) Logger(ctx types.Context) log.Logger { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Logger") + } + var r0 log.Logger if rf, ok := ret.Get(0).(func(types.Context) log.Logger); ok { r0 = rf(ctx) @@ -156,6 +184,10 @@ func (_m *DelayMsgKeeper) Logger(ctx types.Context) log.Logger { func (_m *DelayMsgKeeper) Router() lib.MsgRouter { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Router") + } + var r0 lib.MsgRouter if rf, ok := ret.Get(0).(func() lib.MsgRouter); ok { r0 = rf() @@ -172,6 +204,10 @@ func (_m *DelayMsgKeeper) Router() lib.MsgRouter { func (_m *DelayMsgKeeper) SetDelayedMessage(ctx types.Context, msg *delaymsgtypes.DelayedMessage) error { ret := _m.Called(ctx, msg) + if len(ret) == 0 { + panic("no return value specified for SetDelayedMessage") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, *delaymsgtypes.DelayedMessage) error); ok { r0 = rf(ctx, msg) @@ -187,13 +223,12 @@ func (_m *DelayMsgKeeper) SetNextDelayedMessageId(ctx types.Context, nextDelayed _m.Called(ctx, nextDelayedMessageId) } -type mockConstructorTestingTNewDelayMsgKeeper interface { +// NewDelayMsgKeeper creates a new instance of DelayMsgKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewDelayMsgKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewDelayMsgKeeper creates a new instance of DelayMsgKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewDelayMsgKeeper(t mockConstructorTestingTNewDelayMsgKeeper) *DelayMsgKeeper { +}) *DelayMsgKeeper { mock := &DelayMsgKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/EthClient.go b/protocol/mocks/EthClient.go index ba05b14e38..6ace9daaf9 100644 --- a/protocol/mocks/EthClient.go +++ b/protocol/mocks/EthClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -22,6 +22,10 @@ type EthClient struct { func (_m *EthClient) ChainID(ctx context.Context) (*big.Int, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for ChainID") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(context.Context) (*big.Int, error)); ok { @@ -48,6 +52,10 @@ func (_m *EthClient) ChainID(ctx context.Context) (*big.Int, error) { func (_m *EthClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]coretypes.Log, error) { ret := _m.Called(ctx, q) + if len(ret) == 0 { + panic("no return value specified for FilterLogs") + } + var r0 []coretypes.Log var r1 error if rf, ok := ret.Get(0).(func(context.Context, ethereum.FilterQuery) ([]coretypes.Log, error)); ok { @@ -70,13 +78,12 @@ func (_m *EthClient) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([] return r0, r1 } -type mockConstructorTestingTNewEthClient interface { +// NewEthClient creates a new instance of EthClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewEthClient(t interface { mock.TestingT Cleanup(func()) -} - -// NewEthClient creates a new instance of EthClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewEthClient(t mockConstructorTestingTNewEthClient) *EthClient { +}) *EthClient { mock := &EthClient{} mock.Mock.Test(t) diff --git a/protocol/mocks/ExchangeConfigUpdater.go b/protocol/mocks/ExchangeConfigUpdater.go index 108bd18eba..2788a638be 100644 --- a/protocol/mocks/ExchangeConfigUpdater.go +++ b/protocol/mocks/ExchangeConfigUpdater.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type ExchangeConfigUpdater struct { func (_m *ExchangeConfigUpdater) GetExchangeId() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetExchangeId") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -30,6 +34,10 @@ func (_m *ExchangeConfigUpdater) GetExchangeId() string { func (_m *ExchangeConfigUpdater) UpdateMutableExchangeConfig(newExchangeConfig *types.MutableExchangeMarketConfig, newMarketConfigs []*types.MutableMarketConfig) error { ret := _m.Called(newExchangeConfig, newMarketConfigs) + if len(ret) == 0 { + panic("no return value specified for UpdateMutableExchangeConfig") + } + var r0 error if rf, ok := ret.Get(0).(func(*types.MutableExchangeMarketConfig, []*types.MutableMarketConfig) error); ok { r0 = rf(newExchangeConfig, newMarketConfigs) @@ -40,13 +48,12 @@ func (_m *ExchangeConfigUpdater) UpdateMutableExchangeConfig(newExchangeConfig * return r0 } -type mockConstructorTestingTNewExchangeConfigUpdater interface { +// NewExchangeConfigUpdater creates a new instance of ExchangeConfigUpdater. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewExchangeConfigUpdater(t interface { mock.TestingT Cleanup(func()) -} - -// NewExchangeConfigUpdater creates a new instance of ExchangeConfigUpdater. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewExchangeConfigUpdater(t mockConstructorTestingTNewExchangeConfigUpdater) *ExchangeConfigUpdater { +}) *ExchangeConfigUpdater { mock := &ExchangeConfigUpdater{} mock.Mock.Test(t) diff --git a/protocol/mocks/ExchangeQueryHandler.go b/protocol/mocks/ExchangeQueryHandler.go index 35a021b02c..6296734185 100644 --- a/protocol/mocks/ExchangeQueryHandler.go +++ b/protocol/mocks/ExchangeQueryHandler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -23,6 +23,10 @@ type ExchangeQueryHandler struct { func (_m *ExchangeQueryHandler) Now() time.Time { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Now") + } + var r0 time.Time if rf, ok := ret.Get(0).(func() time.Time); ok { r0 = rf() @@ -37,6 +41,10 @@ func (_m *ExchangeQueryHandler) Now() time.Time { func (_m *ExchangeQueryHandler) Query(ctx context.Context, exchangeQueryDetails *types.ExchangeQueryDetails, exchangeConfig *types.MutableExchangeMarketConfig, marketIds []uint32, requestHandler daemonstypes.RequestHandler, marketPriceExponent map[uint32]int32) ([]*types.MarketPriceTimestamp, map[uint32]error, error) { ret := _m.Called(ctx, exchangeQueryDetails, exchangeConfig, marketIds, requestHandler, marketPriceExponent) + if len(ret) == 0 { + panic("no return value specified for Query") + } + var r0 []*types.MarketPriceTimestamp var r1 map[uint32]error var r2 error @@ -68,13 +76,12 @@ func (_m *ExchangeQueryHandler) Query(ctx context.Context, exchangeQueryDetails return r0, r1, r2 } -type mockConstructorTestingTNewExchangeQueryHandler interface { +// NewExchangeQueryHandler creates a new instance of ExchangeQueryHandler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewExchangeQueryHandler(t interface { mock.TestingT Cleanup(func()) -} - -// NewExchangeQueryHandler creates a new instance of ExchangeQueryHandler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewExchangeQueryHandler(t mockConstructorTestingTNewExchangeQueryHandler) *ExchangeQueryHandler { +}) *ExchangeQueryHandler { mock := &ExchangeQueryHandler{} mock.Mock.Test(t) diff --git a/protocol/mocks/ExchangeToMarketPrices.go b/protocol/mocks/ExchangeToMarketPrices.go index 16dd0bb143..485b0fac65 100644 --- a/protocol/mocks/ExchangeToMarketPrices.go +++ b/protocol/mocks/ExchangeToMarketPrices.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type ExchangeToMarketPrices struct { func (_m *ExchangeToMarketPrices) GetAllPrices() map[string][]types.MarketPriceTimestamp { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetAllPrices") + } + var r0 map[string][]types.MarketPriceTimestamp if rf, ok := ret.Get(0).(func() map[string][]types.MarketPriceTimestamp); ok { r0 = rf() @@ -36,6 +40,10 @@ func (_m *ExchangeToMarketPrices) GetAllPrices() map[string][]types.MarketPriceT func (_m *ExchangeToMarketPrices) GetIndexPrice(marketId uint32, cutoffTime time.Time, resolver pricefeedtypes.Resolver) (uint64, int) { ret := _m.Called(marketId, cutoffTime, resolver) + if len(ret) == 0 { + panic("no return value specified for GetIndexPrice") + } + var r0 uint64 var r1 int if rf, ok := ret.Get(0).(func(uint32, time.Time, pricefeedtypes.Resolver) (uint64, int)); ok { @@ -61,13 +69,12 @@ func (_m *ExchangeToMarketPrices) UpdatePrice(exchangeId string, marketPriceTime _m.Called(exchangeId, marketPriceTimestamp) } -type mockConstructorTestingTNewExchangeToMarketPrices interface { +// NewExchangeToMarketPrices creates a new instance of ExchangeToMarketPrices. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewExchangeToMarketPrices(t interface { mock.TestingT Cleanup(func()) -} - -// NewExchangeToMarketPrices creates a new instance of ExchangeToMarketPrices. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewExchangeToMarketPrices(t mockConstructorTestingTNewExchangeToMarketPrices) *ExchangeToMarketPrices { +}) *ExchangeToMarketPrices { mock := &ExchangeToMarketPrices{} mock.Mock.Test(t) diff --git a/protocol/mocks/ExtendVoteHandler.go b/protocol/mocks/ExtendVoteHandler.go new file mode 100644 index 0000000000..73b932452e --- /dev/null +++ b/protocol/mocks/ExtendVoteHandler.go @@ -0,0 +1,59 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + abcitypes "github.com/cometbft/cometbft/abci/types" + mock "github.com/stretchr/testify/mock" + + types "github.com/cosmos/cosmos-sdk/types" +) + +// ExtendVoteHandler is an autogenerated mock type for the ExtendVoteHandler type +type ExtendVoteHandler struct { + mock.Mock +} + +// Execute provides a mock function with given fields: _a0, _a1 +func (_m *ExtendVoteHandler) Execute(_a0 types.Context, _a1 *abcitypes.RequestExtendVote) (*abcitypes.ResponseExtendVote, error) { + ret := _m.Called(_a0, _a1) + + if len(ret) == 0 { + panic("no return value specified for Execute") + } + + var r0 *abcitypes.ResponseExtendVote + var r1 error + if rf, ok := ret.Get(0).(func(types.Context, *abcitypes.RequestExtendVote) (*abcitypes.ResponseExtendVote, error)); ok { + return rf(_a0, _a1) + } + if rf, ok := ret.Get(0).(func(types.Context, *abcitypes.RequestExtendVote) *abcitypes.ResponseExtendVote); ok { + r0 = rf(_a0, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*abcitypes.ResponseExtendVote) + } + } + + if rf, ok := ret.Get(1).(func(types.Context, *abcitypes.RequestExtendVote) error); ok { + r1 = rf(_a0, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewExtendVoteHandler creates a new instance of ExtendVoteHandler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewExtendVoteHandler(t interface { + mock.TestingT + Cleanup(func()) +}) *ExtendVoteHandler { + mock := &ExtendVoteHandler{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/protocol/mocks/FileHandler.go b/protocol/mocks/FileHandler.go index 2cb099f798..b94d37dce2 100644 --- a/protocol/mocks/FileHandler.go +++ b/protocol/mocks/FileHandler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -13,6 +13,10 @@ type FileHandler struct { func (_m *FileHandler) RemoveAll(path string) error { ret := _m.Called(path) + if len(ret) == 0 { + panic("no return value specified for RemoveAll") + } + var r0 error if rf, ok := ret.Get(0).(func(string) error); ok { r0 = rf(path) @@ -23,13 +27,12 @@ func (_m *FileHandler) RemoveAll(path string) error { return r0 } -type mockConstructorTestingTNewFileHandler interface { +// NewFileHandler creates a new instance of FileHandler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewFileHandler(t interface { mock.TestingT Cleanup(func()) -} - -// NewFileHandler creates a new instance of FileHandler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewFileHandler(t mockConstructorTestingTNewFileHandler) *FileHandler { +}) *FileHandler { mock := &FileHandler{} mock.Mock.Test(t) diff --git a/protocol/mocks/GrpcClient.go b/protocol/mocks/GrpcClient.go index 9651133953..d26e7cb660 100644 --- a/protocol/mocks/GrpcClient.go +++ b/protocol/mocks/GrpcClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type GrpcClient struct { func (_m *GrpcClient) CloseConnection(grpcConn *grpc.ClientConn) error { ret := _m.Called(grpcConn) + if len(ret) == 0 { + panic("no return value specified for CloseConnection") + } + var r0 error if rf, ok := ret.Get(0).(func(*grpc.ClientConn) error); ok { r0 = rf(grpcConn) @@ -33,6 +37,10 @@ func (_m *GrpcClient) CloseConnection(grpcConn *grpc.ClientConn) error { func (_m *GrpcClient) NewGrpcConnection(ctx context.Context, socketAddress string) (*grpc.ClientConn, error) { ret := _m.Called(ctx, socketAddress) + if len(ret) == 0 { + panic("no return value specified for NewGrpcConnection") + } + var r0 *grpc.ClientConn var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*grpc.ClientConn, error)); ok { @@ -59,6 +67,10 @@ func (_m *GrpcClient) NewGrpcConnection(ctx context.Context, socketAddress strin func (_m *GrpcClient) NewTcpConnection(ctx context.Context, endpoint string) (*grpc.ClientConn, error) { ret := _m.Called(ctx, endpoint) + if len(ret) == 0 { + panic("no return value specified for NewTcpConnection") + } + var r0 *grpc.ClientConn var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*grpc.ClientConn, error)); ok { @@ -81,13 +93,12 @@ func (_m *GrpcClient) NewTcpConnection(ctx context.Context, endpoint string) (*g return r0, r1 } -type mockConstructorTestingTNewGrpcClient interface { +// NewGrpcClient creates a new instance of GrpcClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewGrpcClient(t interface { mock.TestingT Cleanup(func()) -} - -// NewGrpcClient creates a new instance of GrpcClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewGrpcClient(t mockConstructorTestingTNewGrpcClient) *GrpcClient { +}) *GrpcClient { mock := &GrpcClient{} mock.Mock.Test(t) diff --git a/protocol/mocks/GrpcServer.go b/protocol/mocks/GrpcServer.go index c8daefae28..8496fe49fb 100644 --- a/protocol/mocks/GrpcServer.go +++ b/protocol/mocks/GrpcServer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -23,6 +23,10 @@ func (_m *GrpcServer) RegisterService(sd *grpc.ServiceDesc, ss interface{}) { func (_m *GrpcServer) Serve(lis net.Listener) error { ret := _m.Called(lis) + if len(ret) == 0 { + panic("no return value specified for Serve") + } + var r0 error if rf, ok := ret.Get(0).(func(net.Listener) error); ok { r0 = rf(lis) @@ -38,13 +42,12 @@ func (_m *GrpcServer) Stop() { _m.Called() } -type mockConstructorTestingTNewGrpcServer interface { +// NewGrpcServer creates a new instance of GrpcServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewGrpcServer(t interface { mock.TestingT Cleanup(func()) -} - -// NewGrpcServer creates a new instance of GrpcServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewGrpcServer(t mockConstructorTestingTNewGrpcServer) *GrpcServer { +}) *GrpcServer { mock := &GrpcServer{} mock.Mock.Test(t) diff --git a/protocol/mocks/HealthCheckable.go b/protocol/mocks/HealthCheckable.go index eec7222a02..dcb49686db 100644 --- a/protocol/mocks/HealthCheckable.go +++ b/protocol/mocks/HealthCheckable.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -13,6 +13,10 @@ type HealthCheckable struct { func (_m *HealthCheckable) HealthCheck() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for HealthCheck") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -37,6 +41,10 @@ func (_m *HealthCheckable) ReportSuccess() { func (_m *HealthCheckable) ServiceName() string { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for ServiceName") + } + var r0 string if rf, ok := ret.Get(0).(func() string); ok { r0 = rf() @@ -47,13 +55,12 @@ func (_m *HealthCheckable) ServiceName() string { return r0 } -type mockConstructorTestingTNewHealthCheckable interface { +// NewHealthCheckable creates a new instance of HealthCheckable. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewHealthCheckable(t interface { mock.TestingT Cleanup(func()) -} - -// NewHealthCheckable creates a new instance of HealthCheckable. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewHealthCheckable(t mockConstructorTestingTNewHealthCheckable) *HealthCheckable { +}) *HealthCheckable { mock := &HealthCheckable{} mock.Mock.Test(t) diff --git a/protocol/mocks/IndexerEventManager.go b/protocol/mocks/IndexerEventManager.go index 832ca79b13..62050ec555 100644 --- a/protocol/mocks/IndexerEventManager.go +++ b/protocol/mocks/IndexerEventManager.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -35,6 +35,10 @@ func (_m *IndexerEventManager) ClearEvents(ctx types.Context) { func (_m *IndexerEventManager) Enabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Enabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -49,6 +53,10 @@ func (_m *IndexerEventManager) Enabled() bool { func (_m *IndexerEventManager) ProduceBlock(ctx types.Context) *indexer_manager.IndexerTendermintBlock { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for ProduceBlock") + } + var r0 *indexer_manager.IndexerTendermintBlock if rf, ok := ret.Get(0).(func(types.Context) *indexer_manager.IndexerTendermintBlock); ok { r0 = rf(ctx) @@ -71,13 +79,12 @@ func (_m *IndexerEventManager) SendOnchainData(block *indexer_manager.IndexerTen _m.Called(block) } -type mockConstructorTestingTNewIndexerEventManager interface { +// NewIndexerEventManager creates a new instance of IndexerEventManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewIndexerEventManager(t interface { mock.TestingT Cleanup(func()) -} - -// NewIndexerEventManager creates a new instance of IndexerEventManager. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewIndexerEventManager(t mockConstructorTestingTNewIndexerEventManager) *IndexerEventManager { +}) *IndexerEventManager { mock := &IndexerEventManager{} mock.Mock.Test(t) diff --git a/protocol/mocks/IndexerMessageSender.go b/protocol/mocks/IndexerMessageSender.go index 6e33024280..dbc52634f0 100644 --- a/protocol/mocks/IndexerMessageSender.go +++ b/protocol/mocks/IndexerMessageSender.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type IndexerMessageSender struct { func (_m *IndexerMessageSender) Close() error { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Close") + } + var r0 error if rf, ok := ret.Get(0).(func() error); ok { r0 = rf() @@ -30,6 +34,10 @@ func (_m *IndexerMessageSender) Close() error { func (_m *IndexerMessageSender) Enabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Enabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -50,13 +58,12 @@ func (_m *IndexerMessageSender) SendOnchainData(message msgsender.Message) { _m.Called(message) } -type mockConstructorTestingTNewIndexerMessageSender interface { +// NewIndexerMessageSender creates a new instance of IndexerMessageSender. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewIndexerMessageSender(t interface { mock.TestingT Cleanup(func()) -} - -// NewIndexerMessageSender creates a new instance of IndexerMessageSender. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewIndexerMessageSender(t mockConstructorTestingTNewIndexerMessageSender) *IndexerMessageSender { +}) *IndexerMessageSender { mock := &IndexerMessageSender{} mock.Mock.Test(t) diff --git a/protocol/mocks/Logger.go b/protocol/mocks/Logger.go index 89805e3832..fadfc0148a 100644 --- a/protocol/mocks/Logger.go +++ b/protocol/mocks/Logger.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -32,6 +32,10 @@ func (_m *Logger) Error(msg string, keyVals ...interface{}) { func (_m *Logger) Impl() interface{} { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Impl") + } + var r0 interface{} if rf, ok := ret.Get(0).(func() interface{}); ok { r0 = rf() @@ -66,6 +70,10 @@ func (_m *Logger) With(keyVals ...interface{}) log.Logger { _ca = append(_ca, keyVals...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for With") + } + var r0 log.Logger if rf, ok := ret.Get(0).(func(...interface{}) log.Logger); ok { r0 = rf(keyVals...) @@ -78,13 +86,12 @@ func (_m *Logger) With(keyVals ...interface{}) log.Logger { return r0 } -type mockConstructorTestingTNewLogger interface { +// NewLogger creates a new instance of Logger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewLogger(t interface { mock.TestingT Cleanup(func()) -} - -// NewLogger creates a new instance of Logger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewLogger(t mockConstructorTestingTNewLogger) *Logger { +}) *Logger { mock := &Logger{} mock.Mock.Test(t) diff --git a/protocol/mocks/Makefile b/protocol/mocks/Makefile index 29f4641b29..12cd02acb2 100644 --- a/protocol/mocks/Makefile +++ b/protocol/mocks/Makefile @@ -4,6 +4,7 @@ COSMOS_VERSION=$(shell go list -m all | grep "github.com/dydxprotocol/cosmos-sdk COSMOS_STORE_VERSION=$(shell go list -m all | grep "cosmossdk.io/store[^/]" | awk '{print $$NF}') COSMOS_LOG_VERSION=$(shell go list -m all | grep "cosmossdk.io/log[^/]" | awk '{print $$NF}') COSMOS_GOGOPROTO_VERSION=$(shell go list -m all | grep "github.com/cosmos/gogoproto[^/]" | awk '{print $$NF}') +SLINKY_VERSION=$(shell go list -m all | grep "github.com/skip-mev/slinky[^/]" | awk '{print $$NF}') mock-clean: @rm -f ./mocks/*.go @@ -24,7 +25,7 @@ mock-gen: @go run github.com/vektra/mockery/v2 --name=PrepareBridgeKeeper --dir=./app/prepare --recursive --output=./mocks @go run github.com/vektra/mockery/v2 --name=PrepareClobKeeper --dir=./app/prepare --recursive --output=./mocks @go run github.com/vektra/mockery/v2 --name=PreparePerpetualsKeeper --dir=./app/prepare --recursive --output=./mocks - @go run github.com/vektra/mockery/v2 --name=PreparePricesKeeper --dir=./app/prepare --recursive --output=./mocks + @go run github.com/vektra/mockery/v2 --name=PricesKeeper --dir=./app/prepare --recursive --output=./mocks @go run github.com/vektra/mockery/v2 --name=ProcessBridgeKeeper --dir=./app/process --recursive --output=./mocks @go run github.com/vektra/mockery/v2 --name=ProcessClobKeeper --dir=./app/process --recursive --output=./mocks @go run github.com/vektra/mockery/v2 --name=ProcessStakingKeeper --dir=./app/process --recursive --output=./mocks @@ -55,6 +56,9 @@ mock-gen: @go run github.com/vektra/mockery/v2 --name=BridgeServiceClient --dir=./daemons/bridge/api --recursive --output=./mocks @go run github.com/vektra/mockery/v2 --name=BridgeQueryClient --dir=./daemons/bridge/client/types --recursive --output=./mocks @go run github.com/vektra/mockery/v2 --name=EthClient --dir=./daemons/bridge/client/types --recursive --output=./mocks + @go run github.com/vektra/mockery/v2 --name=PriceUpdateGenerator --dir=./app/prepare/prices --recursive --output=./mocks @go run github.com/vektra/mockery/v2 --name=PriceFetcher --dir=./daemons/slinky/client --recursive --output=./mocks @go run github.com/vektra/mockery/v2 --name=MarketPairFetcher --dir=./daemons/slinky/client --recursive --output=./mocks - @go run github.com/vektra/mockery/v2 --name=OracleClient --dir=$(GOPATH)/pkg/mod/github.com/skip-mev/slinky@$(SLINKY_VERSION)/service/clients/oracle --filename=oracle_client.go --recursive --output=./mocks + @go run github.com/vektra/mockery/v2 --name=OracleClient --dir=$(GOPATH)/pkg/mod/github.com/skip-mev/slinky@$(SLINKY_VERSION)/service/clients/oracle --recursive --output=./mocks + @go run github.com/vektra/mockery/v2 --name=ExtendVoteHandler --dir=$(GOPATH)/pkg/mod/github.com/dydxprotocol/cosmos-sdk@$(COSMOS_VERSION)/types --recursive --output=./mocks + @go run github.com/vektra/mockery/v2 --name=UpdateMarketPriceTxDecoder --dir=./app/process --recursive --output=./mocks diff --git a/protocol/mocks/MarketPairFetcher.go b/protocol/mocks/MarketPairFetcher.go index 9e1a43ce2d..eada02f661 100644 --- a/protocol/mocks/MarketPairFetcher.go +++ b/protocol/mocks/MarketPairFetcher.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -10,7 +10,7 @@ import ( mock "github.com/stretchr/testify/mock" - types "github.com/skip-mev/slinky/x/oracle/types" + types "github.com/skip-mev/slinky/pkg/types" ) // MarketPairFetcher is an autogenerated mock type for the MarketPairFetcher type diff --git a/protocol/mocks/MemClob.go b/protocol/mocks/MemClob.go index fd027b818e..143319fa30 100644 --- a/protocol/mocks/MemClob.go +++ b/protocol/mocks/MemClob.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -24,6 +24,10 @@ type MemClob struct { func (_m *MemClob) CancelOrder(ctx types.Context, msgCancelOrder *clobtypes.MsgCancelOrder) (*clobtypes.OffchainUpdates, error) { ret := _m.Called(ctx, msgCancelOrder) + if len(ret) == 0 { + panic("no return value specified for CancelOrder") + } + var r0 *clobtypes.OffchainUpdates var r1 error if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgCancelOrder) (*clobtypes.OffchainUpdates, error)); ok { @@ -50,6 +54,10 @@ func (_m *MemClob) CancelOrder(ctx types.Context, msgCancelOrder *clobtypes.MsgC func (_m *MemClob) CountSubaccountShortTermOrders(ctx types.Context, subaccountId subaccountstypes.SubaccountId) uint32 { ret := _m.Called(ctx, subaccountId) + if len(ret) == 0 { + panic("no return value specified for CountSubaccountShortTermOrders") + } + var r0 uint32 if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId) uint32); ok { r0 = rf(ctx, subaccountId) @@ -69,6 +77,10 @@ func (_m *MemClob) CreateOrderbook(ctx types.Context, clobPair clobtypes.ClobPai func (_m *MemClob) DeleverageSubaccount(ctx types.Context, subaccountId subaccountstypes.SubaccountId, perpetualId uint32, deltaQuantums *big.Int, isFinalSettlement bool) (*big.Int, error) { ret := _m.Called(ctx, subaccountId, perpetualId, deltaQuantums, isFinalSettlement) + if len(ret) == 0 { + panic("no return value specified for DeleverageSubaccount") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId, uint32, *big.Int, bool) (*big.Int, error)); ok { @@ -95,6 +107,10 @@ func (_m *MemClob) DeleverageSubaccount(ctx types.Context, subaccountId subaccou func (_m *MemClob) GetCancelOrder(ctx types.Context, orderId clobtypes.OrderId) (uint32, bool) { ret := _m.Called(ctx, orderId) + if len(ret) == 0 { + panic("no return value specified for GetCancelOrder") + } + var r0 uint32 var r1 bool if rf, ok := ret.Get(0).(func(types.Context, clobtypes.OrderId) (uint32, bool)); ok { @@ -119,6 +135,10 @@ func (_m *MemClob) GetCancelOrder(ctx types.Context, orderId clobtypes.OrderId) func (_m *MemClob) GetMidPrice(ctx types.Context, clobPairId clobtypes.ClobPairId) (clobtypes.Subticks, clobtypes.Order, clobtypes.Order, bool) { ret := _m.Called(ctx, clobPairId) + if len(ret) == 0 { + panic("no return value specified for GetMidPrice") + } + var r0 clobtypes.Subticks var r1 clobtypes.Order var r2 clobtypes.Order @@ -157,6 +177,10 @@ func (_m *MemClob) GetMidPrice(ctx types.Context, clobPairId clobtypes.ClobPairI func (_m *MemClob) GetOffchainUpdatesForOrderbookSnapshot(ctx types.Context, clobPairId clobtypes.ClobPairId) *clobtypes.OffchainUpdates { ret := _m.Called(ctx, clobPairId) + if len(ret) == 0 { + panic("no return value specified for GetOffchainUpdatesForOrderbookSnapshot") + } + var r0 *clobtypes.OffchainUpdates if rf, ok := ret.Get(0).(func(types.Context, clobtypes.ClobPairId) *clobtypes.OffchainUpdates); ok { r0 = rf(ctx, clobPairId) @@ -173,6 +197,10 @@ func (_m *MemClob) GetOffchainUpdatesForOrderbookSnapshot(ctx types.Context, clo func (_m *MemClob) GetOperationsRaw(ctx types.Context) []clobtypes.OperationRaw { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetOperationsRaw") + } + var r0 []clobtypes.OperationRaw if rf, ok := ret.Get(0).(func(types.Context) []clobtypes.OperationRaw); ok { r0 = rf(ctx) @@ -189,6 +217,10 @@ func (_m *MemClob) GetOperationsRaw(ctx types.Context) []clobtypes.OperationRaw func (_m *MemClob) GetOperationsToReplay(ctx types.Context) ([]clobtypes.InternalOperation, map[clobtypes.OrderHash][]byte) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetOperationsToReplay") + } + var r0 []clobtypes.InternalOperation var r1 map[clobtypes.OrderHash][]byte if rf, ok := ret.Get(0).(func(types.Context) ([]clobtypes.InternalOperation, map[clobtypes.OrderHash][]byte)); ok { @@ -217,6 +249,10 @@ func (_m *MemClob) GetOperationsToReplay(ctx types.Context) ([]clobtypes.Interna func (_m *MemClob) GetOrder(ctx types.Context, orderId clobtypes.OrderId) (clobtypes.Order, bool) { ret := _m.Called(ctx, orderId) + if len(ret) == 0 { + panic("no return value specified for GetOrder") + } + var r0 clobtypes.Order var r1 bool if rf, ok := ret.Get(0).(func(types.Context, clobtypes.OrderId) (clobtypes.Order, bool)); ok { @@ -241,6 +277,10 @@ func (_m *MemClob) GetOrder(ctx types.Context, orderId clobtypes.OrderId) (clobt func (_m *MemClob) GetOrderFilledAmount(ctx types.Context, orderId clobtypes.OrderId) subaccountstypes.BaseQuantums { ret := _m.Called(ctx, orderId) + if len(ret) == 0 { + panic("no return value specified for GetOrderFilledAmount") + } + var r0 subaccountstypes.BaseQuantums if rf, ok := ret.Get(0).(func(types.Context, clobtypes.OrderId) subaccountstypes.BaseQuantums); ok { r0 = rf(ctx, orderId) @@ -255,6 +295,10 @@ func (_m *MemClob) GetOrderFilledAmount(ctx types.Context, orderId clobtypes.Ord func (_m *MemClob) GetOrderRemainingAmount(ctx types.Context, order clobtypes.Order) (subaccountstypes.BaseQuantums, bool) { ret := _m.Called(ctx, order) + if len(ret) == 0 { + panic("no return value specified for GetOrderRemainingAmount") + } + var r0 subaccountstypes.BaseQuantums var r1 bool if rf, ok := ret.Get(0).(func(types.Context, clobtypes.Order) (subaccountstypes.BaseQuantums, bool)); ok { @@ -275,10 +319,74 @@ func (_m *MemClob) GetOrderRemainingAmount(ctx types.Context, order clobtypes.Or return r0, r1 } +// GetOrderbookUpdatesForOrderPlacement provides a mock function with given fields: ctx, order +func (_m *MemClob) GetOrderbookUpdatesForOrderPlacement(ctx types.Context, order clobtypes.Order) *clobtypes.OffchainUpdates { + ret := _m.Called(ctx, order) + + if len(ret) == 0 { + panic("no return value specified for GetOrderbookUpdatesForOrderPlacement") + } + + var r0 *clobtypes.OffchainUpdates + if rf, ok := ret.Get(0).(func(types.Context, clobtypes.Order) *clobtypes.OffchainUpdates); ok { + r0 = rf(ctx, order) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*clobtypes.OffchainUpdates) + } + } + + return r0 +} + +// GetOrderbookUpdatesForOrderRemoval provides a mock function with given fields: ctx, orderId +func (_m *MemClob) GetOrderbookUpdatesForOrderRemoval(ctx types.Context, orderId clobtypes.OrderId) *clobtypes.OffchainUpdates { + ret := _m.Called(ctx, orderId) + + if len(ret) == 0 { + panic("no return value specified for GetOrderbookUpdatesForOrderRemoval") + } + + var r0 *clobtypes.OffchainUpdates + if rf, ok := ret.Get(0).(func(types.Context, clobtypes.OrderId) *clobtypes.OffchainUpdates); ok { + r0 = rf(ctx, orderId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*clobtypes.OffchainUpdates) + } + } + + return r0 +} + +// GetOrderbookUpdatesForOrderUpdate provides a mock function with given fields: ctx, orderId +func (_m *MemClob) GetOrderbookUpdatesForOrderUpdate(ctx types.Context, orderId clobtypes.OrderId) *clobtypes.OffchainUpdates { + ret := _m.Called(ctx, orderId) + + if len(ret) == 0 { + panic("no return value specified for GetOrderbookUpdatesForOrderUpdate") + } + + var r0 *clobtypes.OffchainUpdates + if rf, ok := ret.Get(0).(func(types.Context, clobtypes.OrderId) *clobtypes.OffchainUpdates); ok { + r0 = rf(ctx, orderId) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*clobtypes.OffchainUpdates) + } + } + + return r0 +} + // GetPricePremium provides a mock function with given fields: ctx, clobPair, params func (_m *MemClob) GetPricePremium(ctx types.Context, clobPair clobtypes.ClobPair, params perpetualstypes.GetPricePremiumParams) (int32, error) { ret := _m.Called(ctx, clobPair, params) + if len(ret) == 0 { + panic("no return value specified for GetPricePremium") + } + var r0 int32 var r1 error if rf, ok := ret.Get(0).(func(types.Context, clobtypes.ClobPair, perpetualstypes.GetPricePremiumParams) (int32, error)); ok { @@ -303,6 +411,10 @@ func (_m *MemClob) GetPricePremium(ctx types.Context, clobPair clobtypes.ClobPai func (_m *MemClob) GetSubaccountOrders(ctx types.Context, clobPairId clobtypes.ClobPairId, subaccountId subaccountstypes.SubaccountId, side clobtypes.Order_Side) ([]clobtypes.Order, error) { ret := _m.Called(ctx, clobPairId, subaccountId, side) + if len(ret) == 0 { + panic("no return value specified for GetSubaccountOrders") + } + var r0 []clobtypes.Order var r1 error if rf, ok := ret.Get(0).(func(types.Context, clobtypes.ClobPairId, subaccountstypes.SubaccountId, clobtypes.Order_Side) ([]clobtypes.Order, error)); ok { @@ -334,6 +446,10 @@ func (_m *MemClob) InsertZeroFillDeleveragingIntoOperationsQueue(ctx types.Conte func (_m *MemClob) PlaceOrder(ctx types.Context, order clobtypes.Order) (subaccountstypes.BaseQuantums, clobtypes.OrderStatus, *clobtypes.OffchainUpdates, error) { ret := _m.Called(ctx, order) + if len(ret) == 0 { + panic("no return value specified for PlaceOrder") + } + var r0 subaccountstypes.BaseQuantums var r1 clobtypes.OrderStatus var r2 *clobtypes.OffchainUpdates @@ -374,6 +490,10 @@ func (_m *MemClob) PlaceOrder(ctx types.Context, order clobtypes.Order) (subacco func (_m *MemClob) PlacePerpetualLiquidation(ctx types.Context, liquidationOrder clobtypes.LiquidationOrder) (subaccountstypes.BaseQuantums, clobtypes.OrderStatus, *clobtypes.OffchainUpdates, error) { ret := _m.Called(ctx, liquidationOrder) + if len(ret) == 0 { + panic("no return value specified for PlacePerpetualLiquidation") + } + var r0 subaccountstypes.BaseQuantums var r1 clobtypes.OrderStatus var r2 *clobtypes.OffchainUpdates @@ -414,6 +534,10 @@ func (_m *MemClob) PlacePerpetualLiquidation(ctx types.Context, liquidationOrder func (_m *MemClob) PurgeInvalidMemclobState(ctx types.Context, fullyFilledOrderIds []clobtypes.OrderId, expiredStatefulOrderIds []clobtypes.OrderId, canceledStatefulOrderIds []clobtypes.OrderId, removedStatefulOrderIds []clobtypes.OrderId, existingOffchainUpdates *clobtypes.OffchainUpdates) *clobtypes.OffchainUpdates { ret := _m.Called(ctx, fullyFilledOrderIds, expiredStatefulOrderIds, canceledStatefulOrderIds, removedStatefulOrderIds, existingOffchainUpdates) + if len(ret) == 0 { + panic("no return value specified for PurgeInvalidMemclobState") + } + var r0 *clobtypes.OffchainUpdates if rf, ok := ret.Get(0).(func(types.Context, []clobtypes.OrderId, []clobtypes.OrderId, []clobtypes.OrderId, []clobtypes.OrderId, *clobtypes.OffchainUpdates) *clobtypes.OffchainUpdates); ok { r0 = rf(ctx, fullyFilledOrderIds, expiredStatefulOrderIds, canceledStatefulOrderIds, removedStatefulOrderIds, existingOffchainUpdates) @@ -440,6 +564,10 @@ func (_m *MemClob) RemoveOrderIfFilled(ctx types.Context, orderId clobtypes.Orde func (_m *MemClob) ReplayOperations(ctx types.Context, localOperations []clobtypes.InternalOperation, shortTermOrderTxBytes map[clobtypes.OrderHash][]byte, existingOffchainUpdates *clobtypes.OffchainUpdates) *clobtypes.OffchainUpdates { ret := _m.Called(ctx, localOperations, shortTermOrderTxBytes, existingOffchainUpdates) + if len(ret) == 0 { + panic("no return value specified for ReplayOperations") + } + var r0 *clobtypes.OffchainUpdates if rf, ok := ret.Get(0).(func(types.Context, []clobtypes.InternalOperation, map[clobtypes.OrderHash][]byte, *clobtypes.OffchainUpdates) *clobtypes.OffchainUpdates); ok { r0 = rf(ctx, localOperations, shortTermOrderTxBytes, existingOffchainUpdates) @@ -462,13 +590,12 @@ func (_m *MemClob) SetMemclobGauges(ctx types.Context) { _m.Called(ctx) } -type mockConstructorTestingTNewMemClob interface { +// NewMemClob creates a new instance of MemClob. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMemClob(t interface { mock.TestingT Cleanup(func()) -} - -// NewMemClob creates a new instance of MemClob. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMemClob(t mockConstructorTestingTNewMemClob) *MemClob { +}) *MemClob { mock := &MemClob{} mock.Mock.Test(t) diff --git a/protocol/mocks/MemClobKeeper.go b/protocol/mocks/MemClobKeeper.go index 6ef8a68e10..e9b6d3456a 100644 --- a/protocol/mocks/MemClobKeeper.go +++ b/protocol/mocks/MemClobKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -28,6 +28,10 @@ type MemClobKeeper struct { func (_m *MemClobKeeper) AddOrderToOrderbookCollatCheck(ctx types.Context, clobPairId clobtypes.ClobPairId, subaccountOpenOrders map[subaccountstypes.SubaccountId][]clobtypes.PendingOpenOrder) (bool, map[subaccountstypes.SubaccountId]subaccountstypes.UpdateResult) { ret := _m.Called(ctx, clobPairId, subaccountOpenOrders) + if len(ret) == 0 { + panic("no return value specified for AddOrderToOrderbookCollatCheck") + } + var r0 bool var r1 map[subaccountstypes.SubaccountId]subaccountstypes.UpdateResult if rf, ok := ret.Get(0).(func(types.Context, clobtypes.ClobPairId, map[subaccountstypes.SubaccountId][]clobtypes.PendingOpenOrder) (bool, map[subaccountstypes.SubaccountId]subaccountstypes.UpdateResult)); ok { @@ -54,6 +58,10 @@ func (_m *MemClobKeeper) AddOrderToOrderbookCollatCheck(ctx types.Context, clobP func (_m *MemClobKeeper) AddPreexistingStatefulOrder(ctx types.Context, order *clobtypes.Order, memclob clobtypes.MemClob) (subaccountstypes.BaseQuantums, clobtypes.OrderStatus, *clobtypes.OffchainUpdates, error) { ret := _m.Called(ctx, order, memclob) + if len(ret) == 0 { + panic("no return value specified for AddPreexistingStatefulOrder") + } + var r0 subaccountstypes.BaseQuantums var r1 clobtypes.OrderStatus var r2 *clobtypes.OffchainUpdates @@ -94,6 +102,10 @@ func (_m *MemClobKeeper) AddPreexistingStatefulOrder(ctx types.Context, order *c func (_m *MemClobKeeper) CanDeleverageSubaccount(ctx types.Context, subaccountId subaccountstypes.SubaccountId, perpetualId uint32) (bool, bool, error) { ret := _m.Called(ctx, subaccountId, perpetualId) + if len(ret) == 0 { + panic("no return value specified for CanDeleverageSubaccount") + } + var r0 bool var r1 bool var r2 error @@ -125,6 +137,10 @@ func (_m *MemClobKeeper) CanDeleverageSubaccount(ctx types.Context, subaccountId func (_m *MemClobKeeper) CancelShortTermOrder(ctx types.Context, msgCancelOrder *clobtypes.MsgCancelOrder) error { ret := _m.Called(ctx, msgCancelOrder) + if len(ret) == 0 { + panic("no return value specified for CancelShortTermOrder") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, *clobtypes.MsgCancelOrder) error); ok { r0 = rf(ctx, msgCancelOrder) @@ -139,6 +155,10 @@ func (_m *MemClobKeeper) CancelShortTermOrder(ctx types.Context, msgCancelOrder func (_m *MemClobKeeper) GetIndexerEventManager() indexer_manager.IndexerEventManager { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetIndexerEventManager") + } + var r0 indexer_manager.IndexerEventManager if rf, ok := ret.Get(0).(func() indexer_manager.IndexerEventManager); ok { r0 = rf() @@ -155,6 +175,10 @@ func (_m *MemClobKeeper) GetIndexerEventManager() indexer_manager.IndexerEventMa func (_m *MemClobKeeper) GetLongTermOrderPlacement(ctx types.Context, orderId clobtypes.OrderId) (clobtypes.LongTermOrderPlacement, bool) { ret := _m.Called(ctx, orderId) + if len(ret) == 0 { + panic("no return value specified for GetLongTermOrderPlacement") + } + var r0 clobtypes.LongTermOrderPlacement var r1 bool if rf, ok := ret.Get(0).(func(types.Context, clobtypes.OrderId) (clobtypes.LongTermOrderPlacement, bool)); ok { @@ -179,6 +203,10 @@ func (_m *MemClobKeeper) GetLongTermOrderPlacement(ctx types.Context, orderId cl func (_m *MemClobKeeper) GetOrderFillAmount(ctx types.Context, orderId clobtypes.OrderId) (bool, subaccountstypes.BaseQuantums, uint32) { ret := _m.Called(ctx, orderId) + if len(ret) == 0 { + panic("no return value specified for GetOrderFillAmount") + } + var r0 bool var r1 subaccountstypes.BaseQuantums var r2 uint32 @@ -210,6 +238,10 @@ func (_m *MemClobKeeper) GetOrderFillAmount(ctx types.Context, orderId clobtypes func (_m *MemClobKeeper) GetStatePosition(ctx types.Context, subaccountId subaccountstypes.SubaccountId, clobPairId clobtypes.ClobPairId) *big.Int { ret := _m.Called(ctx, subaccountId, clobPairId) + if len(ret) == 0 { + panic("no return value specified for GetStatePosition") + } + var r0 *big.Int if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId, clobtypes.ClobPairId) *big.Int); ok { r0 = rf(ctx, subaccountId, clobPairId) @@ -226,6 +258,10 @@ func (_m *MemClobKeeper) GetStatePosition(ctx types.Context, subaccountId subacc func (_m *MemClobKeeper) IsLiquidatable(ctx types.Context, subaccountId subaccountstypes.SubaccountId) (bool, error) { ret := _m.Called(ctx, subaccountId) + if len(ret) == 0 { + panic("no return value specified for IsLiquidatable") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId) (bool, error)); ok { @@ -250,6 +286,10 @@ func (_m *MemClobKeeper) IsLiquidatable(ctx types.Context, subaccountId subaccou func (_m *MemClobKeeper) Logger(ctx types.Context) log.Logger { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Logger") + } + var r0 log.Logger if rf, ok := ret.Get(0).(func(types.Context) log.Logger); ok { r0 = rf(ctx) @@ -271,6 +311,10 @@ func (_m *MemClobKeeper) MustAddOrderToStatefulOrdersTimeSlice(ctx types.Context func (_m *MemClobKeeper) OffsetSubaccountPerpetualPosition(ctx types.Context, liquidatedSubaccountId subaccountstypes.SubaccountId, perpetualId uint32, deltaQuantumsTotal *big.Int, isFinalSettlement bool) ([]clobtypes.MatchPerpetualDeleveraging_Fill, *big.Int) { ret := _m.Called(ctx, liquidatedSubaccountId, perpetualId, deltaQuantumsTotal, isFinalSettlement) + if len(ret) == 0 { + panic("no return value specified for OffsetSubaccountPerpetualPosition") + } + var r0 []clobtypes.MatchPerpetualDeleveraging_Fill var r1 *big.Int if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId, uint32, *big.Int, bool) ([]clobtypes.MatchPerpetualDeleveraging_Fill, *big.Int)); ok { @@ -299,6 +343,10 @@ func (_m *MemClobKeeper) OffsetSubaccountPerpetualPosition(ctx types.Context, li func (_m *MemClobKeeper) ProcessSingleMatch(ctx types.Context, matchWithOrders *clobtypes.MatchWithOrders) (bool, subaccountstypes.UpdateResult, subaccountstypes.UpdateResult, *clobtypes.OffchainUpdates, error) { ret := _m.Called(ctx, matchWithOrders) + if len(ret) == 0 { + panic("no return value specified for ProcessSingleMatch") + } + var r0 bool var r1 subaccountstypes.UpdateResult var r2 subaccountstypes.UpdateResult @@ -346,6 +394,10 @@ func (_m *MemClobKeeper) ProcessSingleMatch(ctx types.Context, matchWithOrders * func (_m *MemClobKeeper) ReplayPlaceOrder(ctx types.Context, msg *clobtypes.MsgPlaceOrder) (subaccountstypes.BaseQuantums, clobtypes.OrderStatus, *clobtypes.OffchainUpdates, error) { ret := _m.Called(ctx, msg) + if len(ret) == 0 { + panic("no return value specified for ReplayPlaceOrder") + } + var r0 subaccountstypes.BaseQuantums var r1 clobtypes.OrderStatus var r2 *clobtypes.OffchainUpdates @@ -382,6 +434,11 @@ func (_m *MemClobKeeper) ReplayPlaceOrder(ctx types.Context, msg *clobtypes.MsgP return r0, r1, r2, r3 } +// SendOrderbookUpdates provides a mock function with given fields: offchainUpdates, snapshot +func (_m *MemClobKeeper) SendOrderbookUpdates(offchainUpdates *clobtypes.OffchainUpdates, snapshot bool) { + _m.Called(offchainUpdates, snapshot) +} + // SetLongTermOrderPlacement provides a mock function with given fields: ctx, order, blockHeight func (_m *MemClobKeeper) SetLongTermOrderPlacement(ctx types.Context, order clobtypes.Order, blockHeight uint32) { _m.Called(ctx, order, blockHeight) @@ -391,6 +448,10 @@ func (_m *MemClobKeeper) SetLongTermOrderPlacement(ctx types.Context, order clob func (_m *MemClobKeeper) ValidateSubaccountEquityTierLimitForShortTermOrder(ctx types.Context, order clobtypes.Order) error { ret := _m.Called(ctx, order) + if len(ret) == 0 { + panic("no return value specified for ValidateSubaccountEquityTierLimitForShortTermOrder") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, clobtypes.Order) error); ok { r0 = rf(ctx, order) @@ -405,6 +466,10 @@ func (_m *MemClobKeeper) ValidateSubaccountEquityTierLimitForShortTermOrder(ctx func (_m *MemClobKeeper) ValidateSubaccountEquityTierLimitForStatefulOrder(ctx types.Context, order clobtypes.Order) error { ret := _m.Called(ctx, order) + if len(ret) == 0 { + panic("no return value specified for ValidateSubaccountEquityTierLimitForStatefulOrder") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, clobtypes.Order) error); ok { r0 = rf(ctx, order) @@ -415,13 +480,12 @@ func (_m *MemClobKeeper) ValidateSubaccountEquityTierLimitForStatefulOrder(ctx t return r0 } -type mockConstructorTestingTNewMemClobKeeper interface { +// NewMemClobKeeper creates a new instance of MemClobKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMemClobKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewMemClobKeeper creates a new instance of MemClobKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMemClobKeeper(t mockConstructorTestingTNewMemClobKeeper) *MemClobKeeper { +}) *MemClobKeeper { mock := &MemClobKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/MsgRouter.go b/protocol/mocks/MsgRouter.go index 1855079c88..b7a12df869 100644 --- a/protocol/mocks/MsgRouter.go +++ b/protocol/mocks/MsgRouter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type MsgRouter struct { func (_m *MsgRouter) Handler(msg proto.Message) func(types.Context, proto.Message) (*types.Result, error) { ret := _m.Called(msg) + if len(ret) == 0 { + panic("no return value specified for Handler") + } + var r0 func(types.Context, proto.Message) (*types.Result, error) if rf, ok := ret.Get(0).(func(proto.Message) func(types.Context, proto.Message) (*types.Result, error)); ok { r0 = rf(msg) @@ -30,13 +34,12 @@ func (_m *MsgRouter) Handler(msg proto.Message) func(types.Context, proto.Messag return r0 } -type mockConstructorTestingTNewMsgRouter interface { +// NewMsgRouter creates a new instance of MsgRouter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMsgRouter(t interface { mock.TestingT Cleanup(func()) -} - -// NewMsgRouter creates a new instance of MsgRouter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMsgRouter(t mockConstructorTestingTNewMsgRouter) *MsgRouter { +}) *MsgRouter { mock := &MsgRouter{} mock.Mock.Test(t) diff --git a/protocol/mocks/MultiStore.go b/protocol/mocks/MultiStore.go index ec8fed1de5..97881f5ea1 100644 --- a/protocol/mocks/MultiStore.go +++ b/protocol/mocks/MultiStore.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type MultiStore struct { func (_m *MultiStore) CacheMultiStore() types.CacheMultiStore { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CacheMultiStore") + } + var r0 types.CacheMultiStore if rf, ok := ret.Get(0).(func() types.CacheMultiStore); ok { r0 = rf() @@ -34,6 +38,10 @@ func (_m *MultiStore) CacheMultiStore() types.CacheMultiStore { func (_m *MultiStore) CacheMultiStoreWithVersion(version int64) (types.CacheMultiStore, error) { ret := _m.Called(version) + if len(ret) == 0 { + panic("no return value specified for CacheMultiStoreWithVersion") + } + var r0 types.CacheMultiStore var r1 error if rf, ok := ret.Get(0).(func(int64) (types.CacheMultiStore, error)); ok { @@ -60,6 +68,10 @@ func (_m *MultiStore) CacheMultiStoreWithVersion(version int64) (types.CacheMult func (_m *MultiStore) CacheWrap() types.CacheWrap { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for CacheWrap") + } + var r0 types.CacheWrap if rf, ok := ret.Get(0).(func() types.CacheWrap); ok { r0 = rf() @@ -76,6 +88,10 @@ func (_m *MultiStore) CacheWrap() types.CacheWrap { func (_m *MultiStore) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) types.CacheWrap { ret := _m.Called(w, tc) + if len(ret) == 0 { + panic("no return value specified for CacheWrapWithTrace") + } + var r0 types.CacheWrap if rf, ok := ret.Get(0).(func(io.Writer, types.TraceContext) types.CacheWrap); ok { r0 = rf(w, tc) @@ -92,6 +108,10 @@ func (_m *MultiStore) CacheWrapWithTrace(w io.Writer, tc types.TraceContext) typ func (_m *MultiStore) GetKVStore(_a0 types.StoreKey) types.KVStore { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetKVStore") + } + var r0 types.KVStore if rf, ok := ret.Get(0).(func(types.StoreKey) types.KVStore); ok { r0 = rf(_a0) @@ -108,6 +128,10 @@ func (_m *MultiStore) GetKVStore(_a0 types.StoreKey) types.KVStore { func (_m *MultiStore) GetStore(_a0 types.StoreKey) types.Store { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for GetStore") + } + var r0 types.Store if rf, ok := ret.Get(0).(func(types.StoreKey) types.Store); ok { r0 = rf(_a0) @@ -124,6 +148,10 @@ func (_m *MultiStore) GetStore(_a0 types.StoreKey) types.Store { func (_m *MultiStore) GetStoreType() types.StoreType { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetStoreType") + } + var r0 types.StoreType if rf, ok := ret.Get(0).(func() types.StoreType); ok { r0 = rf() @@ -138,6 +166,10 @@ func (_m *MultiStore) GetStoreType() types.StoreType { func (_m *MultiStore) LatestVersion() int64 { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for LatestVersion") + } + var r0 int64 if rf, ok := ret.Get(0).(func() int64); ok { r0 = rf() @@ -152,6 +184,10 @@ func (_m *MultiStore) LatestVersion() int64 { func (_m *MultiStore) SetTracer(w io.Writer) types.MultiStore { ret := _m.Called(w) + if len(ret) == 0 { + panic("no return value specified for SetTracer") + } + var r0 types.MultiStore if rf, ok := ret.Get(0).(func(io.Writer) types.MultiStore); ok { r0 = rf(w) @@ -168,6 +204,10 @@ func (_m *MultiStore) SetTracer(w io.Writer) types.MultiStore { func (_m *MultiStore) SetTracingContext(_a0 types.TraceContext) types.MultiStore { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for SetTracingContext") + } + var r0 types.MultiStore if rf, ok := ret.Get(0).(func(types.TraceContext) types.MultiStore); ok { r0 = rf(_a0) @@ -184,6 +224,10 @@ func (_m *MultiStore) SetTracingContext(_a0 types.TraceContext) types.MultiStore func (_m *MultiStore) TracingEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TracingEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -194,13 +238,12 @@ func (_m *MultiStore) TracingEnabled() bool { return r0 } -type mockConstructorTestingTNewMultiStore interface { +// NewMultiStore creates a new instance of MultiStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMultiStore(t interface { mock.TestingT Cleanup(func()) -} - -// NewMultiStore creates a new instance of MultiStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewMultiStore(t mockConstructorTestingTNewMultiStore) *MultiStore { +}) *MultiStore { mock := &MultiStore{} mock.Mock.Test(t) diff --git a/protocol/mocks/OracleClient.go b/protocol/mocks/OracleClient.go index 9f6ce188cc..c45ee24acc 100644 --- a/protocol/mocks/OracleClient.go +++ b/protocol/mocks/OracleClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks diff --git a/protocol/mocks/PerpetualsClobKeeper.go b/protocol/mocks/PerpetualsClobKeeper.go index 7a0e85c655..5072c475e7 100644 --- a/protocol/mocks/PerpetualsClobKeeper.go +++ b/protocol/mocks/PerpetualsClobKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type PerpetualsClobKeeper struct { func (_m *PerpetualsClobKeeper) GetPricePremiumForPerpetual(ctx types.Context, perpetualId uint32, params perpetualstypes.GetPricePremiumParams) (int32, error) { ret := _m.Called(ctx, perpetualId, params) + if len(ret) == 0 { + panic("no return value specified for GetPricePremiumForPerpetual") + } + var r0 int32 var r1 error if rf, ok := ret.Get(0).(func(types.Context, uint32, perpetualstypes.GetPricePremiumParams) (int32, error)); ok { @@ -41,6 +45,10 @@ func (_m *PerpetualsClobKeeper) GetPricePremiumForPerpetual(ctx types.Context, p func (_m *PerpetualsClobKeeper) IsPerpetualClobPairActive(ctx types.Context, perpetualId uint32) (bool, error) { ret := _m.Called(ctx, perpetualId) + if len(ret) == 0 { + panic("no return value specified for IsPerpetualClobPairActive") + } + var r0 bool var r1 error if rf, ok := ret.Get(0).(func(types.Context, uint32) (bool, error)); ok { @@ -61,13 +69,12 @@ func (_m *PerpetualsClobKeeper) IsPerpetualClobPairActive(ctx types.Context, per return r0, r1 } -type mockConstructorTestingTNewPerpetualsClobKeeper interface { +// NewPerpetualsClobKeeper creates a new instance of PerpetualsClobKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPerpetualsClobKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewPerpetualsClobKeeper creates a new instance of PerpetualsClobKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewPerpetualsClobKeeper(t mockConstructorTestingTNewPerpetualsClobKeeper) *PerpetualsClobKeeper { +}) *PerpetualsClobKeeper { mock := &PerpetualsClobKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/PerpetualsKeeper.go b/protocol/mocks/PerpetualsKeeper.go index a82605b354..515ba331ad 100644 --- a/protocol/mocks/PerpetualsKeeper.go +++ b/protocol/mocks/PerpetualsKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type PerpetualsKeeper struct { func (_m *PerpetualsKeeper) AddPremiumVotes(ctx types.Context, votes []perpetualstypes.FundingPremium) error { ret := _m.Called(ctx, votes) + if len(ret) == 0 { + panic("no return value specified for AddPremiumVotes") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, []perpetualstypes.FundingPremium) error); ok { r0 = rf(ctx, votes) @@ -34,6 +38,10 @@ func (_m *PerpetualsKeeper) AddPremiumVotes(ctx types.Context, votes []perpetual func (_m *PerpetualsKeeper) CreatePerpetual(ctx types.Context, id uint32, ticker string, marketId uint32, atomicResolution int32, defaultFundingPpm int32, liquidityTier uint32, marketType perpetualstypes.PerpetualMarketType) (perpetualstypes.Perpetual, error) { ret := _m.Called(ctx, id, ticker, marketId, atomicResolution, defaultFundingPpm, liquidityTier, marketType) + if len(ret) == 0 { + panic("no return value specified for CreatePerpetual") + } + var r0 perpetualstypes.Perpetual var r1 error if rf, ok := ret.Get(0).(func(types.Context, uint32, string, uint32, int32, int32, uint32, perpetualstypes.PerpetualMarketType) (perpetualstypes.Perpetual, error)); ok { @@ -58,6 +66,10 @@ func (_m *PerpetualsKeeper) CreatePerpetual(ctx types.Context, id uint32, ticker func (_m *PerpetualsKeeper) GetAddPremiumVotes(ctx types.Context) *perpetualstypes.MsgAddPremiumVotes { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAddPremiumVotes") + } + var r0 *perpetualstypes.MsgAddPremiumVotes if rf, ok := ret.Get(0).(func(types.Context) *perpetualstypes.MsgAddPremiumVotes); ok { r0 = rf(ctx) @@ -74,6 +86,10 @@ func (_m *PerpetualsKeeper) GetAddPremiumVotes(ctx types.Context) *perpetualstyp func (_m *PerpetualsKeeper) GetAllPerpetuals(ctx types.Context) []perpetualstypes.Perpetual { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAllPerpetuals") + } + var r0 []perpetualstypes.Perpetual if rf, ok := ret.Get(0).(func(types.Context) []perpetualstypes.Perpetual); ok { r0 = rf(ctx) @@ -90,6 +106,10 @@ func (_m *PerpetualsKeeper) GetAllPerpetuals(ctx types.Context) []perpetualstype func (_m *PerpetualsKeeper) GetMarginRequirements(ctx types.Context, id uint32, bigQuantums *big.Int) (*big.Int, *big.Int, error) { ret := _m.Called(ctx, id, bigQuantums) + if len(ret) == 0 { + panic("no return value specified for GetMarginRequirements") + } + var r0 *big.Int var r1 *big.Int var r2 error @@ -125,6 +145,10 @@ func (_m *PerpetualsKeeper) GetMarginRequirements(ctx types.Context, id uint32, func (_m *PerpetualsKeeper) GetNetCollateral(ctx types.Context, id uint32, bigQuantums *big.Int) (*big.Int, error) { ret := _m.Called(ctx, id, bigQuantums) + if len(ret) == 0 { + panic("no return value specified for GetNetCollateral") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(types.Context, uint32, *big.Int) (*big.Int, error)); ok { @@ -151,6 +175,10 @@ func (_m *PerpetualsKeeper) GetNetCollateral(ctx types.Context, id uint32, bigQu func (_m *PerpetualsKeeper) GetNetNotional(ctx types.Context, id uint32, bigQuantums *big.Int) (*big.Int, error) { ret := _m.Called(ctx, id, bigQuantums) + if len(ret) == 0 { + panic("no return value specified for GetNetNotional") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(types.Context, uint32, *big.Int) (*big.Int, error)); ok { @@ -177,6 +205,10 @@ func (_m *PerpetualsKeeper) GetNetNotional(ctx types.Context, id uint32, bigQuan func (_m *PerpetualsKeeper) GetNotionalInBaseQuantums(ctx types.Context, id uint32, bigQuoteQuantums *big.Int) (*big.Int, error) { ret := _m.Called(ctx, id, bigQuoteQuantums) + if len(ret) == 0 { + panic("no return value specified for GetNotionalInBaseQuantums") + } + var r0 *big.Int var r1 error if rf, ok := ret.Get(0).(func(types.Context, uint32, *big.Int) (*big.Int, error)); ok { @@ -203,6 +235,10 @@ func (_m *PerpetualsKeeper) GetNotionalInBaseQuantums(ctx types.Context, id uint func (_m *PerpetualsKeeper) HasAuthority(authority string) bool { ret := _m.Called(authority) + if len(ret) == 0 { + panic("no return value specified for HasAuthority") + } + var r0 bool if rf, ok := ret.Get(0).(func(string) bool); ok { r0 = rf(authority) @@ -227,6 +263,10 @@ func (_m *PerpetualsKeeper) MaybeProcessNewFundingTickEpoch(ctx types.Context) { func (_m *PerpetualsKeeper) ModifyPerpetual(ctx types.Context, id uint32, ticker string, marketId uint32, defaultFundingPpm int32, liquidityTier uint32) (perpetualstypes.Perpetual, error) { ret := _m.Called(ctx, id, ticker, marketId, defaultFundingPpm, liquidityTier) + if len(ret) == 0 { + panic("no return value specified for ModifyPerpetual") + } + var r0 perpetualstypes.Perpetual var r1 error if rf, ok := ret.Get(0).(func(types.Context, uint32, string, uint32, int32, uint32) (perpetualstypes.Perpetual, error)); ok { @@ -251,6 +291,10 @@ func (_m *PerpetualsKeeper) ModifyPerpetual(ctx types.Context, id uint32, ticker func (_m *PerpetualsKeeper) PerformStatefulPremiumVotesValidation(ctx types.Context, msg *perpetualstypes.MsgAddPremiumVotes) error { ret := _m.Called(ctx, msg) + if len(ret) == 0 { + panic("no return value specified for PerformStatefulPremiumVotesValidation") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, *perpetualstypes.MsgAddPremiumVotes) error); ok { r0 = rf(ctx, msg) @@ -265,6 +309,10 @@ func (_m *PerpetualsKeeper) PerformStatefulPremiumVotesValidation(ctx types.Cont func (_m *PerpetualsKeeper) SetLiquidityTier(ctx types.Context, id uint32, name string, initialMarginPpm uint32, maintenanceFractionPpm uint32, impactNotional uint64, openInterestLowerCap uint64, openInterestUpperCap uint64) (perpetualstypes.LiquidityTier, error) { ret := _m.Called(ctx, id, name, initialMarginPpm, maintenanceFractionPpm, impactNotional, openInterestLowerCap, openInterestUpperCap) + if len(ret) == 0 { + panic("no return value specified for SetLiquidityTier") + } + var r0 perpetualstypes.LiquidityTier var r1 error if rf, ok := ret.Get(0).(func(types.Context, uint32, string, uint32, uint32, uint64, uint64, uint64) (perpetualstypes.LiquidityTier, error)); ok { @@ -289,6 +337,10 @@ func (_m *PerpetualsKeeper) SetLiquidityTier(ctx types.Context, id uint32, name func (_m *PerpetualsKeeper) SetParams(ctx types.Context, params perpetualstypes.Params) error { ret := _m.Called(ctx, params) + if len(ret) == 0 { + panic("no return value specified for SetParams") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, perpetualstypes.Params) error); ok { r0 = rf(ctx, params) @@ -303,6 +355,10 @@ func (_m *PerpetualsKeeper) SetParams(ctx types.Context, params perpetualstypes. func (_m *PerpetualsKeeper) SetPerpetualMarketType(ctx types.Context, id uint32, marketType perpetualstypes.PerpetualMarketType) (perpetualstypes.Perpetual, error) { ret := _m.Called(ctx, id, marketType) + if len(ret) == 0 { + panic("no return value specified for SetPerpetualMarketType") + } + var r0 perpetualstypes.Perpetual var r1 error if rf, ok := ret.Get(0).(func(types.Context, uint32, perpetualstypes.PerpetualMarketType) (perpetualstypes.Perpetual, error)); ok { @@ -323,13 +379,12 @@ func (_m *PerpetualsKeeper) SetPerpetualMarketType(ctx types.Context, id uint32, return r0, r1 } -type mockConstructorTestingTNewPerpetualsKeeper interface { +// NewPerpetualsKeeper creates a new instance of PerpetualsKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPerpetualsKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewPerpetualsKeeper creates a new instance of PerpetualsKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewPerpetualsKeeper(t mockConstructorTestingTNewPerpetualsKeeper) *PerpetualsKeeper { +}) *PerpetualsKeeper { mock := &PerpetualsKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/PrepareBridgeKeeper.go b/protocol/mocks/PrepareBridgeKeeper.go index 832ba4e0d3..f7a3132e31 100644 --- a/protocol/mocks/PrepareBridgeKeeper.go +++ b/protocol/mocks/PrepareBridgeKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type PrepareBridgeKeeper struct { func (_m *PrepareBridgeKeeper) GetAcknowledgeBridges(ctx types.Context, blockTimestamp time.Time) *bridgetypes.MsgAcknowledgeBridges { ret := _m.Called(ctx, blockTimestamp) + if len(ret) == 0 { + panic("no return value specified for GetAcknowledgeBridges") + } + var r0 *bridgetypes.MsgAcknowledgeBridges if rf, ok := ret.Get(0).(func(types.Context, time.Time) *bridgetypes.MsgAcknowledgeBridges); ok { r0 = rf(ctx, blockTimestamp) @@ -32,13 +36,12 @@ func (_m *PrepareBridgeKeeper) GetAcknowledgeBridges(ctx types.Context, blockTim return r0 } -type mockConstructorTestingTNewPrepareBridgeKeeper interface { +// NewPrepareBridgeKeeper creates a new instance of PrepareBridgeKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPrepareBridgeKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewPrepareBridgeKeeper creates a new instance of PrepareBridgeKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewPrepareBridgeKeeper(t mockConstructorTestingTNewPrepareBridgeKeeper) *PrepareBridgeKeeper { +}) *PrepareBridgeKeeper { mock := &PrepareBridgeKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/PrepareClobKeeper.go b/protocol/mocks/PrepareClobKeeper.go index 9e1691998d..6c67355640 100644 --- a/protocol/mocks/PrepareClobKeeper.go +++ b/protocol/mocks/PrepareClobKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type PrepareClobKeeper struct { func (_m *PrepareClobKeeper) GetOperations(ctx types.Context) *clobtypes.MsgProposedOperations { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetOperations") + } + var r0 *clobtypes.MsgProposedOperations if rf, ok := ret.Get(0).(func(types.Context) *clobtypes.MsgProposedOperations); ok { r0 = rf(ctx) @@ -30,13 +34,12 @@ func (_m *PrepareClobKeeper) GetOperations(ctx types.Context) *clobtypes.MsgProp return r0 } -type mockConstructorTestingTNewPrepareClobKeeper interface { +// NewPrepareClobKeeper creates a new instance of PrepareClobKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPrepareClobKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewPrepareClobKeeper creates a new instance of PrepareClobKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewPrepareClobKeeper(t mockConstructorTestingTNewPrepareClobKeeper) *PrepareClobKeeper { +}) *PrepareClobKeeper { mock := &PrepareClobKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/PreparePerpetualsKeeper.go b/protocol/mocks/PreparePerpetualsKeeper.go index 8c98aace20..6c417863b6 100644 --- a/protocol/mocks/PreparePerpetualsKeeper.go +++ b/protocol/mocks/PreparePerpetualsKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type PreparePerpetualsKeeper struct { func (_m *PreparePerpetualsKeeper) GetAddPremiumVotes(ctx types.Context) *perpetualstypes.MsgAddPremiumVotes { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAddPremiumVotes") + } + var r0 *perpetualstypes.MsgAddPremiumVotes if rf, ok := ret.Get(0).(func(types.Context) *perpetualstypes.MsgAddPremiumVotes); ok { r0 = rf(ctx) @@ -30,13 +34,12 @@ func (_m *PreparePerpetualsKeeper) GetAddPremiumVotes(ctx types.Context) *perpet return r0 } -type mockConstructorTestingTNewPreparePerpetualsKeeper interface { +// NewPreparePerpetualsKeeper creates a new instance of PreparePerpetualsKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPreparePerpetualsKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewPreparePerpetualsKeeper creates a new instance of PreparePerpetualsKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewPreparePerpetualsKeeper(t mockConstructorTestingTNewPreparePerpetualsKeeper) *PreparePerpetualsKeeper { +}) *PreparePerpetualsKeeper { mock := &PreparePerpetualsKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/PriceFeedMutableMarketConfigs.go b/protocol/mocks/PriceFeedMutableMarketConfigs.go index 13a5cd89de..066812efdb 100644 --- a/protocol/mocks/PriceFeedMutableMarketConfigs.go +++ b/protocol/mocks/PriceFeedMutableMarketConfigs.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -27,6 +27,10 @@ func (_m *PricefeedMutableMarketConfigs) AddPriceFetcher(updater types.ExchangeC func (_m *PricefeedMutableMarketConfigs) GetExchangeMarketConfigCopy(id string) (*types.MutableExchangeMarketConfig, error) { ret := _m.Called(id) + if len(ret) == 0 { + panic("no return value specified for GetExchangeMarketConfigCopy") + } + var r0 *types.MutableExchangeMarketConfig var r1 error if rf, ok := ret.Get(0).(func(string) (*types.MutableExchangeMarketConfig, error)); ok { @@ -53,6 +57,10 @@ func (_m *PricefeedMutableMarketConfigs) GetExchangeMarketConfigCopy(id string) func (_m *PricefeedMutableMarketConfigs) GetMarketConfigCopies(markets []uint32) ([]*types.MutableMarketConfig, error) { ret := _m.Called(markets) + if len(ret) == 0 { + panic("no return value specified for GetMarketConfigCopies") + } + var r0 []*types.MutableMarketConfig var r1 error if rf, ok := ret.Get(0).(func([]uint32) ([]*types.MutableMarketConfig, error)); ok { @@ -79,6 +87,10 @@ func (_m *PricefeedMutableMarketConfigs) GetMarketConfigCopies(markets []uint32) func (_m *PricefeedMutableMarketConfigs) UpdateMarkets(marketParams []pricestypes.MarketParam) (map[uint32]error, error) { ret := _m.Called(marketParams) + if len(ret) == 0 { + panic("no return value specified for UpdateMarkets") + } + var r0 map[uint32]error var r1 error if rf, ok := ret.Get(0).(func([]pricestypes.MarketParam) (map[uint32]error, error)); ok { @@ -101,13 +113,12 @@ func (_m *PricefeedMutableMarketConfigs) UpdateMarkets(marketParams []pricestype return r0, r1 } -type mockConstructorTestingTNewPricefeedMutableMarketConfigs interface { +// NewPricefeedMutableMarketConfigs creates a new instance of PricefeedMutableMarketConfigs. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPricefeedMutableMarketConfigs(t interface { mock.TestingT Cleanup(func()) -} - -// NewPricefeedMutableMarketConfigs creates a new instance of PricefeedMutableMarketConfigs. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewPricefeedMutableMarketConfigs(t mockConstructorTestingTNewPricefeedMutableMarketConfigs) *PricefeedMutableMarketConfigs { +}) *PricefeedMutableMarketConfigs { mock := &PricefeedMutableMarketConfigs{} mock.Mock.Test(t) diff --git a/protocol/mocks/PriceFetcher.go b/protocol/mocks/PriceFetcher.go index c2e798e796..c7daf26fec 100644 --- a/protocol/mocks/PriceFetcher.go +++ b/protocol/mocks/PriceFetcher.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.40.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks diff --git a/protocol/mocks/PriceUpdateGenerator.go b/protocol/mocks/PriceUpdateGenerator.go new file mode 100644 index 0000000000..3c9affd933 --- /dev/null +++ b/protocol/mocks/PriceUpdateGenerator.go @@ -0,0 +1,60 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + mock "github.com/stretchr/testify/mock" + + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" + + types "github.com/cosmos/cosmos-sdk/types" +) + +// PriceUpdateGenerator is an autogenerated mock type for the PriceUpdateGenerator type +type PriceUpdateGenerator struct { + mock.Mock +} + +// GetValidMarketPriceUpdates provides a mock function with given fields: ctx, extCommitBz +func (_m *PriceUpdateGenerator) GetValidMarketPriceUpdates(ctx types.Context, extCommitBz []byte) (*pricestypes.MsgUpdateMarketPrices, error) { + ret := _m.Called(ctx, extCommitBz) + + if len(ret) == 0 { + panic("no return value specified for GetValidMarketPriceUpdates") + } + + var r0 *pricestypes.MsgUpdateMarketPrices + var r1 error + if rf, ok := ret.Get(0).(func(types.Context, []byte) (*pricestypes.MsgUpdateMarketPrices, error)); ok { + return rf(ctx, extCommitBz) + } + if rf, ok := ret.Get(0).(func(types.Context, []byte) *pricestypes.MsgUpdateMarketPrices); ok { + r0 = rf(ctx, extCommitBz) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pricestypes.MsgUpdateMarketPrices) + } + } + + if rf, ok := ret.Get(1).(func(types.Context, []byte) error); ok { + r1 = rf(ctx, extCommitBz) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewPriceUpdateGenerator creates a new instance of PriceUpdateGenerator. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPriceUpdateGenerator(t interface { + mock.TestingT + Cleanup(func()) +}) *PriceUpdateGenerator { + mock := &PriceUpdateGenerator{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/protocol/mocks/PricesKeeper.go b/protocol/mocks/PricesKeeper.go index 845727b6c6..0fef8f39bb 100644 --- a/protocol/mocks/PricesKeeper.go +++ b/protocol/mocks/PricesKeeper.go @@ -1,12 +1,16 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks import ( log "cosmossdk.io/log" - pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" + oracletypes "github.com/skip-mev/slinky/x/oracle/types" mock "github.com/stretchr/testify/mock" + pkgtypes "github.com/skip-mev/slinky/pkg/types" + + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" + types "github.com/cosmos/cosmos-sdk/types" ) @@ -19,6 +23,10 @@ type PricesKeeper struct { func (_m *PricesKeeper) CreateMarket(ctx types.Context, param pricestypes.MarketParam, price pricestypes.MarketPrice) (pricestypes.MarketParam, error) { ret := _m.Called(ctx, param, price) + if len(ret) == 0 { + panic("no return value specified for CreateMarket") + } + var r0 pricestypes.MarketParam var r1 error if rf, ok := ret.Get(0).(func(types.Context, pricestypes.MarketParam, pricestypes.MarketPrice) (pricestypes.MarketParam, error)); ok { @@ -43,6 +51,10 @@ func (_m *PricesKeeper) CreateMarket(ctx types.Context, param pricestypes.Market func (_m *PricesKeeper) GetAllMarketParamPrices(ctx types.Context) ([]pricestypes.MarketParamPrice, error) { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAllMarketParamPrices") + } + var r0 []pricestypes.MarketParamPrice var r1 error if rf, ok := ret.Get(0).(func(types.Context) ([]pricestypes.MarketParamPrice, error)); ok { @@ -69,6 +81,10 @@ func (_m *PricesKeeper) GetAllMarketParamPrices(ctx types.Context) ([]pricestype func (_m *PricesKeeper) GetAllMarketParams(ctx types.Context) []pricestypes.MarketParam { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAllMarketParams") + } + var r0 []pricestypes.MarketParam if rf, ok := ret.Get(0).(func(types.Context) []pricestypes.MarketParam); ok { r0 = rf(ctx) @@ -85,6 +101,10 @@ func (_m *PricesKeeper) GetAllMarketParams(ctx types.Context) []pricestypes.Mark func (_m *PricesKeeper) GetAllMarketPrices(ctx types.Context) []pricestypes.MarketPrice { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAllMarketPrices") + } + var r0 []pricestypes.MarketPrice if rf, ok := ret.Get(0).(func(types.Context) []pricestypes.MarketPrice); ok { r0 = rf(ctx) @@ -97,10 +117,70 @@ func (_m *PricesKeeper) GetAllMarketPrices(ctx types.Context) []pricestypes.Mark return r0 } +// GetCurrencyPairFromID provides a mock function with given fields: ctx, id +func (_m *PricesKeeper) GetCurrencyPairFromID(ctx types.Context, id uint64) (pkgtypes.CurrencyPair, bool) { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for GetCurrencyPairFromID") + } + + var r0 pkgtypes.CurrencyPair + var r1 bool + if rf, ok := ret.Get(0).(func(types.Context, uint64) (pkgtypes.CurrencyPair, bool)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(types.Context, uint64) pkgtypes.CurrencyPair); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Get(0).(pkgtypes.CurrencyPair) + } + + if rf, ok := ret.Get(1).(func(types.Context, uint64) bool); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + +// GetIDForCurrencyPair provides a mock function with given fields: ctx, cp +func (_m *PricesKeeper) GetIDForCurrencyPair(ctx types.Context, cp pkgtypes.CurrencyPair) (uint64, bool) { + ret := _m.Called(ctx, cp) + + if len(ret) == 0 { + panic("no return value specified for GetIDForCurrencyPair") + } + + var r0 uint64 + var r1 bool + if rf, ok := ret.Get(0).(func(types.Context, pkgtypes.CurrencyPair) (uint64, bool)); ok { + return rf(ctx, cp) + } + if rf, ok := ret.Get(0).(func(types.Context, pkgtypes.CurrencyPair) uint64); ok { + r0 = rf(ctx, cp) + } else { + r0 = ret.Get(0).(uint64) + } + + if rf, ok := ret.Get(1).(func(types.Context, pkgtypes.CurrencyPair) bool); ok { + r1 = rf(ctx, cp) + } else { + r1 = ret.Get(1).(bool) + } + + return r0, r1 +} + // GetMarketIdToValidIndexPrice provides a mock function with given fields: ctx func (_m *PricesKeeper) GetMarketIdToValidIndexPrice(ctx types.Context) map[uint32]pricestypes.MarketPrice { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetMarketIdToValidIndexPrice") + } + var r0 map[uint32]pricestypes.MarketPrice if rf, ok := ret.Get(0).(func(types.Context) map[uint32]pricestypes.MarketPrice); ok { r0 = rf(ctx) @@ -117,6 +197,10 @@ func (_m *PricesKeeper) GetMarketIdToValidIndexPrice(ctx types.Context) map[uint func (_m *PricesKeeper) GetMarketParam(ctx types.Context, id uint32) (pricestypes.MarketParam, bool) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetMarketParam") + } + var r0 pricestypes.MarketParam var r1 bool if rf, ok := ret.Get(0).(func(types.Context, uint32) (pricestypes.MarketParam, bool)); ok { @@ -141,6 +225,10 @@ func (_m *PricesKeeper) GetMarketParam(ctx types.Context, id uint32) (pricestype func (_m *PricesKeeper) GetMarketPrice(ctx types.Context, id uint32) (pricestypes.MarketPrice, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetMarketPrice") + } + var r0 pricestypes.MarketPrice var r1 error if rf, ok := ret.Get(0).(func(types.Context, uint32) (pricestypes.MarketPrice, error)); ok { @@ -161,10 +249,62 @@ func (_m *PricesKeeper) GetMarketPrice(ctx types.Context, id uint32) (pricestype return r0, r1 } +// GetPriceForCurrencyPair provides a mock function with given fields: ctx, cp +func (_m *PricesKeeper) GetPriceForCurrencyPair(ctx types.Context, cp pkgtypes.CurrencyPair) (oracletypes.QuotePrice, error) { + ret := _m.Called(ctx, cp) + + if len(ret) == 0 { + panic("no return value specified for GetPriceForCurrencyPair") + } + + var r0 oracletypes.QuotePrice + var r1 error + if rf, ok := ret.Get(0).(func(types.Context, pkgtypes.CurrencyPair) (oracletypes.QuotePrice, error)); ok { + return rf(ctx, cp) + } + if rf, ok := ret.Get(0).(func(types.Context, pkgtypes.CurrencyPair) oracletypes.QuotePrice); ok { + r0 = rf(ctx, cp) + } else { + r0 = ret.Get(0).(oracletypes.QuotePrice) + } + + if rf, ok := ret.Get(1).(func(types.Context, pkgtypes.CurrencyPair) error); ok { + r1 = rf(ctx, cp) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetValidMarketPriceUpdates provides a mock function with given fields: ctx +func (_m *PricesKeeper) GetValidMarketPriceUpdates(ctx types.Context) *pricestypes.MsgUpdateMarketPrices { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetValidMarketPriceUpdates") + } + + var r0 *pricestypes.MsgUpdateMarketPrices + if rf, ok := ret.Get(0).(func(types.Context) *pricestypes.MsgUpdateMarketPrices); ok { + r0 = rf(ctx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*pricestypes.MsgUpdateMarketPrices) + } + } + + return r0 +} + // HasAuthority provides a mock function with given fields: authority func (_m *PricesKeeper) HasAuthority(authority string) bool { ret := _m.Called(authority) + if len(ret) == 0 { + panic("no return value specified for HasAuthority") + } + var r0 bool if rf, ok := ret.Get(0).(func(string) bool); ok { r0 = rf(authority) @@ -179,6 +319,10 @@ func (_m *PricesKeeper) HasAuthority(authority string) bool { func (_m *PricesKeeper) Logger(ctx types.Context) log.Logger { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for Logger") + } + var r0 log.Logger if rf, ok := ret.Get(0).(func(types.Context) log.Logger); ok { r0 = rf(ctx) @@ -195,6 +339,10 @@ func (_m *PricesKeeper) Logger(ctx types.Context) log.Logger { func (_m *PricesKeeper) ModifyMarketParam(ctx types.Context, param pricestypes.MarketParam) (pricestypes.MarketParam, error) { ret := _m.Called(ctx, param) + if len(ret) == 0 { + panic("no return value specified for ModifyMarketParam") + } + var r0 pricestypes.MarketParam var r1 error if rf, ok := ret.Get(0).(func(types.Context, pricestypes.MarketParam) (pricestypes.MarketParam, error)); ok { @@ -219,6 +367,10 @@ func (_m *PricesKeeper) ModifyMarketParam(ctx types.Context, param pricestypes.M func (_m *PricesKeeper) PerformStatefulPriceUpdateValidation(ctx types.Context, marketPriceUpdates *pricestypes.MsgUpdateMarketPrices, performNonDeterministicValidation bool) error { ret := _m.Called(ctx, marketPriceUpdates, performNonDeterministicValidation) + if len(ret) == 0 { + panic("no return value specified for PerformStatefulPriceUpdateValidation") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, *pricestypes.MsgUpdateMarketPrices, bool) error); ok { r0 = rf(ctx, marketPriceUpdates, performNonDeterministicValidation) @@ -233,23 +385,13 @@ func (_m *PricesKeeper) PerformStatefulPriceUpdateValidation(ctx types.Context, func (_m *PricesKeeper) UpdateMarketPrices(ctx types.Context, updates []*pricestypes.MsgUpdateMarketPrices_MarketPrice) error { ret := _m.Called(ctx, updates) - var r0 error - if rf, ok := ret.Get(0).(func(types.Context, []*pricestypes.MsgUpdateMarketPrices_MarketPrice) error); ok { - r0 = rf(ctx, updates) - } else { - r0 = ret.Error(0) + if len(ret) == 0 { + panic("no return value specified for UpdateMarketPrices") } - return r0 -} - -// UpdateSmoothedPrices provides a mock function with given fields: ctx, linearInterpolateFunc -func (_m *PricesKeeper) UpdateSmoothedPrices(ctx types.Context, linearInterpolateFunc func(uint64, uint64, uint32) (uint64, error)) error { - ret := _m.Called(ctx, linearInterpolateFunc) - var r0 error - if rf, ok := ret.Get(0).(func(types.Context, func(uint64, uint64, uint32) (uint64, error)) error); ok { - r0 = rf(ctx, linearInterpolateFunc) + if rf, ok := ret.Get(0).(func(types.Context, []*pricestypes.MsgUpdateMarketPrices_MarketPrice) error); ok { + r0 = rf(ctx, updates) } else { r0 = ret.Error(0) } @@ -257,13 +399,12 @@ func (_m *PricesKeeper) UpdateSmoothedPrices(ctx types.Context, linearInterpolat return r0 } -type mockConstructorTestingTNewPricesKeeper interface { +// NewPricesKeeper creates a new instance of PricesKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewPricesKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewPricesKeeper creates a new instance of PricesKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewPricesKeeper(t mockConstructorTestingTNewPricesKeeper) *PricesKeeper { +}) *PricesKeeper { mock := &PricesKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/ProcessBridgeKeeper.go b/protocol/mocks/ProcessBridgeKeeper.go index 96c863b5e7..66e2c965ad 100644 --- a/protocol/mocks/ProcessBridgeKeeper.go +++ b/protocol/mocks/ProcessBridgeKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type ProcessBridgeKeeper struct { func (_m *ProcessBridgeKeeper) GetAcknowledgedEventInfo(ctx types.Context) bridgetypes.BridgeEventInfo { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAcknowledgedEventInfo") + } + var r0 bridgetypes.BridgeEventInfo if rf, ok := ret.Get(0).(func(types.Context) bridgetypes.BridgeEventInfo); ok { r0 = rf(ctx) @@ -32,6 +36,10 @@ func (_m *ProcessBridgeKeeper) GetAcknowledgedEventInfo(ctx types.Context) bridg func (_m *ProcessBridgeKeeper) GetBridgeEventFromServer(ctx types.Context, id uint32) (bridgetypes.BridgeEvent, bool) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetBridgeEventFromServer") + } + var r0 bridgetypes.BridgeEvent var r1 bool if rf, ok := ret.Get(0).(func(types.Context, uint32) (bridgetypes.BridgeEvent, bool)); ok { @@ -56,6 +64,10 @@ func (_m *ProcessBridgeKeeper) GetBridgeEventFromServer(ctx types.Context, id ui func (_m *ProcessBridgeKeeper) GetRecognizedEventInfo(ctx types.Context) bridgetypes.BridgeEventInfo { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetRecognizedEventInfo") + } + var r0 bridgetypes.BridgeEventInfo if rf, ok := ret.Get(0).(func(types.Context) bridgetypes.BridgeEventInfo); ok { r0 = rf(ctx) @@ -70,6 +82,10 @@ func (_m *ProcessBridgeKeeper) GetRecognizedEventInfo(ctx types.Context) bridget func (_m *ProcessBridgeKeeper) GetSafetyParams(ctx types.Context) bridgetypes.SafetyParams { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetSafetyParams") + } + var r0 bridgetypes.SafetyParams if rf, ok := ret.Get(0).(func(types.Context) bridgetypes.SafetyParams); ok { r0 = rf(ctx) @@ -80,13 +96,12 @@ func (_m *ProcessBridgeKeeper) GetSafetyParams(ctx types.Context) bridgetypes.Sa return r0 } -type mockConstructorTestingTNewProcessBridgeKeeper interface { +// NewProcessBridgeKeeper creates a new instance of ProcessBridgeKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewProcessBridgeKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewProcessBridgeKeeper creates a new instance of ProcessBridgeKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewProcessBridgeKeeper(t mockConstructorTestingTNewProcessBridgeKeeper) *ProcessBridgeKeeper { +}) *ProcessBridgeKeeper { mock := &ProcessBridgeKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/ProcessClobKeeper.go b/protocol/mocks/ProcessClobKeeper.go index 4963b1d98d..38b975a922 100644 --- a/protocol/mocks/ProcessClobKeeper.go +++ b/protocol/mocks/ProcessClobKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -25,6 +25,10 @@ func (_m *ProcessClobKeeper) RecordMevMetrics(ctx types.Context, stakingKeeper p func (_m *ProcessClobKeeper) RecordMevMetricsIsEnabled() bool { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for RecordMevMetricsIsEnabled") + } + var r0 bool if rf, ok := ret.Get(0).(func() bool); ok { r0 = rf() @@ -35,13 +39,12 @@ func (_m *ProcessClobKeeper) RecordMevMetricsIsEnabled() bool { return r0 } -type mockConstructorTestingTNewProcessClobKeeper interface { +// NewProcessClobKeeper creates a new instance of ProcessClobKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewProcessClobKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewProcessClobKeeper creates a new instance of ProcessClobKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewProcessClobKeeper(t mockConstructorTestingTNewProcessClobKeeper) *ProcessClobKeeper { +}) *ProcessClobKeeper { mock := &ProcessClobKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/ProcessPerpetualKeeper.go b/protocol/mocks/ProcessPerpetualKeeper.go index 86519b524d..2475fcd7f1 100644 --- a/protocol/mocks/ProcessPerpetualKeeper.go +++ b/protocol/mocks/ProcessPerpetualKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -20,6 +20,10 @@ type ProcessPerpetualKeeper struct { func (_m *ProcessPerpetualKeeper) GetPerpetual(ctx types.Context, id uint32) (perpetualstypes.Perpetual, error) { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetPerpetual") + } + var r0 perpetualstypes.Perpetual var r1 error if rf, ok := ret.Get(0).(func(types.Context, uint32) (perpetualstypes.Perpetual, error)); ok { @@ -44,6 +48,10 @@ func (_m *ProcessPerpetualKeeper) GetPerpetual(ctx types.Context, id uint32) (pe func (_m *ProcessPerpetualKeeper) GetSettlementPpm(ctx types.Context, perpetualId uint32, quantums *big.Int, index *big.Int) (*big.Int, *big.Int, error) { ret := _m.Called(ctx, perpetualId, quantums, index) + if len(ret) == 0 { + panic("no return value specified for GetSettlementPpm") + } + var r0 *big.Int var r1 *big.Int var r2 error @@ -80,13 +88,12 @@ func (_m *ProcessPerpetualKeeper) MaybeProcessNewFundingTickEpoch(ctx types.Cont _m.Called(ctx) } -type mockConstructorTestingTNewProcessPerpetualKeeper interface { +// NewProcessPerpetualKeeper creates a new instance of ProcessPerpetualKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewProcessPerpetualKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewProcessPerpetualKeeper creates a new instance of ProcessPerpetualKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewProcessPerpetualKeeper(t mockConstructorTestingTNewProcessPerpetualKeeper) *ProcessPerpetualKeeper { +}) *ProcessPerpetualKeeper { mock := &ProcessPerpetualKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/ProcessStakingKeeper.go b/protocol/mocks/ProcessStakingKeeper.go index c9c6a7731a..bf843ed8b8 100644 --- a/protocol/mocks/ProcessStakingKeeper.go +++ b/protocol/mocks/ProcessStakingKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -21,6 +21,10 @@ type ProcessStakingKeeper struct { func (_m *ProcessStakingKeeper) GetValidatorByConsAddr(ctx context.Context, consAddr types.ConsAddress) (stakingtypes.Validator, error) { ret := _m.Called(ctx, consAddr) + if len(ret) == 0 { + panic("no return value specified for GetValidatorByConsAddr") + } + var r0 stakingtypes.Validator var r1 error if rf, ok := ret.Get(0).(func(context.Context, types.ConsAddress) (stakingtypes.Validator, error)); ok { @@ -41,13 +45,12 @@ func (_m *ProcessStakingKeeper) GetValidatorByConsAddr(ctx context.Context, cons return r0, r1 } -type mockConstructorTestingTNewProcessStakingKeeper interface { +// NewProcessStakingKeeper creates a new instance of ProcessStakingKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewProcessStakingKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewProcessStakingKeeper creates a new instance of ProcessStakingKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewProcessStakingKeeper(t mockConstructorTestingTNewProcessStakingKeeper) *ProcessStakingKeeper { +}) *ProcessStakingKeeper { mock := &ProcessStakingKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/QueryClient.go b/protocol/mocks/QueryClient.go index bd9c3832e8..e04a613c33 100644 --- a/protocol/mocks/QueryClient.go +++ b/protocol/mocks/QueryClient.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -41,6 +41,10 @@ func (_m *QueryClient) AddBridgeEvents(ctx context.Context, in *api.AddBridgeEve _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for AddBridgeEvents") + } + var r0 *api.AddBridgeEventsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *api.AddBridgeEventsRequest, ...grpc.CallOption) (*api.AddBridgeEventsResponse, error)); ok { @@ -74,6 +78,10 @@ func (_m *QueryClient) AllDowntimeInfo(ctx context.Context, in *types.QueryAllDo _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for AllDowntimeInfo") + } + var r0 *types.QueryAllDowntimeInfoResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryAllDowntimeInfoRequest, ...grpc.CallOption) (*types.QueryAllDowntimeInfoResponse, error)); ok { @@ -107,6 +115,10 @@ func (_m *QueryClient) AllLiquidityTiers(ctx context.Context, in *perpetualstype _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for AllLiquidityTiers") + } + var r0 *perpetualstypes.QueryAllLiquidityTiersResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *perpetualstypes.QueryAllLiquidityTiersRequest, ...grpc.CallOption) (*perpetualstypes.QueryAllLiquidityTiersResponse, error)); ok { @@ -140,6 +152,10 @@ func (_m *QueryClient) AllMarketParams(ctx context.Context, in *pricestypes.Quer _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for AllMarketParams") + } + var r0 *pricestypes.QueryAllMarketParamsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *pricestypes.QueryAllMarketParamsRequest, ...grpc.CallOption) (*pricestypes.QueryAllMarketParamsResponse, error)); ok { @@ -173,6 +189,10 @@ func (_m *QueryClient) AllMarketPrices(ctx context.Context, in *pricestypes.Quer _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for AllMarketPrices") + } + var r0 *pricestypes.QueryAllMarketPricesResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *pricestypes.QueryAllMarketPricesRequest, ...grpc.CallOption) (*pricestypes.QueryAllMarketPricesResponse, error)); ok { @@ -206,6 +226,10 @@ func (_m *QueryClient) AllPerpetuals(ctx context.Context, in *perpetualstypes.Qu _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for AllPerpetuals") + } + var r0 *perpetualstypes.QueryAllPerpetualsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *perpetualstypes.QueryAllPerpetualsRequest, ...grpc.CallOption) (*perpetualstypes.QueryAllPerpetualsResponse, error)); ok { @@ -239,6 +263,10 @@ func (_m *QueryClient) BlockRateLimitConfiguration(ctx context.Context, in *clob _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for BlockRateLimitConfiguration") + } + var r0 *clobtypes.QueryBlockRateLimitConfigurationResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *clobtypes.QueryBlockRateLimitConfigurationRequest, ...grpc.CallOption) (*clobtypes.QueryBlockRateLimitConfigurationResponse, error)); ok { @@ -272,6 +300,10 @@ func (_m *QueryClient) ClobPair(ctx context.Context, in *clobtypes.QueryGetClobP _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for ClobPair") + } + var r0 *clobtypes.QueryClobPairResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *clobtypes.QueryGetClobPairRequest, ...grpc.CallOption) (*clobtypes.QueryClobPairResponse, error)); ok { @@ -305,6 +337,10 @@ func (_m *QueryClient) ClobPairAll(ctx context.Context, in *clobtypes.QueryAllCl _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for ClobPairAll") + } + var r0 *clobtypes.QueryClobPairAllResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *clobtypes.QueryAllClobPairRequest, ...grpc.CallOption) (*clobtypes.QueryClobPairAllResponse, error)); ok { @@ -338,6 +374,10 @@ func (_m *QueryClient) DowntimeParams(ctx context.Context, in *types.QueryDownti _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for DowntimeParams") + } + var r0 *types.QueryDowntimeParamsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryDowntimeParamsRequest, ...grpc.CallOption) (*types.QueryDowntimeParamsResponse, error)); ok { @@ -371,6 +411,10 @@ func (_m *QueryClient) EquityTierLimitConfiguration(ctx context.Context, in *clo _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for EquityTierLimitConfiguration") + } + var r0 *clobtypes.QueryEquityTierLimitConfigurationResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *clobtypes.QueryEquityTierLimitConfigurationRequest, ...grpc.CallOption) (*clobtypes.QueryEquityTierLimitConfigurationResponse, error)); ok { @@ -404,6 +448,10 @@ func (_m *QueryClient) GetWithdrawalAndTransfersBlockedInfo(ctx context.Context, _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for GetWithdrawalAndTransfersBlockedInfo") + } + var r0 *subaccountstypes.QueryGetWithdrawalAndTransfersBlockedInfoResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *subaccountstypes.QueryGetWithdrawalAndTransfersBlockedInfoRequest, ...grpc.CallOption) (*subaccountstypes.QueryGetWithdrawalAndTransfersBlockedInfoResponse, error)); ok { @@ -437,6 +485,10 @@ func (_m *QueryClient) LiquidateSubaccounts(ctx context.Context, in *liquidation _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for LiquidateSubaccounts") + } + var r0 *liquidationapi.LiquidateSubaccountsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *liquidationapi.LiquidateSubaccountsRequest, ...grpc.CallOption) (*liquidationapi.LiquidateSubaccountsResponse, error)); ok { @@ -470,6 +522,10 @@ func (_m *QueryClient) LiquidationsConfiguration(ctx context.Context, in *clobty _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for LiquidationsConfiguration") + } + var r0 *clobtypes.QueryLiquidationsConfigurationResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *clobtypes.QueryLiquidationsConfigurationRequest, ...grpc.CallOption) (*clobtypes.QueryLiquidationsConfigurationResponse, error)); ok { @@ -503,6 +559,10 @@ func (_m *QueryClient) MarketParam(ctx context.Context, in *pricestypes.QueryMar _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for MarketParam") + } + var r0 *pricestypes.QueryMarketParamResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *pricestypes.QueryMarketParamRequest, ...grpc.CallOption) (*pricestypes.QueryMarketParamResponse, error)); ok { @@ -536,6 +596,10 @@ func (_m *QueryClient) MarketPrice(ctx context.Context, in *pricestypes.QueryMar _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for MarketPrice") + } + var r0 *pricestypes.QueryMarketPriceResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *pricestypes.QueryMarketPriceRequest, ...grpc.CallOption) (*pricestypes.QueryMarketPriceResponse, error)); ok { @@ -569,6 +633,10 @@ func (_m *QueryClient) MevNodeToNodeCalculation(ctx context.Context, in *clobtyp _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for MevNodeToNodeCalculation") + } + var r0 *clobtypes.MevNodeToNodeCalculationResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *clobtypes.MevNodeToNodeCalculationRequest, ...grpc.CallOption) (*clobtypes.MevNodeToNodeCalculationResponse, error)); ok { @@ -602,6 +670,10 @@ func (_m *QueryClient) Params(ctx context.Context, in *perpetualstypes.QueryPara _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Params") + } + var r0 *perpetualstypes.QueryParamsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *perpetualstypes.QueryParamsRequest, ...grpc.CallOption) (*perpetualstypes.QueryParamsResponse, error)); ok { @@ -635,6 +707,10 @@ func (_m *QueryClient) Perpetual(ctx context.Context, in *perpetualstypes.QueryP _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Perpetual") + } + var r0 *perpetualstypes.QueryPerpetualResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *perpetualstypes.QueryPerpetualRequest, ...grpc.CallOption) (*perpetualstypes.QueryPerpetualResponse, error)); ok { @@ -668,6 +744,10 @@ func (_m *QueryClient) PremiumSamples(ctx context.Context, in *perpetualstypes.Q _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for PremiumSamples") + } + var r0 *perpetualstypes.QueryPremiumSamplesResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *perpetualstypes.QueryPremiumSamplesRequest, ...grpc.CallOption) (*perpetualstypes.QueryPremiumSamplesResponse, error)); ok { @@ -701,6 +781,10 @@ func (_m *QueryClient) PremiumVotes(ctx context.Context, in *perpetualstypes.Que _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for PremiumVotes") + } + var r0 *perpetualstypes.QueryPremiumVotesResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *perpetualstypes.QueryPremiumVotesRequest, ...grpc.CallOption) (*perpetualstypes.QueryPremiumVotesResponse, error)); ok { @@ -734,6 +818,10 @@ func (_m *QueryClient) PreviousBlockInfo(ctx context.Context, in *types.QueryPre _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for PreviousBlockInfo") + } + var r0 *types.QueryPreviousBlockInfoResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryPreviousBlockInfoRequest, ...grpc.CallOption) (*types.QueryPreviousBlockInfoResponse, error)); ok { @@ -767,6 +855,10 @@ func (_m *QueryClient) StreamOrderbookUpdates(ctx context.Context, in *clobtypes _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for StreamOrderbookUpdates") + } + var r0 clobtypes.Query_StreamOrderbookUpdatesClient var r1 error if rf, ok := ret.Get(0).(func(context.Context, *clobtypes.StreamOrderbookUpdatesRequest, ...grpc.CallOption) (clobtypes.Query_StreamOrderbookUpdatesClient, error)); ok { @@ -800,6 +892,10 @@ func (_m *QueryClient) Subaccount(ctx context.Context, in *subaccountstypes.Quer _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for Subaccount") + } + var r0 *subaccountstypes.QuerySubaccountResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *subaccountstypes.QueryGetSubaccountRequest, ...grpc.CallOption) (*subaccountstypes.QuerySubaccountResponse, error)); ok { @@ -833,6 +929,10 @@ func (_m *QueryClient) SubaccountAll(ctx context.Context, in *subaccountstypes.Q _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for SubaccountAll") + } + var r0 *subaccountstypes.QuerySubaccountAllResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *subaccountstypes.QueryAllSubaccountRequest, ...grpc.CallOption) (*subaccountstypes.QuerySubaccountAllResponse, error)); ok { @@ -866,6 +966,10 @@ func (_m *QueryClient) UpdateMarketPrices(ctx context.Context, in *pricefeedapi. _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for UpdateMarketPrices") + } + var r0 *pricefeedapi.UpdateMarketPricesResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *pricefeedapi.UpdateMarketPricesRequest, ...grpc.CallOption) (*pricefeedapi.UpdateMarketPricesResponse, error)); ok { @@ -888,13 +992,12 @@ func (_m *QueryClient) UpdateMarketPrices(ctx context.Context, in *pricefeedapi. return r0, r1 } -type mockConstructorTestingTNewQueryClient interface { +// NewQueryClient creates a new instance of QueryClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewQueryClient(t interface { mock.TestingT Cleanup(func()) -} - -// NewQueryClient creates a new instance of QueryClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewQueryClient(t mockConstructorTestingTNewQueryClient) *QueryClient { +}) *QueryClient { mock := &QueryClient{} mock.Mock.Test(t) diff --git a/protocol/mocks/QueryServer.go b/protocol/mocks/QueryServer.go index 8b96577693..86b213e773 100644 --- a/protocol/mocks/QueryServer.go +++ b/protocol/mocks/QueryServer.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type QueryServer struct { func (_m *QueryServer) AllMarketParams(_a0 context.Context, _a1 *types.QueryAllMarketParamsRequest) (*types.QueryAllMarketParamsResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for AllMarketParams") + } + var r0 *types.QueryAllMarketParamsResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryAllMarketParamsRequest) (*types.QueryAllMarketParamsResponse, error)); ok { @@ -45,6 +49,10 @@ func (_m *QueryServer) AllMarketParams(_a0 context.Context, _a1 *types.QueryAllM func (_m *QueryServer) AllMarketPrices(_a0 context.Context, _a1 *types.QueryAllMarketPricesRequest) (*types.QueryAllMarketPricesResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for AllMarketPrices") + } + var r0 *types.QueryAllMarketPricesResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryAllMarketPricesRequest) (*types.QueryAllMarketPricesResponse, error)); ok { @@ -71,6 +79,10 @@ func (_m *QueryServer) AllMarketPrices(_a0 context.Context, _a1 *types.QueryAllM func (_m *QueryServer) MarketParam(_a0 context.Context, _a1 *types.QueryMarketParamRequest) (*types.QueryMarketParamResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for MarketParam") + } + var r0 *types.QueryMarketParamResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryMarketParamRequest) (*types.QueryMarketParamResponse, error)); ok { @@ -97,6 +109,10 @@ func (_m *QueryServer) MarketParam(_a0 context.Context, _a1 *types.QueryMarketPa func (_m *QueryServer) MarketPrice(_a0 context.Context, _a1 *types.QueryMarketPriceRequest) (*types.QueryMarketPriceResponse, error) { ret := _m.Called(_a0, _a1) + if len(ret) == 0 { + panic("no return value specified for MarketPrice") + } + var r0 *types.QueryMarketPriceResponse var r1 error if rf, ok := ret.Get(0).(func(context.Context, *types.QueryMarketPriceRequest) (*types.QueryMarketPriceResponse, error)); ok { @@ -119,13 +135,12 @@ func (_m *QueryServer) MarketPrice(_a0 context.Context, _a1 *types.QueryMarketPr return r0, r1 } -type mockConstructorTestingTNewQueryServer interface { +// NewQueryServer creates a new instance of QueryServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewQueryServer(t interface { mock.TestingT Cleanup(func()) -} - -// NewQueryServer creates a new instance of QueryServer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewQueryServer(t mockConstructorTestingTNewQueryServer) *QueryServer { +}) *QueryServer { mock := &QueryServer{} mock.Mock.Test(t) diff --git a/protocol/mocks/RequestHandler.go b/protocol/mocks/RequestHandler.go index e1bc92e110..c642a4c8b2 100644 --- a/protocol/mocks/RequestHandler.go +++ b/protocol/mocks/RequestHandler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type RequestHandler struct { func (_m *RequestHandler) Get(ctx context.Context, url string) (*http.Response, error) { ret := _m.Called(ctx, url) + if len(ret) == 0 { + panic("no return value specified for Get") + } + var r0 *http.Response var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*http.Response, error)); ok { @@ -40,13 +44,12 @@ func (_m *RequestHandler) Get(ctx context.Context, url string) (*http.Response, return r0, r1 } -type mockConstructorTestingTNewRequestHandler interface { +// NewRequestHandler creates a new instance of RequestHandler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewRequestHandler(t interface { mock.TestingT Cleanup(func()) -} - -// NewRequestHandler creates a new instance of RequestHandler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewRequestHandler(t mockConstructorTestingTNewRequestHandler) *RequestHandler { +}) *RequestHandler { mock := &RequestHandler{} mock.Mock.Test(t) diff --git a/protocol/mocks/SendingKeeper.go b/protocol/mocks/SendingKeeper.go index 4f730c813b..901e67255b 100644 --- a/protocol/mocks/SendingKeeper.go +++ b/protocol/mocks/SendingKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -18,6 +18,10 @@ type SendingKeeper struct { func (_m *SendingKeeper) HasAuthority(authority string) bool { ret := _m.Called(authority) + if len(ret) == 0 { + panic("no return value specified for HasAuthority") + } + var r0 bool if rf, ok := ret.Get(0).(func(string) bool); ok { r0 = rf(authority) @@ -32,6 +36,10 @@ func (_m *SendingKeeper) HasAuthority(authority string) bool { func (_m *SendingKeeper) ProcessDepositToSubaccount(ctx cosmos_sdktypes.Context, msgDepositToSubaccount *types.MsgDepositToSubaccount) error { ret := _m.Called(ctx, msgDepositToSubaccount) + if len(ret) == 0 { + panic("no return value specified for ProcessDepositToSubaccount") + } + var r0 error if rf, ok := ret.Get(0).(func(cosmos_sdktypes.Context, *types.MsgDepositToSubaccount) error); ok { r0 = rf(ctx, msgDepositToSubaccount) @@ -46,6 +54,10 @@ func (_m *SendingKeeper) ProcessDepositToSubaccount(ctx cosmos_sdktypes.Context, func (_m *SendingKeeper) ProcessTransfer(ctx cosmos_sdktypes.Context, transfer *types.Transfer) error { ret := _m.Called(ctx, transfer) + if len(ret) == 0 { + panic("no return value specified for ProcessTransfer") + } + var r0 error if rf, ok := ret.Get(0).(func(cosmos_sdktypes.Context, *types.Transfer) error); ok { r0 = rf(ctx, transfer) @@ -60,6 +72,10 @@ func (_m *SendingKeeper) ProcessTransfer(ctx cosmos_sdktypes.Context, transfer * func (_m *SendingKeeper) ProcessWithdrawFromSubaccount(ctx cosmos_sdktypes.Context, msgWithdrawFromSubaccount *types.MsgWithdrawFromSubaccount) error { ret := _m.Called(ctx, msgWithdrawFromSubaccount) + if len(ret) == 0 { + panic("no return value specified for ProcessWithdrawFromSubaccount") + } + var r0 error if rf, ok := ret.Get(0).(func(cosmos_sdktypes.Context, *types.MsgWithdrawFromSubaccount) error); ok { r0 = rf(ctx, msgWithdrawFromSubaccount) @@ -74,6 +90,10 @@ func (_m *SendingKeeper) ProcessWithdrawFromSubaccount(ctx cosmos_sdktypes.Conte func (_m *SendingKeeper) SendFromModuleToAccount(ctx cosmos_sdktypes.Context, msg *types.MsgSendFromModuleToAccount) error { ret := _m.Called(ctx, msg) + if len(ret) == 0 { + panic("no return value specified for SendFromModuleToAccount") + } + var r0 error if rf, ok := ret.Get(0).(func(cosmos_sdktypes.Context, *types.MsgSendFromModuleToAccount) error); ok { r0 = rf(ctx, msg) @@ -84,13 +104,12 @@ func (_m *SendingKeeper) SendFromModuleToAccount(ctx cosmos_sdktypes.Context, ms return r0 } -type mockConstructorTestingTNewSendingKeeper interface { +// NewSendingKeeper creates a new instance of SendingKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSendingKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewSendingKeeper creates a new instance of SendingKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSendingKeeper(t mockConstructorTestingTNewSendingKeeper) *SendingKeeper { +}) *SendingKeeper { mock := &SendingKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/Server.go b/protocol/mocks/Server.go index cb77088039..320241360f 100644 --- a/protocol/mocks/Server.go +++ b/protocol/mocks/Server.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -17,13 +17,12 @@ func (_m *Server) RegisterService(sd *grpc.ServiceDesc, ss interface{}) { _m.Called(sd, ss) } -type mockConstructorTestingTNewServer interface { +// NewServer creates a new instance of Server. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewServer(t interface { mock.TestingT Cleanup(func()) -} - -// NewServer creates a new instance of Server. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewServer(t mockConstructorTestingTNewServer) *Server { +}) *Server { mock := &Server{} mock.Mock.Test(t) diff --git a/protocol/mocks/SubaccountsKeeper.go b/protocol/mocks/SubaccountsKeeper.go index 7306d8113b..4e869a5641 100644 --- a/protocol/mocks/SubaccountsKeeper.go +++ b/protocol/mocks/SubaccountsKeeper.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -22,6 +22,10 @@ type SubaccountsKeeper struct { func (_m *SubaccountsKeeper) CanUpdateSubaccounts(ctx types.Context, updates []subaccountstypes.Update, updateType subaccountstypes.UpdateType) (bool, []subaccountstypes.UpdateResult, error) { ret := _m.Called(ctx, updates, updateType) + if len(ret) == 0 { + panic("no return value specified for CanUpdateSubaccounts") + } + var r0 bool var r1 []subaccountstypes.UpdateResult var r2 error @@ -55,6 +59,10 @@ func (_m *SubaccountsKeeper) CanUpdateSubaccounts(ctx types.Context, updates []s func (_m *SubaccountsKeeper) DepositFundsFromAccountToSubaccount(ctx types.Context, fromAccount types.AccAddress, toSubaccountId subaccountstypes.SubaccountId, assetId uint32, amount *big.Int) error { ret := _m.Called(ctx, fromAccount, toSubaccountId, assetId, amount) + if len(ret) == 0 { + panic("no return value specified for DepositFundsFromAccountToSubaccount") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, types.AccAddress, subaccountstypes.SubaccountId, uint32, *big.Int) error); ok { r0 = rf(ctx, fromAccount, toSubaccountId, assetId, amount) @@ -69,6 +77,10 @@ func (_m *SubaccountsKeeper) DepositFundsFromAccountToSubaccount(ctx types.Conte func (_m *SubaccountsKeeper) GetAllSubaccount(ctx types.Context) []subaccountstypes.Subaccount { ret := _m.Called(ctx) + if len(ret) == 0 { + panic("no return value specified for GetAllSubaccount") + } + var r0 []subaccountstypes.Subaccount if rf, ok := ret.Get(0).(func(types.Context) []subaccountstypes.Subaccount); ok { r0 = rf(ctx) @@ -85,6 +97,10 @@ func (_m *SubaccountsKeeper) GetAllSubaccount(ctx types.Context) []subaccountsty func (_m *SubaccountsKeeper) GetNetCollateralAndMarginRequirements(ctx types.Context, update subaccountstypes.Update) (*big.Int, *big.Int, *big.Int, error) { ret := _m.Called(ctx, update) + if len(ret) == 0 { + panic("no return value specified for GetNetCollateralAndMarginRequirements") + } + var r0 *big.Int var r1 *big.Int var r2 *big.Int @@ -129,6 +145,10 @@ func (_m *SubaccountsKeeper) GetNetCollateralAndMarginRequirements(ctx types.Con func (_m *SubaccountsKeeper) GetRandomSubaccount(ctx types.Context, _a1 *rand.Rand) (subaccountstypes.Subaccount, error) { ret := _m.Called(ctx, _a1) + if len(ret) == 0 { + panic("no return value specified for GetRandomSubaccount") + } + var r0 subaccountstypes.Subaccount var r1 error if rf, ok := ret.Get(0).(func(types.Context, *rand.Rand) (subaccountstypes.Subaccount, error)); ok { @@ -153,6 +173,10 @@ func (_m *SubaccountsKeeper) GetRandomSubaccount(ctx types.Context, _a1 *rand.Ra func (_m *SubaccountsKeeper) GetSubaccount(ctx types.Context, id subaccountstypes.SubaccountId) subaccountstypes.Subaccount { ret := _m.Called(ctx, id) + if len(ret) == 0 { + panic("no return value specified for GetSubaccount") + } + var r0 subaccountstypes.Subaccount if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId) subaccountstypes.Subaccount); ok { r0 = rf(ctx, id) @@ -168,10 +192,32 @@ func (_m *SubaccountsKeeper) SetSubaccount(ctx types.Context, subaccount subacco _m.Called(ctx, subaccount) } +// TransferFundsFromSubaccountToSubaccount provides a mock function with given fields: ctx, senderSubaccountId, recipientSubaccountId, assetId, quantums +func (_m *SubaccountsKeeper) TransferFundsFromSubaccountToSubaccount(ctx types.Context, senderSubaccountId subaccountstypes.SubaccountId, recipientSubaccountId subaccountstypes.SubaccountId, assetId uint32, quantums *big.Int) error { + ret := _m.Called(ctx, senderSubaccountId, recipientSubaccountId, assetId, quantums) + + if len(ret) == 0 { + panic("no return value specified for TransferFundsFromSubaccountToSubaccount") + } + + var r0 error + if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId, subaccountstypes.SubaccountId, uint32, *big.Int) error); ok { + r0 = rf(ctx, senderSubaccountId, recipientSubaccountId, assetId, quantums) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // UpdateSubaccounts provides a mock function with given fields: ctx, updates, updateType func (_m *SubaccountsKeeper) UpdateSubaccounts(ctx types.Context, updates []subaccountstypes.Update, updateType subaccountstypes.UpdateType) (bool, []subaccountstypes.UpdateResult, error) { ret := _m.Called(ctx, updates, updateType) + if len(ret) == 0 { + panic("no return value specified for UpdateSubaccounts") + } + var r0 bool var r1 []subaccountstypes.UpdateResult var r2 error @@ -205,6 +251,10 @@ func (_m *SubaccountsKeeper) UpdateSubaccounts(ctx types.Context, updates []suba func (_m *SubaccountsKeeper) WithdrawFundsFromSubaccountToAccount(ctx types.Context, fromSubaccountId subaccountstypes.SubaccountId, toAccount types.AccAddress, assetId uint32, amount *big.Int) error { ret := _m.Called(ctx, fromSubaccountId, toAccount, assetId, amount) + if len(ret) == 0 { + panic("no return value specified for WithdrawFundsFromSubaccountToAccount") + } + var r0 error if rf, ok := ret.Get(0).(func(types.Context, subaccountstypes.SubaccountId, types.AccAddress, uint32, *big.Int) error); ok { r0 = rf(ctx, fromSubaccountId, toAccount, assetId, amount) @@ -215,13 +265,12 @@ func (_m *SubaccountsKeeper) WithdrawFundsFromSubaccountToAccount(ctx types.Cont return r0 } -type mockConstructorTestingTNewSubaccountsKeeper interface { +// NewSubaccountsKeeper creates a new instance of SubaccountsKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewSubaccountsKeeper(t interface { mock.TestingT Cleanup(func()) -} - -// NewSubaccountsKeeper creates a new instance of SubaccountsKeeper. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewSubaccountsKeeper(t mockConstructorTestingTNewSubaccountsKeeper) *SubaccountsKeeper { +}) *SubaccountsKeeper { mock := &SubaccountsKeeper{} mock.Mock.Test(t) diff --git a/protocol/mocks/TimeProvider.go b/protocol/mocks/TimeProvider.go index 43b872dab8..eed4990e2c 100644 --- a/protocol/mocks/TimeProvider.go +++ b/protocol/mocks/TimeProvider.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -17,6 +17,10 @@ type TimeProvider struct { func (_m *TimeProvider) Now() time.Time { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for Now") + } + var r0 time.Time if rf, ok := ret.Get(0).(func() time.Time); ok { r0 = rf() @@ -27,13 +31,12 @@ func (_m *TimeProvider) Now() time.Time { return r0 } -type mockConstructorTestingTNewTimeProvider interface { +// NewTimeProvider creates a new instance of TimeProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewTimeProvider(t interface { mock.TestingT Cleanup(func()) -} - -// NewTimeProvider creates a new instance of TimeProvider. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewTimeProvider(t mockConstructorTestingTNewTimeProvider) *TimeProvider { +}) *TimeProvider { mock := &TimeProvider{} mock.Mock.Test(t) diff --git a/protocol/mocks/TxBuilder.go b/protocol/mocks/TxBuilder.go index 6b0ec45047..921e9aef2e 100644 --- a/protocol/mocks/TxBuilder.go +++ b/protocol/mocks/TxBuilder.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -24,6 +24,10 @@ type TxBuilder struct { func (_m *TxBuilder) AddAuxSignerData(_a0 tx.AuxSignerData) error { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for AddAuxSignerData") + } + var r0 error if rf, ok := ret.Get(0).(func(tx.AuxSignerData) error); ok { r0 = rf(_a0) @@ -38,6 +42,10 @@ func (_m *TxBuilder) AddAuxSignerData(_a0 tx.AuxSignerData) error { func (_m *TxBuilder) GetTx() signing.Tx { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for GetTx") + } + var r0 signing.Tx if rf, ok := ret.Get(0).(func() signing.Tx); ok { r0 = rf() @@ -85,6 +93,10 @@ func (_m *TxBuilder) SetMsgs(msgs ...proto.Message) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for SetMsgs") + } + var r0 error if rf, ok := ret.Get(0).(func(...proto.Message) error); ok { r0 = rf(msgs...) @@ -105,6 +117,10 @@ func (_m *TxBuilder) SetSignatures(signatures ...txsigning.SignatureV2) error { _ca = append(_ca, _va...) ret := _m.Called(_ca...) + if len(ret) == 0 { + panic("no return value specified for SetSignatures") + } + var r0 error if rf, ok := ret.Get(0).(func(...txsigning.SignatureV2) error); ok { r0 = rf(signatures...) @@ -120,13 +136,12 @@ func (_m *TxBuilder) SetTimeoutHeight(height uint64) { _m.Called(height) } -type mockConstructorTestingTNewTxBuilder interface { +// NewTxBuilder creates a new instance of TxBuilder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewTxBuilder(t interface { mock.TestingT Cleanup(func()) -} - -// NewTxBuilder creates a new instance of TxBuilder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewTxBuilder(t mockConstructorTestingTNewTxBuilder) *TxBuilder { +}) *TxBuilder { mock := &TxBuilder{} mock.Mock.Test(t) diff --git a/protocol/mocks/TxConfig.go b/protocol/mocks/TxConfig.go index f5d2d36615..2092debde2 100644 --- a/protocol/mocks/TxConfig.go +++ b/protocol/mocks/TxConfig.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.23.1. DO NOT EDIT. +// Code generated by mockery v2.42.0. DO NOT EDIT. package mocks @@ -22,6 +22,10 @@ type TxConfig struct { func (_m *TxConfig) MarshalSignatureJSON(_a0 []signing.SignatureV2) ([]byte, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for MarshalSignatureJSON") + } + var r0 []byte var r1 error if rf, ok := ret.Get(0).(func([]signing.SignatureV2) ([]byte, error)); ok { @@ -48,6 +52,10 @@ func (_m *TxConfig) MarshalSignatureJSON(_a0 []signing.SignatureV2) ([]byte, err func (_m *TxConfig) NewTxBuilder() client.TxBuilder { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for NewTxBuilder") + } + var r0 client.TxBuilder if rf, ok := ret.Get(0).(func() client.TxBuilder); ok { r0 = rf() @@ -64,6 +72,10 @@ func (_m *TxConfig) NewTxBuilder() client.TxBuilder { func (_m *TxConfig) SignModeHandler() *txsigning.HandlerMap { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for SignModeHandler") + } + var r0 *txsigning.HandlerMap if rf, ok := ret.Get(0).(func() *txsigning.HandlerMap); ok { r0 = rf() @@ -80,6 +92,10 @@ func (_m *TxConfig) SignModeHandler() *txsigning.HandlerMap { func (_m *TxConfig) SigningContext() *txsigning.Context { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for SigningContext") + } + var r0 *txsigning.Context if rf, ok := ret.Get(0).(func() *txsigning.Context); ok { r0 = rf() @@ -96,6 +112,10 @@ func (_m *TxConfig) SigningContext() *txsigning.Context { func (_m *TxConfig) TxDecoder() types.TxDecoder { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TxDecoder") + } + var r0 types.TxDecoder if rf, ok := ret.Get(0).(func() types.TxDecoder); ok { r0 = rf() @@ -112,6 +132,10 @@ func (_m *TxConfig) TxDecoder() types.TxDecoder { func (_m *TxConfig) TxEncoder() types.TxEncoder { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TxEncoder") + } + var r0 types.TxEncoder if rf, ok := ret.Get(0).(func() types.TxEncoder); ok { r0 = rf() @@ -128,6 +152,10 @@ func (_m *TxConfig) TxEncoder() types.TxEncoder { func (_m *TxConfig) TxJSONDecoder() types.TxDecoder { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TxJSONDecoder") + } + var r0 types.TxDecoder if rf, ok := ret.Get(0).(func() types.TxDecoder); ok { r0 = rf() @@ -144,6 +172,10 @@ func (_m *TxConfig) TxJSONDecoder() types.TxDecoder { func (_m *TxConfig) TxJSONEncoder() types.TxEncoder { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for TxJSONEncoder") + } + var r0 types.TxEncoder if rf, ok := ret.Get(0).(func() types.TxEncoder); ok { r0 = rf() @@ -160,6 +192,10 @@ func (_m *TxConfig) TxJSONEncoder() types.TxEncoder { func (_m *TxConfig) UnmarshalSignatureJSON(_a0 []byte) ([]signing.SignatureV2, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for UnmarshalSignatureJSON") + } + var r0 []signing.SignatureV2 var r1 error if rf, ok := ret.Get(0).(func([]byte) ([]signing.SignatureV2, error)); ok { @@ -186,6 +222,10 @@ func (_m *TxConfig) UnmarshalSignatureJSON(_a0 []byte) ([]signing.SignatureV2, e func (_m *TxConfig) WrapTxBuilder(_a0 types.Tx) (client.TxBuilder, error) { ret := _m.Called(_a0) + if len(ret) == 0 { + panic("no return value specified for WrapTxBuilder") + } + var r0 client.TxBuilder var r1 error if rf, ok := ret.Get(0).(func(types.Tx) (client.TxBuilder, error)); ok { @@ -208,13 +248,12 @@ func (_m *TxConfig) WrapTxBuilder(_a0 types.Tx) (client.TxBuilder, error) { return r0, r1 } -type mockConstructorTestingTNewTxConfig interface { +// NewTxConfig creates a new instance of TxConfig. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewTxConfig(t interface { mock.TestingT Cleanup(func()) -} - -// NewTxConfig creates a new instance of TxConfig. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -func NewTxConfig(t mockConstructorTestingTNewTxConfig) *TxConfig { +}) *TxConfig { mock := &TxConfig{} mock.Mock.Test(t) diff --git a/protocol/mocks/UpdateMarketPriceTxDecoder.go b/protocol/mocks/UpdateMarketPriceTxDecoder.go new file mode 100644 index 0000000000..49ead5fece --- /dev/null +++ b/protocol/mocks/UpdateMarketPriceTxDecoder.go @@ -0,0 +1,76 @@ +// Code generated by mockery v2.42.0. DO NOT EDIT. + +package mocks + +import ( + types "github.com/cosmos/cosmos-sdk/types" + process "github.com/dydxprotocol/v4-chain/protocol/app/process" + mock "github.com/stretchr/testify/mock" +) + +// UpdateMarketPriceTxDecoder is an autogenerated mock type for the UpdateMarketPriceTxDecoder type +type UpdateMarketPriceTxDecoder struct { + mock.Mock +} + +// DecodeUpdateMarketPricesTx provides a mock function with given fields: ctx, txs +func (_m *UpdateMarketPriceTxDecoder) DecodeUpdateMarketPricesTx(ctx types.Context, txs [][]byte) (*process.UpdateMarketPricesTx, error) { + ret := _m.Called(ctx, txs) + + if len(ret) == 0 { + panic("no return value specified for DecodeUpdateMarketPricesTx") + } + + var r0 *process.UpdateMarketPricesTx + var r1 error + if rf, ok := ret.Get(0).(func(types.Context, [][]byte) (*process.UpdateMarketPricesTx, error)); ok { + return rf(ctx, txs) + } + if rf, ok := ret.Get(0).(func(types.Context, [][]byte) *process.UpdateMarketPricesTx); ok { + r0 = rf(ctx, txs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*process.UpdateMarketPricesTx) + } + } + + if rf, ok := ret.Get(1).(func(types.Context, [][]byte) error); ok { + r1 = rf(ctx, txs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetTxOffset provides a mock function with given fields: ctx +func (_m *UpdateMarketPriceTxDecoder) GetTxOffset(ctx types.Context) int { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for GetTxOffset") + } + + var r0 int + if rf, ok := ret.Get(0).(func(types.Context) int); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(int) + } + + return r0 +} + +// NewUpdateMarketPriceTxDecoder creates a new instance of UpdateMarketPriceTxDecoder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewUpdateMarketPriceTxDecoder(t interface { + mock.TestingT + Cleanup(func()) +}) *UpdateMarketPriceTxDecoder { + mock := &UpdateMarketPriceTxDecoder{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/protocol/scripts/genesis/sample_pregenesis.json b/protocol/scripts/genesis/sample_pregenesis.json index 5cc71f0073..046f53e67f 100644 --- a/protocol/scripts/genesis/sample_pregenesis.json +++ b/protocol/scripts/genesis/sample_pregenesis.json @@ -100,18 +100,14 @@ }, "clob": { "block_rate_limit_config": { - "max_short_term_order_cancellations_per_n_blocks": [ + "max_short_term_order_cancellations_per_n_blocks": [], + "max_short_term_orders_and_cancels_per_n_blocks": [ { - "limit": 200, - "num_blocks": 1 - } - ], - "max_short_term_orders_per_n_blocks": [ - { - "limit": 200, + "limit": 400, "num_blocks": 1 } ], + "max_short_term_orders_per_n_blocks": [], "max_stateful_orders_per_n_blocks": [ { "limit": 2, @@ -1830,15 +1826,15 @@ ] } }, - "app_version": "4.0.0-dev0-101-g18028fd0", + "app_version": "4.0.0-dev0-134-gc65a2bfe", "chain_id": "dydx-sample-1", "consensus": { "params": { "abci": { - "vote_extensions_enable_height": "0" + "vote_extensions_enable_height": "1" }, "block": { - "max_bytes": "22020096", + "max_bytes": "4194304", "max_gas": "-1" }, "evidence": { @@ -1856,12 +1852,6 @@ } } }, - "consensus_params": { - "block": { - "max_bytes": "4194304", - "max_gas": "-1" - } - }, "genesis_time": "2023-12-31T00:00:00Z", "initial_height": 1 } diff --git a/protocol/streaming/grpc/grpc_streaming_manager.go b/protocol/streaming/grpc/grpc_streaming_manager.go index 95ae0a984e..b4cea80b57 100644 --- a/protocol/streaming/grpc/grpc_streaming_manager.go +++ b/protocol/streaming/grpc/grpc_streaming_manager.go @@ -103,17 +103,21 @@ func (sm *GrpcStreamingManagerImpl) SendOrderbookUpdates( // Send updates to subscribers. idsToRemove := make([]uint32, 0) for id, subscription := range sm.orderbookSubscriptions { + updatesToSend := make([]ocutypes.OffChainUpdateV1, 0) for _, clobPairId := range subscription.clobPairIds { if updates, ok := v1updates[clobPairId]; ok { - if err := subscription.srv.Send( - &clobtypes.StreamOrderbookUpdatesResponse{ - Updates: updates, - Snapshot: snapshot, - }, - ); err != nil { - idsToRemove = append(idsToRemove, id) - break - } + updatesToSend = append(updatesToSend, updates...) + } + } + + if len(updatesToSend) > 0 { + if err := subscription.srv.Send( + &clobtypes.StreamOrderbookUpdatesResponse{ + Updates: updatesToSend, + Snapshot: snapshot, + }, + ); err != nil { + idsToRemove = append(idsToRemove, id) } } } diff --git a/protocol/testing/e2e/funding/funding_e2e_test.go b/protocol/testing/e2e/funding/funding_e2e_test.go index 16b82add69..680421c673 100644 --- a/protocol/testing/e2e/funding/funding_e2e_test.go +++ b/protocol/testing/e2e/funding/funding_e2e_test.go @@ -483,7 +483,7 @@ func TestFunding(t *testing.T) { tApp.App, testapp.MustMakeCheckTxOptions{ AccAddressForSigning: transfer.Transfer.Sender.Owner, - Gas: 100_000, + Gas: 110_000, FeeAmt: constants.TestFeeCoins_5Cents, }, &transfer, diff --git a/protocol/testing/e2e/gov/perpetuals_test.go b/protocol/testing/e2e/gov/perpetuals_test.go index e2ddeefcff..1252633a6a 100644 --- a/protocol/testing/e2e/gov/perpetuals_test.go +++ b/protocol/testing/e2e/gov/perpetuals_test.go @@ -1,6 +1,7 @@ package gov_test import ( + "fmt" "testing" "github.com/cometbft/cometbft/types" @@ -276,6 +277,7 @@ func TestUpdatePerpetualsParams(t *testing.T) { for i, marketId := range append(tc.genesisMarketIds, TEST_PERPETUAL_PARAMS.MarketId) { marketParamPrice := pricestest.GenerateMarketParamPrice( pricestest.WithId(marketId), + pricestest.WithPair(fmt.Sprintf("%d-%d", i, i)), ) marketParams[i] = marketParamPrice.Param marketPrices[i] = marketParamPrice.Price diff --git a/protocol/testing/e2e/gov/prices_test.go b/protocol/testing/e2e/gov/prices_test.go index 52531c4ea6..df8ec0d33d 100644 --- a/protocol/testing/e2e/gov/prices_test.go +++ b/protocol/testing/e2e/gov/prices_test.go @@ -28,7 +28,7 @@ var ( MODIFIED_MARKET_PARAM = pricestypes.MarketParam{ Id: GENESIS_MARKET_PARAM.Id, - Pair: "eth-adv4tnt", + Pair: GENESIS_MARKET_PARAM.Pair, Exponent: GENESIS_MARKET_PARAM.Exponent, // exponent cannot be updated MinExchanges: 3, MinPriceChangePpm: 2_002, diff --git a/protocol/testing/e2e/trading_rewards/trading_rewards_test.go b/protocol/testing/e2e/trading_rewards/trading_rewards_test.go index 5c83a2b3aa..9541d84054 100644 --- a/protocol/testing/e2e/trading_rewards/trading_rewards_test.go +++ b/protocol/testing/e2e/trading_rewards/trading_rewards_test.go @@ -3,6 +3,7 @@ package trading_rewards_test import ( sdkmath "cosmossdk.io/math" "github.com/cosmos/gogoproto/proto" + "github.com/dydxprotocol/v4-chain/protocol/app/flags" "math/big" "testing" "time" @@ -705,6 +706,7 @@ func TestTradingRewards(t *testing.T) { msgSender := msgsender.NewIndexerMessageSenderInMemoryCollector() appOpts := map[string]interface{}{ indexer.MsgSenderInstanceForTest: msgSender, + flags.VEOracleEnabled: false, } tApp := testapp.NewTestAppBuilder(t). // UpdateIndexPrice only contacts the tApp.App.Server causing non-determinism in the diff --git a/protocol/testing/genesis.sh b/protocol/testing/genesis.sh index 2c9b065f98..747b02bbbe 100755 --- a/protocol/testing/genesis.sh +++ b/protocol/testing/genesis.sh @@ -77,8 +77,9 @@ function edit_genesis() { dasel put -t string -f "$GENESIS" '.genesis_time' -v "$GENESIS_TIME" # Consensus params - dasel put -t string -f "$GENESIS" '.consensus_params.block.max_bytes' -v '4194304' - dasel put -t string -f "$GENESIS" '.consensus_params.block.max_gas' -v '-1' + dasel put -t string -f "$GENESIS" '.consensus.params.block.max_bytes' -v '4194304' + dasel put -t string -f "$GENESIS" '.consensus.params.block.max_gas' -v '-1' + dasel put -t string -f "$GENESIS" '.consensus.params.abci.vote_extensions_enable_height' -v '1' # Update crisis module. dasel put -t string -f "$GENESIS" '.app_state.crisis.constant_fee.denom' -v "$NATIVE_TOKEN" @@ -1404,14 +1405,10 @@ function edit_genesis() { dasel put -t int -f "$GENESIS" '.app_state.clob.liquidations_config.fillable_price_config.spread_to_maintenance_margin_ratio_ppm' -v '1500000' # 150% # Block Rate Limit - # Max 50 short term orders per block - dasel put -t json -f "$GENESIS" '.app_state.clob.block_rate_limit_config.max_short_term_orders_per_n_blocks.[]' -v "{}" - dasel put -t int -f "$GENESIS" '.app_state.clob.block_rate_limit_config.max_short_term_orders_per_n_blocks.[0].limit' -v '200' - dasel put -t int -f "$GENESIS" '.app_state.clob.block_rate_limit_config.max_short_term_orders_per_n_blocks.[0].num_blocks' -v '1' - # Max 50 short term order cancellations per block - dasel put -t json -f "$GENESIS" '.app_state.clob.block_rate_limit_config.max_short_term_order_cancellations_per_n_blocks.[]' -v "{}" - dasel put -t int -f "$GENESIS" '.app_state.clob.block_rate_limit_config.max_short_term_order_cancellations_per_n_blocks.[0].limit' -v '200' - dasel put -t int -f "$GENESIS" '.app_state.clob.block_rate_limit_config.max_short_term_order_cancellations_per_n_blocks.[0].num_blocks' -v '1' + # Max 400 short term orders/cancels per block + dasel put -t json -f "$GENESIS" '.app_state.clob.block_rate_limit_config.max_short_term_orders_and_cancels_per_n_blocks.[]' -v "{}" + dasel put -t int -f "$GENESIS" '.app_state.clob.block_rate_limit_config.max_short_term_orders_and_cancels_per_n_blocks.[0].limit' -v '400' + dasel put -t int -f "$GENESIS" '.app_state.clob.block_rate_limit_config.max_short_term_orders_and_cancels_per_n_blocks.[0].num_blocks' -v '1' # Max 2 stateful orders per block dasel put -t json -f "$GENESIS" '.app_state.clob.block_rate_limit_config.max_stateful_orders_per_n_blocks.[]' -v "{}" dasel put -t int -f "$GENESIS" '.app_state.clob.block_rate_limit_config.max_stateful_orders_per_n_blocks.[0].limit' -v '2' diff --git a/protocol/testing/testnet-local/local.sh b/protocol/testing/testnet-local/local.sh index 6898682118..c8b599d70d 100755 --- a/protocol/testing/testnet-local/local.sh +++ b/protocol/testing/testnet-local/local.sh @@ -1,5 +1,5 @@ #!/bin/bash -set -eo pipefail +set -exo pipefail # This file initializes muliple validators for local and CI testing purposes. # This file should be run as part of `docker-compose.yml`. @@ -159,8 +159,9 @@ use_slinky() { # Disable pricefeed-daemon dasel put -t bool -f "$CONFIG_FOLDER"/app.toml 'price-daemon-enabled' -v false # Enable slinky daemon - dasel put -t bool -f "$CONFIG_FOLDER"/app.toml 'slinky-daemon-enabled' -v true - dasel put -t string -f "$VAL_CONFIG_DIR"/app.toml '.oracle.oracle_address' -v 'slinky0:8080' + dasel put -t bool -f "$CONFIG_FOLDER"/app.toml 'oracle.enabled' -v true + dasel put -t string -f "$VAL_CONFIG_DIR"/app.toml 'oracle.oracle_address' -v 'slinky0:8080' + dasel put -t string -f "$VAL_CONFIG_DIR"/app.toml 'slinky-vote-extension-oracle-enabled' -v 'true' } # TODO(DEC-1894): remove this function once we migrate off of persistent peers. diff --git a/protocol/testutil/app/app.go b/protocol/testutil/app/app.go index da22536657..2b54364476 100644 --- a/protocol/testutil/app/app.go +++ b/protocol/testutil/app/app.go @@ -63,6 +63,7 @@ import ( sendingtypes "github.com/dydxprotocol/v4-chain/protocol/x/sending/types" stattypes "github.com/dydxprotocol/v4-chain/protocol/x/stats/types" satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" + vaulttypes "github.com/dydxprotocol/v4-chain/protocol/x/vault/types" vesttypes "github.com/dydxprotocol/v4-chain/protocol/x/vest/types" "github.com/stretchr/testify/require" "golang.org/x/exp/slices" @@ -200,7 +201,8 @@ type GenesisStates interface { bridgetypes.GenesisState | govtypesv1.GenesisState | ratelimittypes.GenesisState | - govplus.GenesisState + govplus.GenesisState | + vaulttypes.GenesisState } // UpdateGenesisDocWithAppStateForModule updates the supplied genesis doc using the provided function. The function @@ -254,6 +256,8 @@ func UpdateGenesisDocWithAppStateForModule[T GenesisStates](genesisDoc *types.Ge moduleName = ratelimittypes.ModuleName case govplus.GenesisState: moduleName = govplus.ModuleName + case vaulttypes.GenesisState: + moduleName = vaulttypes.ModuleName default: panic(fmt.Errorf("Unsupported type %T", t)) } @@ -1255,6 +1259,7 @@ func launchValidatorInDir( appConfig.API.Enable = false // We disable telemetry since multiple instances of the application fail to register causing a panic. appConfig.Telemetry.Enabled = false + appConfig.Oracle.MetricsEnabled = false return s, appConfig }, // Capture the application instance. @@ -1282,6 +1287,8 @@ func launchValidatorInDir( "false", "--bridge-daemon-eth-rpc-endpoint", "https://eth-sepolia.g.alchemy.com/v2/demo", + "--oracle.enabled=false", + "--oracle.metrics_enabled=false", }) ctx := svrcmd.CreateExecuteContext(parentCtx) diff --git a/protocol/testutil/appoptions/app_options.go b/protocol/testutil/appoptions/app_options.go index b7a0db07d1..81731e3ae6 100644 --- a/protocol/testutil/appoptions/app_options.go +++ b/protocol/testutil/appoptions/app_options.go @@ -49,6 +49,12 @@ func GetDefaultTestAppOptions(homePath string, customFlags map[string]interface{ // Disable the Price Daemon for all end-to-end and integration tests by default. fao.Set(daemonflags.FlagPriceDaemonEnabled, false) + // Disable the Slinky Daemon for all end-to-end and integration tests by default. + fao.Set(daemonflags.FlagOracleEnabled, false) + + // Disable Slinky Metrics for all end-to-end and integration tests by default. + fao.Set(daemonflags.FlagOracleMetricsEnabled, false) + // Disable the Bridge Daemon for all end-to-end and integration tests by default. fao.Set(daemonflags.FlagBridgeDaemonEnabled, false) diff --git a/protocol/testutil/constants/genesis.go b/protocol/testutil/constants/genesis.go index d8b1ca8209..dc56ee3590 100644 --- a/protocol/testutil/constants/genesis.go +++ b/protocol/testutil/constants/genesis.go @@ -218,10 +218,10 @@ const GenesisState = `{ }, "clob": { "block_rate_limit_config": { - "max_short_term_orders_per_n_blocks": [ + "max_short_term_orders_and_cancels_per_n_blocks": [ { "num_blocks": 1, - "limit": 200 + "limit": 400 } ], "max_stateful_orders_per_n_blocks": [ @@ -233,12 +233,6 @@ const GenesisState = `{ "num_blocks": 100, "limit": 20 } - ], - "max_short_term_order_cancellations_per_n_blocks": [ - { - "num_blocks": 1, - "limit": 200 - } ] }, "clob_pairs": [ diff --git a/protocol/testutil/constants/perpetuals.go b/protocol/testutil/constants/perpetuals.go index 2069fe0e65..a27535afe1 100644 --- a/protocol/testutil/constants/perpetuals.go +++ b/protocol/testutil/constants/perpetuals.go @@ -327,6 +327,18 @@ var ( }, FundingIndex: dtypes.ZeroInt(), } + Iso2Usd_IsolatedMarket = perptypes.Perpetual{ + Params: perptypes.PerpetualParams{ + Id: 4, + Ticker: "ISO2-USD", + MarketId: uint32(4), + AtomicResolution: int32(-7), + DefaultFundingPpm: int32(0), + LiquidityTier: uint32(3), + MarketType: perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_ISOLATED, + }, + FundingIndex: dtypes.ZeroInt(), + } ) var TestMarketPerpetuals = []perptypes.Perpetual{ @@ -367,6 +379,7 @@ var TestMarketPerpetuals = []perptypes.Perpetual{ FundingIndex: dtypes.ZeroInt(), }, IsoUsd_IsolatedMarket, + Iso2Usd_IsolatedMarket, } // AddPremiumVotes messages. diff --git a/protocol/testutil/constants/positions.go b/protocol/testutil/constants/positions.go index 125af3e227..5035068aa7 100644 --- a/protocol/testutil/constants/positions.go +++ b/protocol/testutil/constants/positions.go @@ -79,6 +79,22 @@ var ( Quantums: dtypes.NewInt(100_000_000), FundingIndex: dtypes.NewInt(0), } + PerpetualPosition_OneISO2Long = satypes.PerpetualPosition{ + PerpetualId: 4, + Quantums: dtypes.NewInt(10_000_000), + FundingIndex: dtypes.NewInt(0), + } + // Short position for arbitrary isolated market + PerpetualPosition_OneISOShort = satypes.PerpetualPosition{ + PerpetualId: 3, + Quantums: dtypes.NewInt(-100_000_000), + FundingIndex: dtypes.NewInt(0), + } + PerpetualPosition_OneISO2Short = satypes.PerpetualPosition{ + PerpetualId: 4, + Quantums: dtypes.NewInt(-10_000_000), + FundingIndex: dtypes.NewInt(0), + } // Asset Positions Usdc_Asset_0 = satypes.AssetPosition{ diff --git a/protocol/testutil/constants/pricefeed.go b/protocol/testutil/constants/pricefeed.go index 47df2ae63a..f7bdf0e2c2 100644 --- a/protocol/testutil/constants/pricefeed.go +++ b/protocol/testutil/constants/pricefeed.go @@ -14,6 +14,7 @@ var ( MarketId1 = uint32(1) MarketId2 = uint32(2) MarketId3 = uint32(3) + MarketId4 = uint32(4) MarketId7 = uint32(7) MarketId8 = uint32(8) @@ -276,12 +277,19 @@ var ( Exchange3_Price3_TimeT, }, }, + { + MarketId: MarketId4, + ExchangePrices: []*api.ExchangePrice{ + Exchange3_Price3_TimeT, + }, + }, } AtTimeTSingleExchangeSmoothedPrices = map[uint32]uint64{ MarketId0: Exchange0_Price4_TimeT.Price, MarketId1: Exchange1_Price1_TimeT.Price, MarketId2: Exchange2_Price2_TimeT.Price, MarketId3: Exchange3_Price3_TimeT.Price, + MarketId4: Exchange3_Price3_TimeT.Price, } AtTimeTSingleExchangeSmoothedPricesPlus10 = map[uint32]uint64{ @@ -289,6 +297,7 @@ var ( MarketId1: Exchange1_Price1_TimeT.Price + 10, MarketId2: Exchange2_Price2_TimeT.Price + 10, MarketId3: Exchange3_Price3_TimeT.Price + 10, + MarketId4: Exchange3_Price3_TimeT.Price + 10, } AtTimeTSingleExchangeSmoothedPricesPlus7 = map[uint32]uint64{ @@ -296,6 +305,7 @@ var ( MarketId1: Exchange1_Price1_TimeT.Price + 7, MarketId2: Exchange2_Price2_TimeT.Price + 7, MarketId3: Exchange3_Price3_TimeT.Price + 7, + MarketId4: Exchange3_Price3_TimeT.Price + 7, } MixedTimePriceUpdate = []*api.MarketPriceUpdate{ diff --git a/protocol/testutil/constants/prices.go b/protocol/testutil/constants/prices.go index 022d1793c5..89c7ee360f 100644 --- a/protocol/testutil/constants/prices.go +++ b/protocol/testutil/constants/prices.go @@ -27,6 +27,7 @@ const ( SolUsdPair = "SOL-USD" LtcUsdPair = "LTC-USD" IsoUsdPair = "ISO-USD" + Iso2UsdPair = "ISO2-USD" BtcUsdExponent = -5 EthUsdExponent = -6 @@ -36,6 +37,7 @@ const ( SolUsdExponent = -8 LtcUsdExponent = -7 IsoUsdExponent = -8 + Iso2UsdExponent = -7 CoinbaseExchangeName = "Coinbase" BinanceExchangeName = "Binance" @@ -212,6 +214,15 @@ var TestMarketExchangeConfigs = map[pricefeedclient.MarketId]string{ } ] }`, + exchange_config.MARKET_ISO2_USD: `{ + "exchanges": [ + { + "exchangeName": "Binance", + "ticker": "ISO2USDT", + "adjustByMarket": "USDT-USD" + } + ] + }`, } var TestMarketParams = []types.MarketParam{ @@ -247,6 +258,14 @@ var TestMarketParams = []types.MarketParam{ MinPriceChangePpm: 50, ExchangeConfigJson: TestMarketExchangeConfigs[exchange_config.MARKET_ISO_USD], }, + { + Id: 4, + Pair: Iso2UsdPair, + Exponent: Iso2UsdExponent, + MinExchanges: 1, + MinPriceChangePpm: 50, + ExchangeConfigJson: TestMarketExchangeConfigs[exchange_config.MARKET_ISO2_USD], + }, } var TestMarketPrices = []types.MarketPrice{ @@ -270,6 +289,11 @@ var TestMarketPrices = []types.MarketPrice{ Exponent: IsoUsdExponent, Price: FiveBillion, // 50$ == 1 ISO }, + { + Id: 4, + Exponent: Iso2UsdExponent, + Price: ThreeBillion, // 300$ == 1 ISO2 + }, } var TestMarketIdsToExponents = map[uint32]int32{ @@ -277,6 +301,7 @@ var TestMarketIdsToExponents = map[uint32]int32{ 1: EthUsdExponent, 2: SolUsdExponent, 3: IsoUsdExponent, + 4: Iso2UsdExponent, } var TestPricesGenesisState = types.GenesisState{ @@ -290,6 +315,7 @@ var ( types.NewMarketPriceUpdate(MarketId1, Price6), types.NewMarketPriceUpdate(MarketId2, Price7), types.NewMarketPriceUpdate(MarketId3, Price4), + types.NewMarketPriceUpdate(MarketId4, Price3), } // `MsgUpdateMarketPrices`. diff --git a/protocol/testutil/constants/subaccounts.go b/protocol/testutil/constants/subaccounts.go index a0e125dcac..e1613b3fe3 100644 --- a/protocol/testutil/constants/subaccounts.go +++ b/protocol/testutil/constants/subaccounts.go @@ -12,28 +12,41 @@ var ( AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_10_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Alice_Num0_100_000USD = satypes.Subaccount{ Id: &Alice_Num0, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_100_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, + } + Alice_Num0_1ISO_LONG_10_000USD = satypes.Subaccount{ + Id: &Alice_Num0, + AssetPositions: []*satypes.AssetPosition{ + &Usdc_Asset_10_000, + }, + PerpetualPositions: []*satypes.PerpetualPosition{ + { + PerpetualId: 3, + Quantums: dtypes.NewInt(1_000_000_000), // 1 ISO + FundingIndex: dtypes.NewInt(0), + }, + }, } Alice_Num1_10_000USD = satypes.Subaccount{ Id: &Alice_Num1, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_10_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Alice_Num1_100_000USD = satypes.Subaccount{ Id: &Alice_Num1, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_100_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Alice_Num1_1BTC_Short_100_000USD = satypes.Subaccount{ Id: &Alice_Num1, @@ -42,8 +55,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -54,8 +68,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(100_000_000), // +1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(100_000_000), // +1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -64,14 +79,40 @@ var ( AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_10_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Bob_Num0_100_000USD = satypes.Subaccount{ Id: &Bob_Num0, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_100_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, + } + Bob_Num0_1ISO_LONG_10_000USD = satypes.Subaccount{ + Id: &Bob_Num0, + AssetPositions: []*satypes.AssetPosition{ + &Usdc_Asset_10_000, + }, + PerpetualPositions: []*satypes.PerpetualPosition{ + { + PerpetualId: 3, + Quantums: dtypes.NewInt(10_000_000), // 1 ISO2 + FundingIndex: dtypes.NewInt(0), + }, + }, + } + Bob_Num0_1ISO2_LONG_10_000USD = satypes.Subaccount{ + Id: &Bob_Num0, + AssetPositions: []*satypes.AssetPosition{ + &Usdc_Asset_10_000, + }, + PerpetualPositions: []*satypes.PerpetualPosition{ + { + PerpetualId: 4, + Quantums: dtypes.NewInt(10_000_000), // 1 ISO2 + FundingIndex: dtypes.NewInt(0), + }, + }, } Carl_Num0_100BTC_Short_10100USD = satypes.Subaccount{ Id: &Carl_Num0, @@ -80,8 +121,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-10_000_000_000), // -100 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-10_000_000_000), // -100 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -92,8 +134,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -104,8 +147,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -135,8 +179,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -150,8 +195,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -181,8 +227,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(100_000_000), // 1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -212,8 +259,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -227,12 +275,14 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(0), }, { - PerpetualId: 1, - Quantums: dtypes.NewInt(1_000_000_000), // 1 ETH + PerpetualId: 1, + Quantums: dtypes.NewInt(1_000_000_000), // 1 ETH + FundingIndex: dtypes.NewInt(0), }, }, } @@ -241,68 +291,68 @@ var ( AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_599, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Carl_Num0_660USD = satypes.Subaccount{ Id: &Carl_Num0, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_660, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Carl_Num0_10000USD = satypes.Subaccount{ Id: &Carl_Num0, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_10_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Carl_Num0_50000USD = satypes.Subaccount{ Id: &Carl_Num0, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_50_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Carl_Num0_100000USD = satypes.Subaccount{ Id: &Carl_Num0, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_100_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Carl_Num0_500000USD = satypes.Subaccount{ Id: &Carl_Num0, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_500_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Carl_Num0_0USD = satypes.Subaccount{ Id: &Carl_Num0, AssetPositions: []*satypes.AssetPosition{}, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Carl_Num1_500USD = satypes.Subaccount{ Id: &Carl_Num1, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_500, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Carl_Num1_100000USD = satypes.Subaccount{ Id: &Carl_Num1, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_100_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Carl_Num1_Short_500USD = satypes.Subaccount{ Id: &Carl_Num1, AssetPositions: []*satypes.AssetPosition{ &Short_Usdc_Asset_500, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Carl_Num1_01BTC_Long_4600USD_Short = satypes.Subaccount{ Id: &Carl_Num1, @@ -311,8 +361,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(10_000_000), // 0.1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(10_000_000), // 0.1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -326,8 +377,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -338,8 +390,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(10_000_000), // 0.1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(10_000_000), // 0.1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -379,8 +432,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-100_000_000), // -1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -426,8 +480,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(100_000_000), // 1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -468,21 +523,21 @@ var ( AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_599, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Dave_Num0_10000USD = satypes.Subaccount{ Id: &Dave_Num0, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_10_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Dave_Num0_500000USD = satypes.Subaccount{ Id: &Dave_Num0, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_500_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Dave_Num0_100BTC_Short_10200USD = satypes.Subaccount{ Id: &Dave_Num0, @@ -491,8 +546,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-10_000_000_000), // -100 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-10_000_000_000), // -100 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -503,8 +559,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(10_000_000_000), // 100 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(10_000_000_000), // 100 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -515,8 +572,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(100_000_000), // 1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -527,12 +585,14 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(100_000_000), // 1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(0), }, { - PerpetualId: 1, - Quantums: dtypes.NewInt(1_000_000_000), // 1 ETH + PerpetualId: 1, + Quantums: dtypes.NewInt(1_000_000_000), // 1 ETH + FundingIndex: dtypes.NewInt(0), }, }, } @@ -541,14 +601,14 @@ var ( AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_10_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Dave_Num1_500000USD = satypes.Subaccount{ Id: &Dave_Num1, AssetPositions: []*satypes.AssetPosition{ &Usdc_Asset_500_000, }, - PerpetualPositions: []*satypes.PerpetualPosition{}, + PerpetualPositions: nil, } Dave_Num1_025BTC_Long_50000USD = satypes.Subaccount{ Id: &Dave_Num1, @@ -557,8 +617,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(25_000_000), // 0.25 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(25_000_000), // 0.25 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -569,8 +630,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(50_000_000), // 0.5 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(50_000_000), // 0.5 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -581,8 +643,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(100_000_000), // 1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -593,8 +656,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(-10_000_000_000), // -100 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(-10_000_000_000), // -100 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -608,8 +672,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 0, - Quantums: dtypes.NewInt(100_000_000), // 1 BTC + PerpetualId: 0, + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(0), }, }, } @@ -620,8 +685,9 @@ var ( }, PerpetualPositions: []*satypes.PerpetualPosition{ { - PerpetualId: 1, - Quantums: dtypes.NewInt(1_000_000_000), // 1 ETH + PerpetualId: 1, + Quantums: dtypes.NewInt(1_000_000_000), // 1 ETH + FundingIndex: dtypes.NewInt(0), }, }, } diff --git a/protocol/testutil/constants/vault.go b/protocol/testutil/constants/vault.go new file mode 100644 index 0000000000..eba5e6120d --- /dev/null +++ b/protocol/testutil/constants/vault.go @@ -0,0 +1,16 @@ +package constants + +import ( + vaulttypes "github.com/dydxprotocol/v4-chain/protocol/x/vault/types" +) + +var ( + Vault_Clob_0 = vaulttypes.VaultId{ + Type: vaulttypes.VaultType_VAULT_TYPE_CLOB, + Number: 0, + } + Vault_Clob_1 = vaulttypes.VaultId{ + Type: vaulttypes.VaultType_VAULT_TYPE_CLOB, + Number: 1, + } +) diff --git a/protocol/testutil/daemons/pricefeed/exchange_config/market_id.go b/protocol/testutil/daemons/pricefeed/exchange_config/market_id.go index 2123f76b1c..cb6fe6c4d6 100644 --- a/protocol/testutil/daemons/pricefeed/exchange_config/market_id.go +++ b/protocol/testutil/daemons/pricefeed/exchange_config/market_id.go @@ -75,8 +75,9 @@ const ( // MARKET_TEST_USD is the id used for the TEST-USD market pair. MARKET_TEST_USD types.MarketId = 33 - // Arbitrary isolated market - MARKET_ISO_USD types.MarketId = 999_999 + // Arbitrary isolated markets + MARKET_ISO2_USD types.MarketId = 999_998 + MARKET_ISO_USD types.MarketId = 999_999 // Non-trading markets. // MARKET_USDT_USD is the id for the USDT-USD market pair. diff --git a/protocol/testutil/keeper/assets.go b/protocol/testutil/keeper/assets.go index 6e1afcba5d..7b159f4f2d 100644 --- a/protocol/testutil/keeper/assets.go +++ b/protocol/testutil/keeper/assets.go @@ -56,7 +56,7 @@ func AssetsKeepers( transientStoreKey storetypes.StoreKey, ) []GenesisInitializer { // Define necessary keepers here for unit tests - pricesKeeper, _, _, _, mockTimeProvider = createPricesKeeper(stateStore, db, cdc, transientStoreKey) + pricesKeeper, _, _, mockTimeProvider = createPricesKeeper(stateStore, db, cdc, transientStoreKey) accountKeeper, _ = createAccountKeeper(stateStore, db, cdc, registry) bankKeeper, _ = createBankKeeper(stateStore, db, cdc, accountKeeper) keeper, storeKey = createAssetsKeeper(stateStore, db, cdc, pricesKeeper, transientStoreKey, msgSenderEnabled) diff --git a/protocol/testutil/keeper/clob.go b/protocol/testutil/keeper/clob.go index 506ebe114a..024dc962f6 100644 --- a/protocol/testutil/keeper/clob.go +++ b/protocol/testutil/keeper/clob.go @@ -79,7 +79,7 @@ func NewClobKeepersTestContextWithUninitializedMemStore( indexerEventsTransientStoreKey storetypes.StoreKey, ) []GenesisInitializer { // Define necessary keepers here for unit tests - ks.PricesKeeper, _, _, _, mockTimeProvider = createPricesKeeper(stateStore, db, cdc, indexerEventsTransientStoreKey) + ks.PricesKeeper, _, _, mockTimeProvider = createPricesKeeper(stateStore, db, cdc, indexerEventsTransientStoreKey) // Mock time provider response for market creation. mockTimeProvider.On("Now").Return(constants.TimeT) epochsKeeper, _ := createEpochsKeeper(stateStore, db, cdc) @@ -218,8 +218,7 @@ func createClobKeeper( streaming.NewNoopGrpcStreamingManager(), constants.TestEncodingCfg.TxConfig.TxDecoder(), flags.GetDefaultClobFlags(), - rate_limit.NewNoOpRateLimiter[*types.MsgPlaceOrder](), - rate_limit.NewNoOpRateLimiter[*types.MsgCancelOrder](), + rate_limit.NewNoOpRateLimiter[sdk.Msg](), liquidationtypes.NewDaemonLiquidationInfo(), ) k.SetAnteHandler(constants.EmptyAnteHandler) @@ -288,6 +287,7 @@ func CreateNClobPair( items[i].SubticksPerTick, items[i].StepBaseQuantums, perps[i].Params.LiquidityTier, + perps[i].Params.MarketType, ), ), ).Return() diff --git a/protocol/testutil/keeper/perpetuals.go b/protocol/testutil/keeper/perpetuals.go index 851926a66d..6417ef3362 100644 --- a/protocol/testutil/keeper/perpetuals.go +++ b/protocol/testutil/keeper/perpetuals.go @@ -63,7 +63,7 @@ func PerpetualsKeepersWithClobHelpers( transientStoreKey storetypes.StoreKey, ) []GenesisInitializer { // Define necessary keepers here for unit tests - pc.PricesKeeper, _, pc.IndexPriceCache, _, pc.MockTimeProvider = createPricesKeeper( + pc.PricesKeeper, _, pc.IndexPriceCache, pc.MockTimeProvider = createPricesKeeper( stateStore, db, cdc, @@ -237,9 +237,8 @@ func CreateNPerpetuals( allLiquidityTiers := keeper.GetAllLiquidityTiers(ctx) require.Greater(t, len(allLiquidityTiers), 0) + CreateNMarkets(t, ctx, pricesKeeper, n) for i := range items { - CreateNMarkets(t, ctx, pricesKeeper, n) - var defaultFundingPpm int32 marketType := types.PerpetualMarketType_PERPETUAL_MARKET_TYPE_CROSS if i%3 == 0 { diff --git a/protocol/testutil/keeper/prices.go b/protocol/testutil/keeper/prices.go index 85175abd1c..d44604676d 100644 --- a/protocol/testutil/keeper/prices.go +++ b/protocol/testutil/keeper/prices.go @@ -31,7 +31,6 @@ func PricesKeepers( keeper *keeper.Keeper, storeKey storetypes.StoreKey, indexPriceCache *pricefeedserver_types.MarketToExchangePrices, - marketToSmoothedPrices types.MarketToSmoothedPrices, mockTimeProvider *mocks.TimeProvider, ) { ctx = initKeepers(t, func( @@ -42,13 +41,13 @@ func PricesKeepers( transientStoreKey storetypes.StoreKey, ) []GenesisInitializer { // Define necessary keepers here for unit tests - keeper, storeKey, indexPriceCache, marketToSmoothedPrices, mockTimeProvider = + keeper, storeKey, indexPriceCache, mockTimeProvider = createPricesKeeper(stateStore, db, cdc, transientStoreKey) return []GenesisInitializer{keeper} }) - return ctx, keeper, storeKey, indexPriceCache, marketToSmoothedPrices, mockTimeProvider + return ctx, keeper, storeKey, indexPriceCache, mockTimeProvider } func createPricesKeeper( @@ -60,7 +59,6 @@ func createPricesKeeper( *keeper.Keeper, storetypes.StoreKey, *pricefeedserver_types.MarketToExchangePrices, - types.MarketToSmoothedPrices, *mocks.TimeProvider, ) { storeKey := storetypes.NewKVStoreKey(types.StoreKey) @@ -69,8 +67,6 @@ func createPricesKeeper( indexPriceCache := pricefeedserver_types.NewMarketToExchangePrices(pricefeed_types.MaxPriceAge) - marketToSmoothedPrices := types.NewMarketToSmoothedPrices(types.SmoothedPriceTrackingBlockHistoryLength) - mockTimeProvider := &mocks.TimeProvider{} mockMsgSender := &mocks.IndexerMessageSender{} @@ -84,7 +80,6 @@ func createPricesKeeper( cdc, storeKey, indexPriceCache, - marketToSmoothedPrices, mockTimeProvider, mockIndexerEventsManager, []string{ @@ -93,7 +88,7 @@ func createPricesKeeper( }, ) - return k, storeKey, indexPriceCache, marketToSmoothedPrices, mockTimeProvider + return k, storeKey, indexPriceCache, mockTimeProvider } // CreateTestMarkets creates a standard set of test markets for testing. @@ -123,7 +118,7 @@ func CreateNMarkets(t *testing.T, ctx sdk.Context, keeper *keeper.Keeper, n int) numExistingMarkets := GetNumMarkets(t, ctx, keeper) for i := range items { items[i].Param.Id = uint32(i) + numExistingMarkets - items[i].Param.Pair = fmt.Sprintf("%v", i) + items[i].Param.Pair = fmt.Sprintf("%v-%v", i, i) items[i].Param.Exponent = int32(i) items[i].Param.ExchangeConfigJson = "" items[i].Param.MinExchanges = uint32(1) @@ -171,6 +166,17 @@ func AssertMarketEventsNotInIndexerBlock( require.Equal(t, 0, len(indexerMarketEvents)) } +// AssertNMarketEventsNotInIndexerBlock verifies that N market events were included in the Indexer block message. +func AssertNMarketEventsNotInIndexerBlock( + t *testing.T, + k *keeper.Keeper, + ctx sdk.Context, + n int, +) { + indexerMarketEvents := getMarketEventsFromIndexerBlock(ctx, k) + require.Equal(t, n, len(indexerMarketEvents)) +} + // getMarketEventsFromIndexerBlock returns the market events from the Indexer Block event Kafka message. func getMarketEventsFromIndexerBlock( ctx sdk.Context, diff --git a/protocol/testutil/keeper/rewards.go b/protocol/testutil/keeper/rewards.go index bb63a4ecd5..c467e278f4 100644 --- a/protocol/testutil/keeper/rewards.go +++ b/protocol/testutil/keeper/rewards.go @@ -42,7 +42,7 @@ func RewardsKeepers( transientStoreKey storetypes.StoreKey, ) []GenesisInitializer { // Define necessary keepers here for unit tests - pricesKeeper, _, _, _, _ = createPricesKeeper(stateStore, db, cdc, transientStoreKey) + pricesKeeper, _, _, _ = createPricesKeeper(stateStore, db, cdc, transientStoreKey) // Mock time provider response for market creation. epochsKeeper, _ := createEpochsKeeper(stateStore, db, cdc) assetsKeeper, _ = createAssetsKeeper( diff --git a/protocol/testutil/keeper/sending.go b/protocol/testutil/keeper/sending.go index 93cd6c429f..6fd275bc66 100644 --- a/protocol/testutil/keeper/sending.go +++ b/protocol/testutil/keeper/sending.go @@ -57,7 +57,7 @@ func SendingKeepersWithSubaccountsKeeper(t testing.TB, saKeeper types.Subaccount // Define necessary keepers here for unit tests epochsKeeper, _ := createEpochsKeeper(stateStore, db, cdc) blockTimeKeeper, _ := createBlockTimeKeeper(stateStore, db, cdc) - ks.PricesKeeper, _, _, _, mockTimeProvider = createPricesKeeper(stateStore, db, cdc, transientStoreKey) + ks.PricesKeeper, _, _, mockTimeProvider = createPricesKeeper(stateStore, db, cdc, transientStoreKey) ks.PerpetualsKeeper, _ = createPerpetualsKeeper( stateStore, db, diff --git a/protocol/testutil/keeper/subaccounts.go b/protocol/testutil/keeper/subaccounts.go index ee3c8ec666..07b33984b0 100644 --- a/protocol/testutil/keeper/subaccounts.go +++ b/protocol/testutil/keeper/subaccounts.go @@ -52,7 +52,7 @@ func SubaccountsKeepers( transientStoreKey storetypes.StoreKey, ) []GenesisInitializer { // Define necessary keepers here for unit tests - pricesKeeper, _, _, _, mockTimeProvider = createPricesKeeper(stateStore, db, cdc, transientStoreKey) + pricesKeeper, _, _, mockTimeProvider = createPricesKeeper(stateStore, db, cdc, transientStoreKey) epochsKeeper, _ := createEpochsKeeper(stateStore, db, cdc) perpetualsKeeper, _ = createPerpetualsKeeper(stateStore, db, cdc, pricesKeeper, epochsKeeper, transientStoreKey) assetsKeeper, _ = createAssetsKeeper(stateStore, db, cdc, pricesKeeper, transientStoreKey, msgSenderEnabled) diff --git a/protocol/testutil/memclob/keeper.go b/protocol/testutil/memclob/keeper.go index 83b368608f..6ba8856285 100644 --- a/protocol/testutil/memclob/keeper.go +++ b/protocol/testutil/memclob/keeper.go @@ -511,3 +511,9 @@ func (f *FakeMemClobKeeper) ValidateSubaccountEquityTierLimitForStatefulOrder( func (f *FakeMemClobKeeper) Logger(ctx sdk.Context) log.Logger { return ctx.Logger() } + +func (f *FakeMemClobKeeper) SendOrderbookUpdates( + offchainUpdates *types.OffchainUpdates, + snapshot bool, +) { +} diff --git a/protocol/x/clob/abci.go b/protocol/x/clob/abci.go index a2201e3035..27f4492589 100644 --- a/protocol/x/clob/abci.go +++ b/protocol/x/clob/abci.go @@ -166,6 +166,18 @@ func PrepareCheckState( offchainUpdates, ) + // For orders that are filled in the last block, send an orderbook update to the grpc streams. + if keeper.GetGrpcStreamingManager().Enabled() { + allUpdates := types.NewOffchainUpdates() + for _, orderId := range processProposerMatchesEvents.OrderIdsFilledInLastBlock { + if _, exists := keeper.MemClob.GetOrder(ctx, orderId); exists { + orderbookUpdate := keeper.MemClob.GetOrderbookUpdatesForOrderUpdate(ctx, orderId) + allUpdates.Append(orderbookUpdate) + } + } + keeper.SendOrderbookUpdates(allUpdates, false) + } + // 3. Place all stateful order placements included in the last block on the memclob. // Note telemetry is measured outside of the function call because `PlaceStatefulOrdersFromLastBlock` // is called within `PlaceConditionalOrdersTriggeredInLastBlock`. diff --git a/protocol/x/clob/abci_test.go b/protocol/x/clob/abci_test.go index 44d2d9af36..4a10197b3f 100644 --- a/protocol/x/clob/abci_test.go +++ b/protocol/x/clob/abci_test.go @@ -712,6 +712,7 @@ func TestEndBlocker_Success(t *testing.T) { constants.ClobPair_Btc.SubticksPerTick, constants.ClobPair_Btc.StepBaseQuantums, constants.BtcUsd_20PercentInitial_10PercentMaintenance.Params.LiquidityTier, + constants.BtcUsd_20PercentInitial_10PercentMaintenance.Params.MarketType, ), ), ).Once().Return() @@ -744,6 +745,7 @@ func TestEndBlocker_Success(t *testing.T) { constants.ClobPair_Eth.SubticksPerTick, constants.ClobPair_Eth.StepBaseQuantums, constants.EthUsd_20PercentInitial_10PercentMaintenance.Params.LiquidityTier, + constants.BtcUsd_20PercentInitial_10PercentMaintenance.Params.MarketType, ), ), ).Once().Return() diff --git a/protocol/x/clob/ante/rate_limit.go b/protocol/x/clob/ante/rate_limit.go index 3be0da2180..a6ba7c89e9 100644 --- a/protocol/x/clob/ante/rate_limit.go +++ b/protocol/x/clob/ante/rate_limit.go @@ -8,14 +8,15 @@ import ( var _ sdktypes.AnteDecorator = (*ClobRateLimitDecorator)(nil) // ClobRateLimitDecorator is an AnteDecorator which is responsible for rate limiting MsgCancelOrder and MsgPlaceOrder -// requests. +// and MsgBatchCancel requests. // // This AnteDecorator is a no-op if: -// - No messages in the transaction are `MsgCancelOrder` or `MsgPlaceOrder`. +// - No messages in the transaction are `MsgCancelOrder` or `MsgPlaceOrder` or `MsgBatchCancel` // // This AnteDecorator returns an error if: // - The rate limit is exceeded for any `MsgCancelOrder` messages. // - The rate limit is exceeded for any `MsgPlaceOrder` messages. +// - The rate limit is exceeded for any `MsgBatchCancel` messages. // // TODO(CLOB-721): Rate limit short term order cancellations. type ClobRateLimitDecorator struct { @@ -43,6 +44,10 @@ func (r ClobRateLimitDecorator) AnteHandle( if err = r.clobKeeper.RateLimitPlaceOrder(ctx, msg); err != nil { return ctx, err } + case *types.MsgBatchCancel: + if err = r.clobKeeper.RateLimitBatchCancel(ctx, msg); err != nil { + return ctx, err + } } } return next(ctx, tx, simulate) diff --git a/protocol/x/clob/client/cli/cancel_order_cli_test.go b/protocol/x/clob/client/cli/cancel_order_cli_test.go index 933e9082fe..8de1f7d1ff 100644 --- a/protocol/x/clob/client/cli/cancel_order_cli_test.go +++ b/protocol/x/clob/client/cli/cancel_order_cli_test.go @@ -73,6 +73,7 @@ func (s *CancelOrderIntegrationTestSuite) SetupTest() { // Disable the Bridge and Price daemons in the integration tests. appOptions.Set(daemonflags.FlagPriceDaemonEnabled, false) appOptions.Set(daemonflags.FlagBridgeDaemonEnabled, false) + appOptions.Set(daemonflags.FlagOracleEnabled, false) // Effectively disable the health monitor panic timeout for these tests. This is necessary // because all clob cli tests are running in the same process and the total time to run is >> 5 minutes @@ -207,6 +208,7 @@ func (s *CancelOrderIntegrationTestSuite) TestCLICancelPendingOrder() { s.validatorAddress, cancelsSubaccountNumberZero, clientId, + constants.ClobPair_Btc.Id, goodTilBlock, ) s.Require().NoError(err) @@ -232,6 +234,7 @@ func (s *CancelOrderIntegrationTestSuite) TestCLICancelPendingOrder() { s.validatorAddress, cancelsSubaccountNumberZero, unknownClientId, + constants.ClobPair_Btc.Id, goodTilBlock, ) s.Require().NoError(err) @@ -345,6 +348,7 @@ func (s *CancelOrderIntegrationTestSuite) TestCLICancelMatchingOrders() { s.validatorAddress, cancelsSubaccountNumberZero, clientId, + constants.ClobPair_Btc.Id, goodTilBlock, ) s.Require().NoError(err) diff --git a/protocol/x/clob/client/cli/liquidations_cli_test.go b/protocol/x/clob/client/cli/liquidations_cli_test.go index 89aaa4b2b6..4f85898114 100644 --- a/protocol/x/clob/client/cli/liquidations_cli_test.go +++ b/protocol/x/clob/client/cli/liquidations_cli_test.go @@ -74,6 +74,7 @@ func TestLiquidationOrderIntegrationTestSuite(t *testing.T) { // Disable the Bridge and Price daemons in the integration tests. appOptions.Set(daemonflags.FlagPriceDaemonEnabled, false) appOptions.Set(daemonflags.FlagBridgeDaemonEnabled, false) + appOptions.Set(daemonflags.FlagOracleEnabled, false) // Effectively disable the health monitor panic timeout for these tests. This is necessary // because all clob cli tests are running in the same process and the total time to run is >> 5 minutes diff --git a/protocol/x/clob/client/cli/place_order_cli_test.go b/protocol/x/clob/client/cli/place_order_cli_test.go index 331113edf7..92d4fdf467 100644 --- a/protocol/x/clob/client/cli/place_order_cli_test.go +++ b/protocol/x/clob/client/cli/place_order_cli_test.go @@ -68,6 +68,7 @@ func TestPlaceOrderIntegrationTestSuite(t *testing.T) { // Disable the Bridge and Price daemons in the integration tests. appOptions.Set(daemonflags.FlagPriceDaemonEnabled, false) appOptions.Set(daemonflags.FlagBridgeDaemonEnabled, false) + appOptions.Set(daemonflags.FlagOracleEnabled, false) // Effectively disable the health monitor panic timeout for these tests. This is necessary // because all clob cli tests are running in the same process and the total time to run is >> 5 minutes diff --git a/protocol/x/clob/client/cli/query_block_rate_limit_configuration_test.go b/protocol/x/clob/client/cli/query_block_rate_limit_configuration_test.go index 8faf3b636a..4aeabb226f 100644 --- a/protocol/x/clob/client/cli/query_block_rate_limit_configuration_test.go +++ b/protocol/x/clob/client/cli/query_block_rate_limit_configuration_test.go @@ -4,19 +4,21 @@ package cli_test import ( "fmt" + "testing" + tmcli "github.com/cometbft/cometbft/libs/cli" clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli" "github.com/dydxprotocol/v4-chain/protocol/x/clob/client/cli" "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" "github.com/stretchr/testify/require" - "testing" ) var ( emptyConfig = types.BlockRateLimitConfiguration{ MaxShortTermOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{}, - MaxStatefulOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{}, MaxShortTermOrderCancellationsPerNBlocks: []types.MaxPerNBlocksRateLimit{}, + MaxStatefulOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{}, + MaxShortTermOrdersAndCancelsPerNBlocks: []types.MaxPerNBlocksRateLimit{}, } ) diff --git a/protocol/x/clob/client/cli/tx.go b/protocol/x/clob/client/cli/tx.go index a1b2d99a1e..5a435abdde 100644 --- a/protocol/x/clob/client/cli/tx.go +++ b/protocol/x/clob/client/cli/tx.go @@ -27,6 +27,9 @@ func GetTxCmd() *cobra.Command { cmd.AddCommand(CmdPlaceOrder()) cmd.AddCommand(CmdCancelOrder()) + batchCancelCmd := CmdBatchCancel() + batchCancelCmd.PersistentFlags().String("clientIds", "", "A list of client ids to to batch cancel") + cmd.AddCommand(batchCancelCmd) // this line is used by starport scaffolding # 1 return cmd diff --git a/protocol/x/clob/client/cli/tx_batch_cancel.go b/protocol/x/clob/client/cli/tx_batch_cancel.go new file mode 100644 index 0000000000..3051f95358 --- /dev/null +++ b/protocol/x/clob/client/cli/tx_batch_cancel.go @@ -0,0 +1,80 @@ +package cli + +import ( + "strconv" + "strings" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" + "github.com/spf13/cast" + "github.com/spf13/cobra" +) + +func CmdBatchCancel() *cobra.Command { + cmd := &cobra.Command{ + Use: "batch-cancel owner subaccount_number clobPairId goodTilBlock --clientIds=\"\"", + Short: "Broadcast message batch cancel for a specific clobPairId", + Args: cobra.ExactArgs(4), + RunE: func(cmd *cobra.Command, args []string) (err error) { + argOwner := args[0] + clientIds, err := cmd.Flags().GetString("clientIds") + if err != nil { + return err + } + argClientIds := []uint32{} + for _, idString := range strings.Fields(clientIds) { + idUint64, err := strconv.ParseUint(idString, 10, 32) + if err != nil { + return err + } + argClientIds = append(argClientIds, uint32(idUint64)) + } + + argNumber, err := cast.ToUint32E(args[1]) + if err != nil { + return err + } + + argClobPairId, err := cast.ToUint32E(args[2]) + if err != nil { + return err + } + + argGoodTilBlock, err := cast.ToUint32E(args[3]) + if err != nil { + return err + } + + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + msg := types.NewMsgBatchCancel( + satypes.SubaccountId{ + Owner: argOwner, + Number: argNumber, + }, + []types.OrderBatch{ + { + ClobPairId: argClobPairId, + ClientIds: argClientIds, + }, + }, + argGoodTilBlock, + ) + + if err := msg.ValidateBasic(); err != nil { + return err + } + return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + + return cmd +} diff --git a/protocol/x/clob/client/cli/tx_cancel_order.go b/protocol/x/clob/client/cli/tx_cancel_order.go index 63bd2de969..303d362192 100644 --- a/protocol/x/clob/client/cli/tx_cancel_order.go +++ b/protocol/x/clob/client/cli/tx_cancel_order.go @@ -12,13 +12,13 @@ import ( func CmdCancelOrder() *cobra.Command { cmd := &cobra.Command{ - Use: "cancel-order owner number clientId goodTilBlock", - Short: "Broadcast message cancel_order", - Args: cobra.ExactArgs(4), + Use: "cancel-order owner subaccount_number clientId clobPairId goodTilBlock", + Short: "Broadcasts message cancel_order. Assumes short term order cancellation.", + Args: cobra.ExactArgs(5), RunE: func(cmd *cobra.Command, args []string) (err error) { argOwner := args[0] - argNumber, err := cast.ToUint32E(args[1]) + argSubaccountNumber, err := cast.ToUint32E(args[1]) if err != nil { return err } @@ -28,7 +28,12 @@ func CmdCancelOrder() *cobra.Command { return err } - argGoodTilBlock, err := cast.ToUint32E(args[3]) + argClobPairId, err := cast.ToUint32E(args[3]) + if err != nil { + return err + } + + argGoodTilBlock, err := cast.ToUint32E(args[4]) if err != nil { return err } @@ -40,10 +45,11 @@ func CmdCancelOrder() *cobra.Command { msg := types.NewMsgCancelOrderShortTerm( types.OrderId{ - ClientId: argClientId, + ClobPairId: argClobPairId, + ClientId: argClientId, SubaccountId: satypes.SubaccountId{ Owner: argOwner, - Number: argNumber, + Number: argSubaccountNumber, }, }, argGoodTilBlock, diff --git a/protocol/x/clob/client/cli/tx_place_order.go b/protocol/x/clob/client/cli/tx_place_order.go index 1cc3e865fb..28e9d69ff1 100644 --- a/protocol/x/clob/client/cli/tx_place_order.go +++ b/protocol/x/clob/client/cli/tx_place_order.go @@ -12,13 +12,13 @@ import ( func CmdPlaceOrder() *cobra.Command { cmd := &cobra.Command{ - Use: "place-order owner number clientId clobPairId side quantums subticks goodTilBlock", - Short: "Broadcast message place_order", + Use: "place-order owner subaccount_number clientId clobPairId side quantums subticks goodTilBlock", + Short: "Broadcast message place_order. Assumes short term order placement.", Args: cobra.ExactArgs(8), RunE: func(cmd *cobra.Command, args []string) (err error) { argOwner := args[0] - argNumber, err := cast.ToUint32E(args[1]) + argSubaccountNumber, err := cast.ToUint32E(args[1]) if err != nil { return err } @@ -64,7 +64,7 @@ func CmdPlaceOrder() *cobra.Command { ClientId: argClientId, SubaccountId: satypes.SubaccountId{ Owner: argOwner, - Number: argNumber, + Number: argSubaccountNumber, }, ClobPairId: argClobPairId, }, diff --git a/protocol/x/clob/client/testutil/cli_helpers.go b/protocol/x/clob/client/testutil/cli_helpers.go index 4da6cab379..d2247fdf3f 100644 --- a/protocol/x/clob/client/testutil/cli_helpers.go +++ b/protocol/x/clob/client/testutil/cli_helpers.go @@ -55,12 +55,14 @@ func MsgCancelOrderExec( owner sdk.AccAddress, number uint32, clientId uint64, + clobPairId uint32, goodTilBlock uint32, ) (testutil.BufferWriter, error) { args := []string{ owner.String(), fmt.Sprint(number), fmt.Sprint(clientId), + fmt.Sprint(clobPairId), fmt.Sprint(goodTilBlock), } diff --git a/protocol/x/clob/e2e/app_test.go b/protocol/x/clob/e2e/app_test.go index 33037954fb..e2ed8f1952 100644 --- a/protocol/x/clob/e2e/app_test.go +++ b/protocol/x/clob/e2e/app_test.go @@ -53,6 +53,26 @@ var ( }, testapp.DefaultGenesis(), )) + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB23 = *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: 23}, + }, + testapp.DefaultGenesis(), + )) + PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB24 = *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: 24}, + }, + testapp.DefaultGenesis(), + )) PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB27 = *clobtypes.NewMsgPlaceOrder(testapp.MustScaleOrder( clobtypes.Order{ OrderId: clobtypes.OrderId{SubaccountId: constants.Alice_Num0, ClientId: 0, ClobPairId: 0}, @@ -158,6 +178,14 @@ var ( }, 27, ) + CancelOrder_Alice_Num0_Id0_Clob0_GTB23 = *clobtypes.NewMsgCancelOrderShortTerm( + clobtypes.OrderId{ + SubaccountId: constants.Alice_Num0, + ClientId: 0, + ClobPairId: 0, + }, + 23, + ) CancelOrder_Alice_Num1_Id0_Clob0_GTB20 = *clobtypes.NewMsgCancelOrderShortTerm( clobtypes.OrderId{ SubaccountId: constants.Alice_Num1, @@ -166,6 +194,14 @@ var ( }, 20, ) + CancelOrder_Alice_Num0_Id1_Clob0_GTB20 = *clobtypes.NewMsgCancelOrderShortTerm( + clobtypes.OrderId{ + SubaccountId: constants.Alice_Num0, + ClientId: 0, + ClobPairId: 1, + }, + 20, + ) PlaceOrder_Bob_Num0_Id0_Clob0_Sell5_Price10_GTB20 = *clobtypes.NewMsgPlaceOrder(testapp.MustScaleOrder( clobtypes.Order{ OrderId: clobtypes.OrderId{SubaccountId: constants.Bob_Num0, ClientId: 0, ClobPairId: 0}, @@ -223,6 +259,57 @@ var ( constants.ConditionalOrder_Alice_Num1_Id0_Clob0_Sell5_Price10_GTB15, testapp.DefaultGenesis(), )) + + BatchCancel_Alice_Num0_Clob0_1_2_3_GTB5 = *clobtypes.NewMsgBatchCancel( + constants.Alice_Num0, + []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{1, 2, 3}, + }, + }, + 5, + ) + BatchCancel_Alice_Num0_Clob0_1_2_3_GTB27 = *clobtypes.NewMsgBatchCancel( + constants.Alice_Num0, + []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{1, 2, 3}, + }, + }, + 27, + ) + BatchCancel_Alice_Num0_Clob0_1_2_3_GTB20 = *clobtypes.NewMsgBatchCancel( + constants.Alice_Num0, + []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{1, 2, 3}, + }, + }, + 20, + ) + BatchCancel_Alice_Num0_Clob1_1_2_3_GTB20 = *clobtypes.NewMsgBatchCancel( + constants.Alice_Num0, + []clobtypes.OrderBatch{ + { + ClobPairId: 1, + ClientIds: []uint32{1, 2, 3}, + }, + }, + 20, + ) + BatchCancel_Alice_Num1_Clob0_1_2_3_GTB20 = *clobtypes.NewMsgBatchCancel( + constants.Alice_Num1, + []clobtypes.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{1, 2, 3}, + }, + }, + 20, + ) ) // We place 300 orders that match and 700 orders followed by their cancellations concurrently. diff --git a/protocol/x/clob/e2e/rate_limit_test.go b/protocol/x/clob/e2e/rate_limit_test.go index 8ad5a348a9..f88e1a1e51 100644 --- a/protocol/x/clob/e2e/rate_limit_test.go +++ b/protocol/x/clob/e2e/rate_limit_test.go @@ -1,9 +1,10 @@ package clob_test import ( - satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" "testing" + satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" + abcitypes "github.com/cometbft/cometbft/abci/types" sdktypes "github.com/cosmos/cosmos-sdk/types" @@ -18,13 +19,13 @@ import ( func TestRateLimitingOrders_RateLimitsAreEnforced(t *testing.T) { tests := map[string]struct { - blockRateLimitConifg clobtypes.BlockRateLimitConfiguration + blockRateLimitConfig clobtypes.BlockRateLimitConfiguration firstMsg sdktypes.Msg secondMsg sdktypes.Msg }{ "Short term orders with same subaccounts": { - blockRateLimitConifg: clobtypes.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + blockRateLimitConfig: clobtypes.BlockRateLimitConfiguration{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ { NumBlocks: 2, Limit: 1, @@ -35,8 +36,8 @@ func TestRateLimitingOrders_RateLimitsAreEnforced(t *testing.T) { secondMsg: &PlaceOrder_Alice_Num0_Id0_Clob1_Buy5_Price10_GTB20, }, "Short term orders with different subaccounts": { - blockRateLimitConifg: clobtypes.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + blockRateLimitConfig: clobtypes.BlockRateLimitConfiguration{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ { NumBlocks: 2, Limit: 1, @@ -47,7 +48,7 @@ func TestRateLimitingOrders_RateLimitsAreEnforced(t *testing.T) { secondMsg: &PlaceOrder_Alice_Num1_Id0_Clob0_Buy5_Price10_GTB20, }, "Stateful orders with same subaccounts": { - blockRateLimitConifg: clobtypes.BlockRateLimitConfiguration{ + blockRateLimitConfig: clobtypes.BlockRateLimitConfiguration{ MaxStatefulOrdersPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ { NumBlocks: 2, @@ -59,7 +60,7 @@ func TestRateLimitingOrders_RateLimitsAreEnforced(t *testing.T) { secondMsg: &LongTermPlaceOrder_Alice_Num0_Id0_Clob1_Buy5_Price10_GTBT5, }, "Stateful orders with different subaccounts": { - blockRateLimitConifg: clobtypes.BlockRateLimitConfiguration{ + blockRateLimitConfig: clobtypes.BlockRateLimitConfiguration{ MaxStatefulOrdersPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ { NumBlocks: 2, @@ -71,8 +72,8 @@ func TestRateLimitingOrders_RateLimitsAreEnforced(t *testing.T) { secondMsg: &LongTermPlaceOrder_Alice_Num1_Id0_Clob0_Buy5_Price10_GTBT5, }, "Short term order cancellations with same subaccounts": { - blockRateLimitConifg: clobtypes.BlockRateLimitConfiguration{ - MaxShortTermOrderCancellationsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + blockRateLimitConfig: clobtypes.BlockRateLimitConfiguration{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ { NumBlocks: 2, Limit: 1, @@ -83,8 +84,8 @@ func TestRateLimitingOrders_RateLimitsAreEnforced(t *testing.T) { secondMsg: &CancelOrder_Alice_Num0_Id0_Clob0_GTB20, }, "Short term order cancellations with different subaccounts": { - blockRateLimitConifg: clobtypes.BlockRateLimitConfiguration{ - MaxShortTermOrderCancellationsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + blockRateLimitConfig: clobtypes.BlockRateLimitConfiguration{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ { NumBlocks: 2, Limit: 1, @@ -94,6 +95,30 @@ func TestRateLimitingOrders_RateLimitsAreEnforced(t *testing.T) { firstMsg: &CancelOrder_Alice_Num0_Id0_Clob1_GTB5, secondMsg: &CancelOrder_Alice_Num1_Id0_Clob0_GTB20, }, + "Batch cancellations with same subaccounts": { + blockRateLimitConfig: clobtypes.BlockRateLimitConfiguration{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + { + NumBlocks: 2, + Limit: 2, + }, + }, + }, + firstMsg: &BatchCancel_Alice_Num0_Clob0_1_2_3_GTB5, + secondMsg: &BatchCancel_Alice_Num0_Clob0_1_2_3_GTB20, + }, + "Batch cancellations with different subaccounts": { + blockRateLimitConfig: clobtypes.BlockRateLimitConfiguration{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + { + NumBlocks: 2, + Limit: 2, + }, + }, + }, + firstMsg: &BatchCancel_Alice_Num0_Clob0_1_2_3_GTB5, + secondMsg: &BatchCancel_Alice_Num1_Clob0_1_2_3_GTB20, + }, } for name, tc := range tests { @@ -106,7 +131,7 @@ func TestRateLimitingOrders_RateLimitsAreEnforced(t *testing.T) { testapp.UpdateGenesisDocWithAppStateForModule( &genesis, func(genesisState *clobtypes.GenesisState) { - genesisState.BlockRateLimitConfig = tc.blockRateLimitConifg + genesisState.BlockRateLimitConfig = tc.blockRateLimitConfig }, ) testapp.UpdateGenesisDocWithAppStateForModule( @@ -146,21 +171,21 @@ func TestRateLimitingOrders_RateLimitsAreEnforced(t *testing.T) { resp = tApp.CheckTx(secondCheckTx) require.Conditionf(t, resp.IsErr, "Expected CheckTx to error. Response: %+v", resp) require.Equal(t, clobtypes.ErrBlockRateLimitExceeded.ABCICode(), resp.Code) - require.Contains(t, resp.Log, "Rate of 2 exceeds configured block rate limit") + require.Contains(t, resp.Log, "exceeds configured block rate limit") // Rate limit of 1 over two blocks should still apply, total should be 3 now (2 in block 2, 1 in block 3). tApp.AdvanceToBlock(3, testapp.AdvanceToBlockOptions{}) resp = tApp.CheckTx(secondCheckTx) require.Conditionf(t, resp.IsErr, "Expected CheckTx to error. Response: %+v", resp) require.Equal(t, clobtypes.ErrBlockRateLimitExceeded.ABCICode(), resp.Code) - require.Contains(t, resp.Log, "Rate of 3 exceeds configured block rate limit") + require.Contains(t, resp.Log, "exceeds configured block rate limit") // Rate limit of 1 over two blocks should still apply, total should be 2 now (1 in block 3, 1 in block 4). tApp.AdvanceToBlock(4, testapp.AdvanceToBlockOptions{}) resp = tApp.CheckTx(secondCheckTx) require.Conditionf(t, resp.IsErr, "Expected CheckTx to error. Response: %+v", resp) require.Equal(t, clobtypes.ErrBlockRateLimitExceeded.ABCICode(), resp.Code) - require.Contains(t, resp.Log, "Rate of 2 exceeds configured block rate limit") + require.Contains(t, resp.Log, "exceeds configured block rate limit") // Advancing two blocks should make the total count 0 now and the msg should be accepted. tApp.AdvanceToBlock(6, testapp.AdvanceToBlockOptions{}) @@ -170,6 +195,180 @@ func TestRateLimitingOrders_RateLimitsAreEnforced(t *testing.T) { } } +func TestCombinedPlaceCancelBatchCancel_RateLimitsAreEnforced(t *testing.T) { + tests := map[string]struct { + blockRateLimitConfig clobtypes.BlockRateLimitConfiguration + firstBatch []sdktypes.Msg + secondBatch []sdktypes.Msg + thirdBatch []sdktypes.Msg + firstBatchSuccess []bool + secondBatchSuccess []bool + thirdBatchSuccess []bool + lastOrder sdktypes.Msg + }{ + "Combination Place, Cancel, BatchCancel orders": { + blockRateLimitConfig: clobtypes.BlockRateLimitConfiguration{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + { + NumBlocks: 2, + Limit: 6, // TODO FIX THIS AFTER SETTLE ON A NUM + }, + }, + }, + firstBatch: []sdktypes.Msg{ + &PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB20, // 1-weight success @ 1 + &PlaceOrder_Alice_Num0_Id0_Clob1_Buy5_Price10_GTB20, // 1-weight success @ 2 + &CancelOrder_Alice_Num0_Id0_Clob0_GTB20, // 1-weight success @ 3 + }, + firstBatchSuccess: []bool{ + true, + true, + true, + }, + secondBatch: []sdktypes.Msg{ + &PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB23, // 1-weight success @ 4 + &CancelOrder_Alice_Num1_Id0_Clob0_GTB20, // 1-weight success @ 5 + &BatchCancel_Alice_Num0_Clob0_1_2_3_GTB20, // 2-weight failure @ 7 + &CancelOrder_Alice_Num0_Id0_Clob0_GTB23, // 1-weight failure @ 8 + }, + secondBatchSuccess: []bool{ + true, + true, + false, + false, + }, + // advance one block, subtract 3 for a count of 5 + thirdBatch: []sdktypes.Msg{ + &PlaceOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTB24, // 1-weight success @ 6 + &BatchCancel_Alice_Num0_Clob0_1_2_3_GTB20, // 2-weight failure @ 8 + &CancelOrder_Alice_Num0_Id0_Clob0_GTB20, // 1-weight failure @ 9 + }, + thirdBatchSuccess: []bool{ + true, + false, + false, + }, + // advance one block, subtract 5 for a count of 4 + lastOrder: &BatchCancel_Alice_Num1_Clob0_1_2_3_GTB20, // 2-weight pass @ 6 + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t). + // Disable non-determinism checks since we mutate keeper state directly. + WithNonDeterminismChecksEnabled(false). + WithGenesisDocFn(func() (genesis types.GenesisDoc) { + genesis = testapp.DefaultGenesis() + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *clobtypes.GenesisState) { + genesisState.BlockRateLimitConfig = tc.blockRateLimitConfig + }, + ) + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *satypes.GenesisState) { + genesisState.Subaccounts = []satypes.Subaccount{ + constants.Alice_Num0_10_000USD, + constants.Alice_Num1_10_000USD, + } + }) + return genesis + }).Build() + ctx := tApp.InitChain() + + firstCheckTxArray := []abcitypes.RequestCheckTx{} + for _, msg := range tc.firstBatch { + checkTx := testapp.MustMakeCheckTx( + ctx, + tApp.App, + testapp.MustMakeCheckTxOptions{ + AccAddressForSigning: testtx.MustGetOnlySignerAddress(tApp.App.AppCodec(), msg), + }, + msg, + ) + firstCheckTxArray = append(firstCheckTxArray, checkTx) + } + secondCheckTxArray := []abcitypes.RequestCheckTx{} + for _, msg := range tc.secondBatch { + checkTx := testapp.MustMakeCheckTx( + ctx, + tApp.App, + testapp.MustMakeCheckTxOptions{ + AccAddressForSigning: testtx.MustGetOnlySignerAddress(tApp.App.AppCodec(), msg), + }, + msg, + ) + secondCheckTxArray = append(secondCheckTxArray, checkTx) + } + thirdCheckTxArray := []abcitypes.RequestCheckTx{} + for _, msg := range tc.thirdBatch { + checkTx := testapp.MustMakeCheckTx( + ctx, + tApp.App, + testapp.MustMakeCheckTxOptions{ + AccAddressForSigning: testtx.MustGetOnlySignerAddress(tApp.App.AppCodec(), msg), + }, + msg, + ) + thirdCheckTxArray = append(thirdCheckTxArray, checkTx) + } + + tApp.AdvanceToBlock(2, testapp.AdvanceToBlockOptions{}) + // First batch of transactions. + for idx, checkTx := range firstCheckTxArray { + resp := tApp.CheckTx(checkTx) + shouldSucceed := tc.firstBatchSuccess[idx] + if shouldSucceed { + require.Conditionf(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp) + } else { + require.Equal(t, clobtypes.ErrBlockRateLimitExceeded.ABCICode(), resp.Code) + require.Contains(t, resp.Log, "exceeds configured block rate limit") + } + } + // Advance one block + tApp.AdvanceToBlock(3, testapp.AdvanceToBlockOptions{}) + // Second batch of transactions. + for idx, checkTx := range secondCheckTxArray { + resp := tApp.CheckTx(checkTx) + shouldSucceed := tc.secondBatchSuccess[idx] + if shouldSucceed { + require.Conditionf(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp) + } else { + require.Equal(t, clobtypes.ErrBlockRateLimitExceeded.ABCICode(), resp.Code) + require.Contains(t, resp.Log, "exceeds configured block rate limit") + } + } + // Advance one block + tApp.AdvanceToBlock(4, testapp.AdvanceToBlockOptions{}) + // Third batch of transactions. + for idx, checkTx := range thirdCheckTxArray { + resp := tApp.CheckTx(checkTx) + shouldSucceed := tc.thirdBatchSuccess[idx] + if shouldSucceed { + require.Conditionf(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp) + } else { + require.Equal(t, clobtypes.ErrBlockRateLimitExceeded.ABCICode(), resp.Code) + require.Contains(t, resp.Log, "exceeds configured block rate limit") + } + } + // Advance one block + tApp.AdvanceToBlock(5, testapp.AdvanceToBlockOptions{}) + lastCheckTx := testapp.MustMakeCheckTx( + ctx, + tApp.App, + testapp.MustMakeCheckTxOptions{ + AccAddressForSigning: testtx.MustGetOnlySignerAddress(tApp.App.AppCodec(), tc.lastOrder), + }, + tc.lastOrder, + ) + resp := tApp.CheckTx(lastCheckTx) + require.Conditionf(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp) + }) + } +} + func TestCancellationAndMatchInTheSameBlock_Regression(t *testing.T) { tApp := testapp.NewTestAppBuilder(t).Build() @@ -487,7 +686,7 @@ func TestRateLimitingShortTermOrders_GuardedAgainstReplayAttacks(t *testing.T) { }{ "Short term order placements": { blockRateLimitConfig: clobtypes.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ { NumBlocks: 1, Limit: 1, @@ -501,7 +700,7 @@ func TestRateLimitingShortTermOrders_GuardedAgainstReplayAttacks(t *testing.T) { }, "Short term order cancellations": { blockRateLimitConfig: clobtypes.BlockRateLimitConfiguration{ - MaxShortTermOrderCancellationsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ { NumBlocks: 1, Limit: 1, @@ -511,7 +710,21 @@ func TestRateLimitingShortTermOrders_GuardedAgainstReplayAttacks(t *testing.T) { replayLessGTB: &CancelOrder_Alice_Num0_Id0_Clob0_GTB5, replayGreaterGTB: &CancelOrder_Alice_Num0_Id0_Clob0_GTB27, firstValidGTB: &CancelOrder_Alice_Num0_Id0_Clob0_GTB20, - secondValidGTB: &CancelOrder_Alice_Num1_Id0_Clob0_GTB20, + secondValidGTB: &CancelOrder_Alice_Num0_Id1_Clob0_GTB20, + }, + "Batch cancellations": { + blockRateLimitConfig: clobtypes.BlockRateLimitConfiguration{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + { + NumBlocks: 1, + Limit: 2, + }, + }, + }, + replayLessGTB: &BatchCancel_Alice_Num0_Clob0_1_2_3_GTB5, + replayGreaterGTB: &BatchCancel_Alice_Num0_Clob0_1_2_3_GTB27, + firstValidGTB: &BatchCancel_Alice_Num0_Clob0_1_2_3_GTB20, + secondValidGTB: &BatchCancel_Alice_Num0_Clob1_1_2_3_GTB20, }, } @@ -537,6 +750,7 @@ func TestRateLimitingShortTermOrders_GuardedAgainstReplayAttacks(t *testing.T) { }).Build() ctx := tApp.AdvanceToBlock(5, testapp.AdvanceToBlockOptions{}) + // First tx fails due to GTB being too low. replayLessGTBTx := testapp.MustMakeCheckTx( ctx, tApp.App, @@ -549,6 +763,7 @@ func TestRateLimitingShortTermOrders_GuardedAgainstReplayAttacks(t *testing.T) { require.Conditionf(t, resp.IsErr, "Expected CheckTx to error. Response: %+v", resp) require.Equal(t, clobtypes.ErrHeightExceedsGoodTilBlock.ABCICode(), resp.Code) + // Second tx fails due to GTB being too high. replayGreaterGTBTx := testapp.MustMakeCheckTx( ctx, tApp.App, @@ -569,7 +784,8 @@ func TestRateLimitingShortTermOrders_GuardedAgainstReplayAttacks(t *testing.T) { }, tc.firstValidGTB, ) - // First transaction should be allowed. + // First transaction should be allowed due to GTB being valid. The first two tx do not count towards + // the rate limit. resp = tApp.CheckTx(firstCheckTx) require.Conditionf(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp) @@ -585,7 +801,7 @@ func TestRateLimitingShortTermOrders_GuardedAgainstReplayAttacks(t *testing.T) { resp = tApp.CheckTx(secondCheckTx) require.Conditionf(t, resp.IsErr, "Expected CheckTx to error. Response: %+v", resp) require.Equal(t, clobtypes.ErrBlockRateLimitExceeded.ABCICode(), resp.Code) - require.Contains(t, resp.Log, "Rate of 2 exceeds configured block rate limit") + require.Contains(t, resp.Log, "exceeds configured block rate limit") }) } } diff --git a/protocol/x/clob/genesis_test.go b/protocol/x/clob/genesis_test.go index 852a32a562..e84d54cd5d 100644 --- a/protocol/x/clob/genesis_test.go +++ b/protocol/x/clob/genesis_test.go @@ -1,9 +1,10 @@ package clob_test import ( - errorsmod "cosmossdk.io/errors" "testing" + errorsmod "cosmossdk.io/errors" + indexerevents "github.com/dydxprotocol/v4-chain/protocol/indexer/events" "github.com/dydxprotocol/v4-chain/protocol/indexer/indexer_manager" clobtest "github.com/dydxprotocol/v4-chain/protocol/testutil/clob" @@ -34,13 +35,7 @@ func TestGenesis(t *testing.T) { "Genesis state is valid": { genesis: types.GenesisState{ BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{ - { - Limit: 200, - NumBlocks: 1, - }, - }, - MaxShortTermOrderCancellationsPerNBlocks: []types.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []types.MaxPerNBlocksRateLimit{ { Limit: 200, NumBlocks: 1, @@ -377,7 +372,7 @@ func TestGenesis(t *testing.T) { "Genesis state is invalid when BlockRateLimitConfiguration is invalid": { genesis: types.GenesisState{ BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []types.MaxPerNBlocksRateLimit{ { Limit: 1, NumBlocks: 0, @@ -394,7 +389,7 @@ func TestGenesis(t *testing.T) { SubaccountBlockLimits: constants.SubaccountBlockLimits_Default, }, }, - expectedErr: "0 is not a valid NumBlocks for MaxShortTermOrdersPerNBlocks rate limit " + + expectedErr: "0 is not a valid NumBlocks for MaxShortTermOrdersAndCancelsPerNBlocks rate limit " + "{NumBlocks:0 Limit:1}", expectedErrType: types.ErrInvalidBlockRateLimitConfig, }, @@ -456,6 +451,7 @@ func TestGenesis(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, perpetual.Params.LiquidityTier, + perpetual.Params.MarketType, ), ), ).Once().Return() diff --git a/protocol/x/clob/keeper/block_rate_limit_config.go b/protocol/x/clob/keeper/block_rate_limit_config.go index 205694d9a3..035ffa875f 100644 --- a/protocol/x/clob/keeper/block_rate_limit_config.go +++ b/protocol/x/clob/keeper/block_rate_limit_config.go @@ -37,8 +37,7 @@ func (k *Keeper) InitalizeBlockRateLimitFromStateIfExists(ctx sdk.Context) { var config types.BlockRateLimitConfiguration k.cdc.MustUnmarshal(b, &config) - k.placeOrderRateLimiter = rate_limit.NewPlaceOrderRateLimiter(config) - k.cancelOrderRateLimiter = rate_limit.NewCancelOrderRateLimiter(config) + k.placeCancelOrderRateLimiter = rate_limit.NewPlaceCancelOrderRateLimiter(config) } // InitializeBlockRateLimit initializes the block rate limit configuration in state and uses @@ -61,8 +60,7 @@ func (k *Keeper) InitializeBlockRateLimit( b := k.cdc.MustMarshal(&config) store.Set([]byte(types.BlockRateLimitConfigKey), b) - k.placeOrderRateLimiter = rate_limit.NewPlaceOrderRateLimiter(config) - k.cancelOrderRateLimiter = rate_limit.NewCancelOrderRateLimiter(config) + k.placeCancelOrderRateLimiter = rate_limit.NewPlaceCancelOrderRateLimiter(config) return nil } diff --git a/protocol/x/clob/keeper/clob_pair.go b/protocol/x/clob/keeper/clob_pair.go index 8ab56f976e..12a3f6fef1 100644 --- a/protocol/x/clob/keeper/clob_pair.go +++ b/protocol/x/clob/keeper/clob_pair.go @@ -1,10 +1,11 @@ package keeper import ( - storetypes "cosmossdk.io/store/types" "fmt" "sort" + storetypes "cosmossdk.io/store/types" + errorsmod "cosmossdk.io/errors" "cosmossdk.io/store/prefix" @@ -104,6 +105,7 @@ func (k Keeper) CreatePerpetualClobPair( subticksPerTick, stepSizeBaseQuantums.ToUint64(), perpetual.Params.LiquidityTier, + perpetual.Params.MarketType, ), ), ) diff --git a/protocol/x/clob/keeper/clob_pair_test.go b/protocol/x/clob/keeper/clob_pair_test.go index 87c69356b7..4ae6fb3e14 100644 --- a/protocol/x/clob/keeper/clob_pair_test.go +++ b/protocol/x/clob/keeper/clob_pair_test.go @@ -66,6 +66,7 @@ func TestCreatePerpetualClobPair_MultiplePerpetual(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, constants.Perpetuals_DefaultGenesisState.Perpetuals[i].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[i].Params.MarketType, ), ), ).Once().Return() @@ -187,6 +188,7 @@ func TestCreatePerpetualClobPair_FailsWithDuplicateClobPairId(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketType, ), ), ).Once().Return() @@ -278,6 +280,7 @@ func TestCreatePerpetualClobPair(t *testing.T) { tc.clobPair.SubticksPerTick, tc.clobPair.StepBaseQuantums, perpetual.Params.LiquidityTier, + perpetual.Params.MarketType, ), ), ).Return() @@ -430,6 +433,7 @@ func TestCreateMultipleClobPairs(t *testing.T) { make.clobPair.SubticksPerTick, make.clobPair.StepBaseQuantums, perpetual.Params.LiquidityTier, + perpetual.Params.MarketType, ), ), ).Return() @@ -621,6 +625,7 @@ func TestUpdateClobPair_FinalSettlement(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketType, ), ), ).Once().Return() @@ -737,6 +742,7 @@ func TestUpdateClobPair(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketType, ), ), ).Once().Return() @@ -794,6 +800,7 @@ func TestUpdateClobPair(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketType, ), ), ).Once().Return() @@ -831,6 +838,7 @@ func TestUpdateClobPair(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketType, ), ), ).Once().Return() diff --git a/protocol/x/clob/keeper/deleveraging_test.go b/protocol/x/clob/keeper/deleveraging_test.go index 1bfeda123a..fa13dd960e 100644 --- a/protocol/x/clob/keeper/deleveraging_test.go +++ b/protocol/x/clob/keeper/deleveraging_test.go @@ -413,6 +413,7 @@ func TestCanDeleverageSubaccount(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, perpetuals[i].Params.LiquidityTier, + perpetuals[i].Params.MarketType, ), ), ).Once().Return() @@ -786,6 +787,7 @@ func TestOffsetSubaccountPerpetualPosition(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, perps[i].Params.LiquidityTier, + perps[i].Params.MarketType, ), ), ).Once().Return() diff --git a/protocol/x/clob/keeper/get_price_premium_test.go b/protocol/x/clob/keeper/get_price_premium_test.go index 43ca311246..20afc053c5 100644 --- a/protocol/x/clob/keeper/get_price_premium_test.go +++ b/protocol/x/clob/keeper/get_price_premium_test.go @@ -183,6 +183,7 @@ func TestGetPricePremiumForPerpetual(t *testing.T) { tc.args.clobPair.SubticksPerTick, tc.args.clobPair.StepBaseQuantums, perpetual.Params.LiquidityTier, + perpetual.Params.MarketType, ), ), ).Return() diff --git a/protocol/x/clob/keeper/grpc_query_block_rate_limit_configuration_test.go b/protocol/x/clob/keeper/grpc_query_block_rate_limit_configuration_test.go index 1d489dfefa..56ffcb8531 100644 --- a/protocol/x/clob/keeper/grpc_query_block_rate_limit_configuration_test.go +++ b/protocol/x/clob/keeper/grpc_query_block_rate_limit_configuration_test.go @@ -1,12 +1,13 @@ package keeper_test import ( + "testing" + testApp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - "testing" ) func TestGetBlockRateLimitConfiguration(t *testing.T) { @@ -19,10 +20,10 @@ func TestGetBlockRateLimitConfiguration(t *testing.T) { req: &types.QueryBlockRateLimitConfigurationRequest{}, res: &types.QueryBlockRateLimitConfigurationResponse{ BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []types.MaxPerNBlocksRateLimit{ { NumBlocks: 1, - Limit: 200, + Limit: 400, }, }, MaxStatefulOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{ @@ -35,12 +36,6 @@ func TestGetBlockRateLimitConfiguration(t *testing.T) { Limit: 20, }, }, - MaxShortTermOrderCancellationsPerNBlocks: []types.MaxPerNBlocksRateLimit{ - { - NumBlocks: 1, - Limit: 200, - }, - }, }, }, }, diff --git a/protocol/x/clob/keeper/keeper.go b/protocol/x/clob/keeper/keeper.go index 370ef8d3ac..e6e34619ac 100644 --- a/protocol/x/clob/keeper/keeper.go +++ b/protocol/x/clob/keeper/keeper.go @@ -56,8 +56,7 @@ type ( // Note that the antehandler is not set until after the BaseApp antehandler is also set. antehandler sdk.AnteHandler - placeOrderRateLimiter rate_limit.RateLimiter[*types.MsgPlaceOrder] - cancelOrderRateLimiter rate_limit.RateLimiter[*types.MsgCancelOrder] + placeCancelOrderRateLimiter rate_limit.RateLimiter[sdk.Msg] DaemonLiquidationInfo *liquidationtypes.DaemonLiquidationInfo } @@ -88,8 +87,7 @@ func NewKeeper( grpcStreamingManager streamingtypes.GrpcStreamingManager, txDecoder sdk.TxDecoder, clobFlags flags.ClobFlags, - placeOrderRateLimiter rate_limit.RateLimiter[*types.MsgPlaceOrder], - cancelOrderRateLimiter rate_limit.RateLimiter[*types.MsgCancelOrder], + placeCancelOrderRateLimiter rate_limit.RateLimiter[sdk.Msg], daemonLiquidationInfo *liquidationtypes.DaemonLiquidationInfo, ) *Keeper { keeper := &Keeper{ @@ -119,10 +117,9 @@ func NewKeeper( Hosts: clobFlags.MevTelemetryHosts, Identifier: clobFlags.MevTelemetryIdentifier, }, - Flags: clobFlags, - placeOrderRateLimiter: placeOrderRateLimiter, - cancelOrderRateLimiter: cancelOrderRateLimiter, - DaemonLiquidationInfo: daemonLiquidationInfo, + Flags: clobFlags, + placeCancelOrderRateLimiter: placeCancelOrderRateLimiter, + DaemonLiquidationInfo: daemonLiquidationInfo, } // Provide the keeper to the MemClob. @@ -236,5 +233,17 @@ func (k Keeper) InitializeNewGrpcStreams(ctx sdk.Context) { allUpdates.Append(update) } - streamingManager.SendOrderbookUpdates(allUpdates, true) + k.SendOrderbookUpdates(allUpdates, true) +} + +// SendOrderbookUpdates sends the offchain updates to the gRPC streaming manager. +func (k Keeper) SendOrderbookUpdates( + offchainUpdates *types.OffchainUpdates, + snapshot bool, +) { + if len(offchainUpdates.Messages) == 0 { + return + } + + k.GetGrpcStreamingManager().SendOrderbookUpdates(offchainUpdates, snapshot) } diff --git a/protocol/x/clob/keeper/liquidations_test.go b/protocol/x/clob/keeper/liquidations_test.go index ec916312e1..799e922321 100644 --- a/protocol/x/clob/keeper/liquidations_test.go +++ b/protocol/x/clob/keeper/liquidations_test.go @@ -1177,6 +1177,7 @@ func TestPlacePerpetualLiquidation_PreexistingLiquidation(t *testing.T) { constants.ClobPair_Btc.SubticksPerTick, constants.ClobPair_Btc.StepBaseQuantums, constants.BtcUsd_100PercentMarginRequirement.Params.LiquidityTier, + constants.BtcUsd_100PercentMarginRequirement.Params.MarketType, ), ), ).Once().Return() @@ -1206,6 +1207,7 @@ func TestPlacePerpetualLiquidation_PreexistingLiquidation(t *testing.T) { constants.ClobPair_Eth.SubticksPerTick, constants.ClobPair_Eth.StepBaseQuantums, constants.EthUsd_100PercentMarginRequirement.Params.LiquidityTier, + constants.EthUsd_100PercentMarginRequirement.Params.MarketType, ), ), ).Once().Return() @@ -2088,6 +2090,7 @@ func TestPlacePerpetualLiquidation_Deleveraging(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, perpetuals[i].Params.LiquidityTier, + perpetuals[i].Params.MarketType, ), ), ).Once().Return() @@ -2205,6 +2208,7 @@ func TestPlacePerpetualLiquidation_SendOffchainMessages(t *testing.T) { constants.ClobPair_Btc.SubticksPerTick, constants.ClobPair_Btc.StepBaseQuantums, constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketType, ), ), ).Once().Return() @@ -3730,6 +3734,7 @@ func TestGetLiquidationInsuranceFundDelta(t *testing.T) { constants.ClobPair_Btc.SubticksPerTick, constants.ClobPair_Btc.StepBaseQuantums, tc.perpetuals[0].Params.LiquidityTier, + tc.perpetuals[0].Params.MarketType, ), ), ).Once().Return() @@ -4469,6 +4474,7 @@ func TestGetPerpetualPositionToLiquidate(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, tc.perpetuals[perpetualId].Params.LiquidityTier, + tc.perpetuals[perpetualId].Params.MarketType, ), ), ).Once().Return() @@ -5034,6 +5040,7 @@ func TestGetMaxAndMinPositionNotionalLiquidatable(t *testing.T) { constants.ClobPair_Btc.SubticksPerTick, constants.ClobPair_Btc.StepBaseQuantums, constants.BtcUsd_100PercentMarginRequirement.Params.LiquidityTier, + constants.BtcUsd_100PercentMarginRequirement.Params.MarketType, ), ), ).Once().Return() @@ -5188,6 +5195,7 @@ func TestSortLiquidationOrders(t *testing.T) { constants.ClobPair_Btc.SubticksPerTick, constants.ClobPair_Btc.StepBaseQuantums, constants.BtcUsd_100PercentMarginRequirement.Params.LiquidityTier, + constants.BtcUsd_100PercentMarginRequirement.Params.MarketType, ), ), ).Once().Return() diff --git a/protocol/x/clob/keeper/msg_server_cancel_orders.go b/protocol/x/clob/keeper/msg_server_cancel_orders.go index 6765da2945..cd241c572e 100644 --- a/protocol/x/clob/keeper/msg_server_cancel_orders.go +++ b/protocol/x/clob/keeper/msg_server_cancel_orders.go @@ -5,7 +5,6 @@ import ( "errors" errorsmod "cosmossdk.io/errors" - "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" indexerevents "github.com/dydxprotocol/v4-chain/protocol/indexer/events" @@ -24,6 +23,23 @@ func (k msgServer) CancelOrder( ) (resp *types.MsgCancelOrderResponse, err error) { ctx := lib.UnwrapSDKContext(goCtx, types.ModuleName) + if err := k.Keeper.HandleMsgCancelOrder(ctx, msg); err != nil { + return nil, err + } + + return &types.MsgCancelOrderResponse{}, nil +} + +// HandleMsgCancelOrder handles a MsgCancelOrder by +// 1. persisting the cancellation on chain. +// 2. updating ProcessProposerMatchesEvents with the new stateful order cancellation. +// 3. adding order cancellation on-chain indexer event. +func (k Keeper) HandleMsgCancelOrder( + ctx sdk.Context, + msg *types.MsgCancelOrder, +) (err error) { + lib.AssertDeliverTxMode(ctx) + // Attach various logging tags relative to this request. These should be static with no changes. ctx = log.AddPersistentTagsToLogger(ctx, log.Module, log.Clob, @@ -48,7 +64,7 @@ func (k msgServer) CancelOrder( // the order, it has already been removed from state due to errors encountered while matching. // TODO(CLOB-778): Prevent invalid MsgCancelOrder messages from being included in the block. if errors.Is(err, types.ErrStatefulOrderDoesNotExist) { - processProposerMatchesEvents := k.Keeper.GetProcessProposerMatchesEvents(ctx) + processProposerMatchesEvents := k.GetProcessProposerMatchesEvents(ctx) removedOrderIds := lib.UniqueSliceToSet(processProposerMatchesEvents.RemovedStatefulOrderIds) if _, found := removedOrderIds[msg.GetOrderId()]; found { telemetry.IncrCounterWithLabels( @@ -80,22 +96,22 @@ func (k msgServer) CancelOrder( // 2. Cancel the order on the ClobKeeper which is responsible for: // - stateful cancellation validation. // - removing the order from state and the memstore. - if err := k.Keeper.CancelStatefulOrder(ctx, msg); err != nil { - return nil, err + if err := k.CancelStatefulOrder(ctx, msg); err != nil { + return err } // 3. Update `ProcessProposerMatchesEvents` with the new stateful order cancellation. - processProposerMatchesEvents := k.Keeper.GetProcessProposerMatchesEvents(ctx) + processProposerMatchesEvents := k.GetProcessProposerMatchesEvents(ctx) processProposerMatchesEvents.PlacedStatefulCancellationOrderIds = append( processProposerMatchesEvents.PlacedStatefulCancellationOrderIds, msg.OrderId, ) - k.Keeper.MustSetProcessProposerMatchesEvents(ctx, processProposerMatchesEvents) + k.MustSetProcessProposerMatchesEvents(ctx, processProposerMatchesEvents) // 4. Add the relevant on-chain Indexer event for the cancellation. - k.Keeper.GetIndexerEventManager().AddTxnEvent( + k.GetIndexerEventManager().AddTxnEvent( ctx, indexerevents.SubtypeStatefulOrder, indexerevents.StatefulOrderEventVersion, @@ -107,5 +123,5 @@ func (k msgServer) CancelOrder( ), ) - return &types.MsgCancelOrderResponse{}, nil + return nil } diff --git a/protocol/x/clob/keeper/msg_server_create_clob_pair_test.go b/protocol/x/clob/keeper/msg_server_create_clob_pair_test.go index d93cf10f26..bbb64676a1 100644 --- a/protocol/x/clob/keeper/msg_server_create_clob_pair_test.go +++ b/protocol/x/clob/keeper/msg_server_create_clob_pair_test.go @@ -1,9 +1,10 @@ package keeper_test import ( - "github.com/dydxprotocol/v4-chain/protocol/lib" "testing" + "github.com/dydxprotocol/v4-chain/protocol/lib" + indexerevents "github.com/dydxprotocol/v4-chain/protocol/indexer/events" "github.com/dydxprotocol/v4-chain/protocol/indexer/indexer_manager" "github.com/dydxprotocol/v4-chain/protocol/mocks" @@ -62,6 +63,7 @@ func TestCreateClobPair(t *testing.T) { testClobPair1.SubticksPerTick, testClobPair1.StepBaseQuantums, testPerp1.Params.LiquidityTier, + testPerp1.Params.MarketType, ), ), ).Return() diff --git a/protocol/x/clob/keeper/msg_server_place_order.go b/protocol/x/clob/keeper/msg_server_place_order.go index 68251063dc..df53654fda 100644 --- a/protocol/x/clob/keeper/msg_server_place_order.go +++ b/protocol/x/clob/keeper/msg_server_place_order.go @@ -25,6 +25,23 @@ func (k msgServer) PlaceOrder(goCtx context.Context, msg *types.MsgPlaceOrder) ( ) { ctx := lib.UnwrapSDKContext(goCtx, types.ModuleName) + if err := k.Keeper.HandleMsgPlaceOrder(ctx, msg); err != nil { + return nil, err + } + + return &types.MsgPlaceOrderResponse{}, nil +} + +// HandleMsgPlaceOrder handles a MsgPlaceOrder by +// 1. persisting the placement on chain. +// 2. updating ProcessProposerMatchesEvents with the new stateful order placement. +// 3. adding order placement on-chain indexer event. +func (k Keeper) HandleMsgPlaceOrder( + ctx sdk.Context, + msg *types.MsgPlaceOrder, +) (err error) { + lib.AssertDeliverTxMode(ctx) + // Attach various logging tags relative to this request. These should be static with no changes. ctx = log.AddPersistentTagsToLogger(ctx, log.Module, log.Clob, @@ -66,10 +83,10 @@ func (k msgServer) PlaceOrder(goCtx context.Context, msg *types.MsgPlaceOrder) ( order.MustBeStatefulOrder() // 2. Return an error if an associated cancellation or removal already exists in the current block. - processProposerMatchesEvents := k.Keeper.GetProcessProposerMatchesEvents(ctx) + processProposerMatchesEvents := k.GetProcessProposerMatchesEvents(ctx) cancelledOrderIds := lib.UniqueSliceToSet(processProposerMatchesEvents.PlacedStatefulCancellationOrderIds) if _, found := cancelledOrderIds[order.GetOrderId()]; found { - return nil, errorsmod.Wrapf( + return errorsmod.Wrapf( types.ErrStatefulOrderPreviouslyCancelled, "PlaceOrder: order (%+v)", order, @@ -77,7 +94,7 @@ func (k msgServer) PlaceOrder(goCtx context.Context, msg *types.MsgPlaceOrder) ( } removedOrderIds := lib.UniqueSliceToSet(processProposerMatchesEvents.RemovedStatefulOrderIds) if _, found := removedOrderIds[order.GetOrderId()]; found { - return nil, errorsmod.Wrapf( + return errorsmod.Wrapf( types.ErrStatefulOrderPreviouslyRemoved, "PlaceOrder: order (%+v)", order, @@ -88,13 +105,13 @@ func (k msgServer) PlaceOrder(goCtx context.Context, msg *types.MsgPlaceOrder) ( // - stateful order validation. // - collateralization check. // - writing the order to state and the memstore. - if err := k.Keeper.PlaceStatefulOrder(ctx, msg); err != nil { - return nil, err + if err := k.PlaceStatefulOrder(ctx, msg); err != nil { + return err } // 4. Emit the new order placement indexer event. if order.IsConditionalOrder() { - k.Keeper.GetIndexerEventManager().AddTxnEvent( + k.GetIndexerEventManager().AddTxnEvent( ctx, indexerevents.SubtypeStatefulOrder, indexerevents.StatefulOrderEventVersion, @@ -109,7 +126,7 @@ func (k msgServer) PlaceOrder(goCtx context.Context, msg *types.MsgPlaceOrder) ( order.OrderId, ) } else { - k.Keeper.GetIndexerEventManager().AddTxnEvent( + k.GetIndexerEventManager().AddTxnEvent( ctx, indexerevents.SubtypeStatefulOrder, indexerevents.StatefulOrderEventVersion, @@ -125,10 +142,10 @@ func (k msgServer) PlaceOrder(goCtx context.Context, msg *types.MsgPlaceOrder) ( ) } // 5. Add the newly-placed stateful order to `ProcessProposerMatchesEvents` for use in `PrepareCheckState`. - k.Keeper.MustSetProcessProposerMatchesEvents( + k.MustSetProcessProposerMatchesEvents( ctx, processProposerMatchesEvents, ) - return &types.MsgPlaceOrderResponse{}, nil + return nil } diff --git a/protocol/x/clob/keeper/msg_server_place_order_test.go b/protocol/x/clob/keeper/msg_server_place_order_test.go index 48fd1547a1..b65a5326d7 100644 --- a/protocol/x/clob/keeper/msg_server_place_order_test.go +++ b/protocol/x/clob/keeper/msg_server_place_order_test.go @@ -176,6 +176,7 @@ func TestPlaceOrder_Error(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, perpetual.Params.LiquidityTier, + perpetual.Params.MarketType, ), ), ).Once().Return() @@ -320,6 +321,7 @@ func TestPlaceOrder_Success(t *testing.T) { clobPair.SubticksPerTick, clobPair.StepBaseQuantums, perpetual.Params.LiquidityTier, + perpetual.Params.MarketType, ), ), ).Once().Return() diff --git a/protocol/x/clob/keeper/msg_server_update_block_rate_limit_config_test.go b/protocol/x/clob/keeper/msg_server_update_block_rate_limit_config_test.go index e5ec7bf9b2..04bd635e78 100644 --- a/protocol/x/clob/keeper/msg_server_update_block_rate_limit_config_test.go +++ b/protocol/x/clob/keeper/msg_server_update_block_rate_limit_config_test.go @@ -1,6 +1,8 @@ package keeper_test import ( + "testing" + "github.com/cometbft/cometbft/types" "github.com/dydxprotocol/v4-chain/protocol/lib" testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" @@ -8,7 +10,6 @@ import ( clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" "github.com/stretchr/testify/require" - "testing" ) func TestUpdateBlockRateLimitConfig(t *testing.T) { @@ -22,7 +23,7 @@ func TestUpdateBlockRateLimitConfig(t *testing.T) { }) testapp.UpdateGenesisDocWithAppStateForModule(&genesis, func(state *clobtypes.GenesisState) { state.BlockRateLimitConfig = clobtypes.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ { NumBlocks: 1, Limit: 2, @@ -34,19 +35,13 @@ func TestUpdateBlockRateLimitConfig(t *testing.T) { Limit: 4, }, }, - MaxShortTermOrderCancellationsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ - { - NumBlocks: 5, - Limit: 6, - }, - }, } }) return genesis }).Build() expectedConfig := clobtypes.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ { NumBlocks: 7, Limit: 8, @@ -58,12 +53,6 @@ func TestUpdateBlockRateLimitConfig(t *testing.T) { Limit: 10, }, }, - MaxShortTermOrderCancellationsPerNBlocks: []clobtypes.MaxPerNBlocksRateLimit{ - { - NumBlocks: 11, - Limit: 12, - }, - }, } ctx := tApp.InitChain() diff --git a/protocol/x/clob/keeper/orders.go b/protocol/x/clob/keeper/orders.go index f65aff2fce..23b4324c63 100644 --- a/protocol/x/clob/keeper/orders.go +++ b/protocol/x/clob/keeper/orders.go @@ -105,7 +105,7 @@ func (k Keeper) BatchCancelShortTermOrder( failure = append(failure, clientId) log.InfoLog( ctx, - "Failed to cancel short term order.", + "Batch Cancel: Failed to cancel a short term order.", log.Error, err, ) } else { @@ -1310,8 +1310,6 @@ func (k Keeper) SendOffchainMessages( } k.GetIndexerEventManager().SendOffchainData(update) } - - k.GetGrpcStreamingManager().SendOrderbookUpdates(offchainUpdates, false) } // getFillQuoteQuantums returns the total fillAmount price in quote quantums based on the maker subticks. diff --git a/protocol/x/clob/keeper/orders_test.go b/protocol/x/clob/keeper/orders_test.go index 3bf0795743..dfc374a8c0 100644 --- a/protocol/x/clob/keeper/orders_test.go +++ b/protocol/x/clob/keeper/orders_test.go @@ -1044,6 +1044,7 @@ func TestPlaceOrder_SendOffchainMessages(t *testing.T) { constants.ClobPair_Btc.SubticksPerTick, constants.ClobPair_Btc.StepBaseQuantums, constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketType, ), ), ).Once().Return() @@ -1098,6 +1099,7 @@ func TestPerformStatefulOrderValidation_PreExistingStatefulOrder(t *testing.T) { constants.ClobPair_Btc.SubticksPerTick, constants.ClobPair_Btc.StepBaseQuantums, constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketType, ), ), ).Once().Return() @@ -1852,6 +1854,7 @@ func TestGetStatePosition_Success(t *testing.T) { cp.SubticksPerTick, cp.StepBaseQuantums, constants.Perpetuals_DefaultGenesisState.Perpetuals[i].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[i].Params.MarketType, ), ), ).Once().Return() @@ -2065,6 +2068,7 @@ func TestInitStatefulOrders(t *testing.T) { constants.ClobPair_Btc.SubticksPerTick, constants.ClobPair_Btc.StepBaseQuantums, constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketType, ), ), ).Once().Return() @@ -2196,6 +2200,7 @@ func TestHydrateUntriggeredConditionalOrdersInMemClob(t *testing.T) { constants.ClobPair_Btc.SubticksPerTick, constants.ClobPair_Btc.StepBaseQuantums, constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketType, ), ), ).Once().Return() diff --git a/protocol/x/clob/keeper/process_operations_test.go b/protocol/x/clob/keeper/process_operations_test.go index a631110803..df6af12351 100644 --- a/protocol/x/clob/keeper/process_operations_test.go +++ b/protocol/x/clob/keeper/process_operations_test.go @@ -2261,6 +2261,7 @@ func setupProcessProposerOperationsTestCase( clobPair.SubticksPerTick, clobPair.StepBaseQuantums, tc.perpetuals[perpetualId].Params.LiquidityTier, + tc.perpetuals[perpetualId].Params.MarketType, ), ), ).Once().Return() diff --git a/protocol/x/clob/keeper/rate_limit.go b/protocol/x/clob/keeper/rate_limit.go index 6079b82987..3be72e2d87 100644 --- a/protocol/x/clob/keeper/rate_limit.go +++ b/protocol/x/clob/keeper/rate_limit.go @@ -31,8 +31,7 @@ func (k *Keeper) RateLimitCancelOrder(ctx sdk.Context, msg *types.MsgCancelOrder return err } } - - return k.cancelOrderRateLimiter.RateLimit(ctx, msg) + return k.placeCancelOrderRateLimiter.RateLimit(ctx, msg) } // RateLimitPlaceOrder passes orders with valid clob pairs to `placeOrderRateLimiter`. @@ -60,10 +59,37 @@ func (k *Keeper) RateLimitPlaceOrder(ctx sdk.Context, msg *types.MsgPlaceOrder) } } - return k.placeOrderRateLimiter.RateLimit(ctx, msg) + return k.placeCancelOrderRateLimiter.RateLimit(ctx, msg) +} + +// RateLimitBatchCancel passes orders with valid clob pairs to `placeOrderRateLimiter`. +// The rate limiting is only performed during `CheckTx` and `ReCheckTx`. +func (k *Keeper) RateLimitBatchCancel(ctx sdk.Context, msg *types.MsgBatchCancel) error { + // Only rate limit during `CheckTx` and `ReCheckTx`. + if lib.IsDeliverTxMode(ctx) { + return nil + } + + for _, batch := range msg.ShortTermCancels { + _, found := k.GetClobPair(ctx, types.ClobPairId(batch.GetClobPairId())) + // If the clob pair isn't found then we expect order validation to fail the order as being invalid. + if !found { + return nil + } + } + + // Ensure that the GTB is valid before we attempt to rate limit. This is to prevent a replay attack + // where short-term order placements with GTBs in the past or the far future could be replayed by an adversary. + // Normally transaction replay attacks rely on sequence numbers being part of the signature and being incremented + // for each transaction but sequence number verification is skipped for short-term orders. + nextBlockHeight := lib.MustConvertIntegerToUint32(ctx.BlockHeight() + 1) + if err := k.validateGoodTilBlock(msg.GetGoodTilBlock(), nextBlockHeight); err != nil { + return err + } + + return k.placeCancelOrderRateLimiter.RateLimit(ctx, msg) } func (k *Keeper) PruneRateLimits(ctx sdk.Context) { - k.placeOrderRateLimiter.PruneRateLimits(ctx) - k.cancelOrderRateLimiter.PruneRateLimits(ctx) + k.placeCancelOrderRateLimiter.PruneRateLimits(ctx) } diff --git a/protocol/x/clob/memclob/memclob.go b/protocol/x/clob/memclob/memclob.go index 8f17780a85..11579c6488 100644 --- a/protocol/x/clob/memclob/memclob.go +++ b/protocol/x/clob/memclob/memclob.go @@ -47,6 +47,9 @@ type MemClobPriceTimePriority struct { // ---- Fields for determining if off-chain update messages should be generated ---- generateOffchainUpdates bool + + // ---- Fields for determining if orderbook updates should be generated ---- + generateOrderbookUpdates bool } type OrderWithRemovalReason struct { @@ -58,10 +61,11 @@ func NewMemClobPriceTimePriority( generateOffchainUpdates bool, ) *MemClobPriceTimePriority { return &MemClobPriceTimePriority{ - openOrders: newMemclobOpenOrders(), - cancels: newMemclobCancels(), - operationsToPropose: *types.NewOperationsToPropose(), - generateOffchainUpdates: generateOffchainUpdates, + openOrders: newMemclobOpenOrders(), + cancels: newMemclobCancels(), + operationsToPropose: *types.NewOperationsToPropose(), + generateOffchainUpdates: generateOffchainUpdates, + generateOrderbookUpdates: false, } } @@ -73,6 +77,11 @@ func (m *MemClobPriceTimePriority) SetClobKeeper(clobKeeper types.MemClobKeeper) m.clobKeeper = clobKeeper } +// SetGenerateOffchainUpdates sets the `generateOffchainUpdates` field of the MemClob. +func (m *MemClobPriceTimePriority) SetGenerateOrderbookUpdates(generateOrderbookUpdates bool) { + m.generateOrderbookUpdates = generateOrderbookUpdates +} + // CancelOrder removes a Short-Term order by `OrderId` (if it exists) from all order-related data structures // in the memclob. This method manages only Short-Term cancellations. For stateful cancellations, see // `msg_server_cancel_orders.go`. @@ -1510,6 +1519,12 @@ func (m *MemClobPriceTimePriority) mustAddOrderToOrderbook( } m.openOrders.mustAddOrderToOrderbook(ctx, newOrder, forceToFrontOfLevel) + + if m.generateOrderbookUpdates { + // Send an orderbook update to grpc streams. + orderbookUpdate := m.GetOrderbookUpdatesForOrderPlacement(ctx, newOrder) + m.clobKeeper.SendOrderbookUpdates(orderbookUpdate, false) + } } // mustPerformTakerOrderMatching performs matching using the provided taker order while the order @@ -1944,6 +1959,12 @@ func (m *MemClobPriceTimePriority) mustRemoveOrder( !m.operationsToPropose.IsOrderPlacementInOperationsQueue(order) { m.operationsToPropose.RemoveShortTermOrderTxBytes(order) } + + if m.generateOrderbookUpdates { + // Send an orderbook update to grpc streams. + orderbookUpdate := m.GetOrderbookUpdatesForOrderRemoval(ctx, order.OrderId) + m.clobKeeper.SendOrderbookUpdates(orderbookUpdate, false) + } } // mustUpdateOrderbookStateWithMatchedMakerOrder updates the orderbook with a matched maker order. @@ -1961,6 +1982,12 @@ func (m *MemClobPriceTimePriority) mustUpdateOrderbookStateWithMatchedMakerOrder panic("Total filled size of maker order greater than the order size") } + // Send an orderbook update for the order's new total filled amount. + if m.generateOrderbookUpdates { + orderbookUpdate := m.GetOrderbookUpdatesForOrderUpdate(ctx, makerOrder.OrderId) + m.clobKeeper.SendOrderbookUpdates(orderbookUpdate, false) + } + // If the order is fully filled, remove it from the orderbook. // Note we shouldn't remove Short-Term order hashes from `ShortTermOrderTxBytes` here since // the order was matched. diff --git a/protocol/x/clob/memclob/memclob_grpc_streaming.go b/protocol/x/clob/memclob/memclob_grpc_streaming.go index a1c86d4d0c..4bce8ec772 100644 --- a/protocol/x/clob/memclob/memclob_grpc_streaming.go +++ b/protocol/x/clob/memclob/memclob_grpc_streaming.go @@ -3,6 +3,8 @@ package memclob import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/dydxprotocol/v4-chain/protocol/indexer/off_chain_updates" + ocutypes "github.com/dydxprotocol/v4-chain/protocol/indexer/off_chain_updates/types" + indexersharedtypes "github.com/dydxprotocol/v4-chain/protocol/indexer/shared/types" "github.com/dydxprotocol/v4-chain/protocol/lib" "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" ) @@ -27,7 +29,7 @@ func (m *MemClobPriceTimePriority) GetOffchainUpdatesForOrderbookSnapshot( level.LevelOrders.Front.Each( func(order types.ClobOrder) { offchainUpdates.Append( - m.GetOffchainUpdatesForOrder(ctx, order.Order), + m.GetOrderbookUpdatesForOrderPlacement(ctx, order.Order), ) }, ) @@ -44,7 +46,7 @@ func (m *MemClobPriceTimePriority) GetOffchainUpdatesForOrderbookSnapshot( level.LevelOrders.Front.Each( func(order types.ClobOrder) { offchainUpdates.Append( - m.GetOffchainUpdatesForOrder(ctx, order.Order), + m.GetOrderbookUpdatesForOrderPlacement(ctx, order.Order), ) }, ) @@ -54,10 +56,10 @@ func (m *MemClobPriceTimePriority) GetOffchainUpdatesForOrderbookSnapshot( return offchainUpdates } -// GetOffchainUpdatesForOrder returns a place order offchain message and -// a update order offchain message used to construct an order for -// the orderbook snapshot grpc stream. -func (m *MemClobPriceTimePriority) GetOffchainUpdatesForOrder( +// GetOrderbookUpdatesForOrderPlacement returns a place order offchain message and +// a update order offchain message used to add an order for +// the orderbook grpc stream. +func (m *MemClobPriceTimePriority) GetOrderbookUpdatesForOrderPlacement( ctx sdk.Context, order types.Order, ) (offchainUpdates *types.OffchainUpdates) { @@ -86,3 +88,43 @@ func (m *MemClobPriceTimePriority) GetOffchainUpdatesForOrder( return offchainUpdates } + +// GetOrderbookUpdatesForOrderRemoval returns a remove order offchain message +// used to remove an order for the orderbook grpc stream. +func (m *MemClobPriceTimePriority) GetOrderbookUpdatesForOrderRemoval( + ctx sdk.Context, + orderId types.OrderId, +) (offchainUpdates *types.OffchainUpdates) { + offchainUpdates = types.NewOffchainUpdates() + if message, success := off_chain_updates.CreateOrderRemoveMessageWithReason( + ctx, + orderId, + indexersharedtypes.OrderRemovalReason_ORDER_REMOVAL_REASON_UNSPECIFIED, + ocutypes.OrderRemoveV1_ORDER_REMOVAL_STATUS_BEST_EFFORT_CANCELED, + ); success { + offchainUpdates.AddRemoveMessage(orderId, message) + } + return offchainUpdates +} + +// GetOrderbookUpdatesForOrderUpdate returns an update order offchain message +// used to update an order for the orderbook grpc stream. +func (m *MemClobPriceTimePriority) GetOrderbookUpdatesForOrderUpdate( + ctx sdk.Context, + orderId types.OrderId, +) (offchainUpdates *types.OffchainUpdates) { + offchainUpdates = types.NewOffchainUpdates() + + // Get the current fill amount of the order. + fillAmount := m.GetOrderFilledAmount(ctx, orderId) + + // Generate an update message updating the total filled amount of order. + if message, success := off_chain_updates.CreateOrderUpdateMessage( + ctx, + orderId, + fillAmount, + ); success { + offchainUpdates.AddUpdateMessage(orderId, message) + } + return offchainUpdates +} diff --git a/protocol/x/clob/memclob/memclob_grpc_streaming_test.go b/protocol/x/clob/memclob/memclob_grpc_streaming_test.go index e5bd2bf3f2..5a865b25f9 100644 --- a/protocol/x/clob/memclob/memclob_grpc_streaming_test.go +++ b/protocol/x/clob/memclob/memclob_grpc_streaming_test.go @@ -21,6 +21,7 @@ func TestGetOffchainUpdatesForOrderbookSnapshot_Buy(t *testing.T) { mock.Anything, mock.Anything, ).Return(false, satypes.BaseQuantums(0), uint32(0)) + clobKeeper.On("SendOrderbookUpdates", mock.Anything, mock.Anything).Return() memclob := NewMemClobPriceTimePriority(false) memclob.SetClobKeeper(clobKeeper) @@ -44,9 +45,9 @@ func TestGetOffchainUpdatesForOrderbookSnapshot_Buy(t *testing.T) { expected := types.NewOffchainUpdates() // Buy orders are in descending order. - expected.Append(memclob.GetOffchainUpdatesForOrder(ctx, orders[2])) - expected.Append(memclob.GetOffchainUpdatesForOrder(ctx, orders[0])) - expected.Append(memclob.GetOffchainUpdatesForOrder(ctx, orders[1])) + expected.Append(memclob.GetOrderbookUpdatesForOrderPlacement(ctx, orders[2])) + expected.Append(memclob.GetOrderbookUpdatesForOrderPlacement(ctx, orders[0])) + expected.Append(memclob.GetOrderbookUpdatesForOrderPlacement(ctx, orders[1])) require.Equal(t, expected, offchainUpdates) } @@ -60,6 +61,7 @@ func TestGetOffchainUpdatesForOrderbookSnapshot_Sell(t *testing.T) { mock.Anything, mock.Anything, ).Return(false, satypes.BaseQuantums(0), uint32(0)) + clobKeeper.On("SendOrderbookUpdates", mock.Anything, mock.Anything).Return() memclob := NewMemClobPriceTimePriority(false) memclob.SetClobKeeper(clobKeeper) @@ -83,9 +85,9 @@ func TestGetOffchainUpdatesForOrderbookSnapshot_Sell(t *testing.T) { expected := types.NewOffchainUpdates() // Sell orders are in ascending order. - expected.Append(memclob.GetOffchainUpdatesForOrder(ctx, orders[1])) - expected.Append(memclob.GetOffchainUpdatesForOrder(ctx, orders[2])) - expected.Append(memclob.GetOffchainUpdatesForOrder(ctx, orders[0])) + expected.Append(memclob.GetOrderbookUpdatesForOrderPlacement(ctx, orders[1])) + expected.Append(memclob.GetOrderbookUpdatesForOrderPlacement(ctx, orders[2])) + expected.Append(memclob.GetOrderbookUpdatesForOrderPlacement(ctx, orders[0])) require.Equal(t, expected, offchainUpdates) } diff --git a/protocol/x/clob/memclob/memclob_purge_invalid_memclob_state_test.go b/protocol/x/clob/memclob/memclob_purge_invalid_memclob_state_test.go index 2f6e78a68e..b15df32321 100644 --- a/protocol/x/clob/memclob/memclob_purge_invalid_memclob_state_test.go +++ b/protocol/x/clob/memclob/memclob_purge_invalid_memclob_state_test.go @@ -249,10 +249,11 @@ func TestPurgeInvalidMemclobState(t *testing.T) { // Setup memclob state. ctx, _, _ := sdktest.NewSdkContextWithMultistore() ctx = ctx.WithIsCheckTx(true) - mockMemClobKeeper := &mocks.MemClobKeeper{} memclob := NewMemClobPriceTimePriority(true) + mockMemClobKeeper := &mocks.MemClobKeeper{} memclob.SetClobKeeper(mockMemClobKeeper) mockMemClobKeeper.On("Logger", mock.Anything).Return(log.NewNopLogger()).Maybe() + mockMemClobKeeper.On("SendOrderbookUpdates", mock.Anything, mock.Anything).Return().Maybe() for _, operation := range tc.placedOperations { switch operation.Operation.(type) { @@ -339,6 +340,10 @@ func TestPurgeInvalidMemclobState_PanicsWhenCalledWithDuplicateCanceledStatefulO ctx, _, _ := sdktest.NewSdkContextWithMultistore() ctx = ctx.WithIsCheckTx(true) memclob := NewMemClobPriceTimePriority(true) + mockMemClobKeeper := &mocks.MemClobKeeper{} + memclob.SetClobKeeper(mockMemClobKeeper) + mockMemClobKeeper.On("SendOrderbookUpdates", mock.Anything, mock.Anything).Return().Maybe() + canceledStatefulOrderIds := []types.OrderId{ constants.LongTermOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTBT15.OrderId, constants.LongTermOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTBT15.OrderId, @@ -368,6 +373,10 @@ func TestPurgeInvalidMemclobState_PanicsWhenNonStatefulOrderIsCanceled(t *testin ctx, _, _ := sdktest.NewSdkContextWithMultistore() ctx = ctx.WithIsCheckTx(true) memclob := NewMemClobPriceTimePriority(true) + mockMemClobKeeper := &mocks.MemClobKeeper{} + memclob.SetClobKeeper(mockMemClobKeeper) + mockMemClobKeeper.On("SendOrderbookUpdates", mock.Anything, mock.Anything).Return().Maybe() + shortTermOrderId := constants.Order_Alice_Num0_Id0_Clob2_Buy5_Price10_GTB15.OrderId require.PanicsWithValue( @@ -395,6 +404,10 @@ func TestPurgeInvalidMemclobState_PanicsWhenCalledWithDuplicateExpiredStatefulOr ctx = ctx.WithIsCheckTx(true) memclob := NewMemClobPriceTimePriority(true) + mockMemClobKeeper := &mocks.MemClobKeeper{} + memclob.SetClobKeeper(mockMemClobKeeper) + mockMemClobKeeper.On("SendOrderbookUpdates", mock.Anything, mock.Anything).Return().Maybe() + expiredStatefulOrderIds := []types.OrderId{ constants.LongTermOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTBT15.OrderId, constants.LongTermOrder_Alice_Num0_Id0_Clob0_Buy5_Price10_GTBT15.OrderId, @@ -425,6 +438,10 @@ func TestPurgeInvalidMemclobState_PanicsWhenCalledWithShortTermExpiredStatefulOr ctx = ctx.WithIsCheckTx(true) memclob := NewMemClobPriceTimePriority(true) + mockMemClobKeeper := &mocks.MemClobKeeper{} + memclob.SetClobKeeper(mockMemClobKeeper) + mockMemClobKeeper.On("SendOrderbookUpdates", mock.Anything, mock.Anything).Return().Maybe() + shortTermOrderId := constants.Order_Alice_Num0_Id0_Clob2_Buy5_Price10_GTB15.OrderId require.PanicsWithValue( diff --git a/protocol/x/clob/memclob/memclob_remove_order_test.go b/protocol/x/clob/memclob/memclob_remove_order_test.go index 7d3aa4e0b9..15c1d88803 100644 --- a/protocol/x/clob/memclob/memclob_remove_order_test.go +++ b/protocol/x/clob/memclob/memclob_remove_order_test.go @@ -330,6 +330,7 @@ func TestRemoveOrderIfFilled(t *testing.T) { memClobKeeper.On("AddOrderToOrderbookCollatCheck", mock.Anything, mock.Anything, mock.Anything). Return(true, make(map[satypes.SubaccountId]satypes.UpdateResult)) memClobKeeper.On("ValidateSubaccountEquityTierLimitForNewOrder", mock.Anything, mock.Anything).Return(nil) + memClobKeeper.On("SendOrderbookUpdates", mock.Anything, mock.Anything).Return().Maybe() // Set initial fill amount to `0` for all orders. initialCall := memClobKeeper.On("GetOrderFillAmount", mock.Anything, mock.Anything). diff --git a/protocol/x/clob/module_test.go b/protocol/x/clob/module_test.go index 45c349e212..9035ae7f56 100644 --- a/protocol/x/clob/module_test.go +++ b/protocol/x/clob/module_test.go @@ -46,9 +46,8 @@ func getValidGenesisStr() string { gs += `{"max_notional_liquidated":"100000000000000","max_quantums_insurance_lost":"100000000000000"},` gs += `"fillable_price_config":{"bankruptcy_adjustment_ppm":1000000,` gs += `"spread_to_maintenance_margin_ratio_ppm":100000}},"block_rate_limit_config":` - gs += `{"max_short_term_orders_per_n_blocks":[{"limit": 200,"num_blocks":1}],` - gs += `"max_stateful_orders_per_n_blocks":[{"limit": 2,"num_blocks":1},{"limit": 20,"num_blocks":100}],` - gs += `"max_short_term_order_cancellations_per_n_blocks":[{"limit": 200,"num_blocks":1}]},` + gs += `{"max_short_term_orders_and_cancels_per_n_blocks":[{"limit": 400,"num_blocks":1}],` + gs += `"max_stateful_orders_per_n_blocks":[{"limit": 2,"num_blocks":1},{"limit": 20,"num_blocks":100}]},` gs += `"equity_tier_limit_config":{"short_term_order_equity_tiers":[{"limit":0,"usd_tnc_required":"0"},` gs += `{"limit":1,"usd_tnc_required":"20"},{"limit":5,"usd_tnc_required":"100"},` gs += `{"limit":10,"usd_tnc_required":"1000"},{"limit":100,"usd_tnc_required":"10000"},` @@ -167,8 +166,8 @@ func TestAppModuleBasic_DefaultGenesis(t *testing.T) { expected += `{"max_notional_liquidated":"100000000000000","max_quantums_insurance_lost":"100000000000000"},` expected += `"fillable_price_config":{"bankruptcy_adjustment_ppm":1000000,` expected += `"spread_to_maintenance_margin_ratio_ppm":100000}},"block_rate_limit_config":` - expected += `{"max_short_term_orders_per_n_blocks":[],"max_stateful_orders_per_n_blocks":[],` - expected += `"max_short_term_order_cancellations_per_n_blocks":[]},` + expected += `{"max_short_term_orders_and_cancels_per_n_blocks":[],"max_stateful_orders_per_n_blocks":[],` + expected += `"max_short_term_order_cancellations_per_n_blocks":[],"max_short_term_orders_per_n_blocks":[]},` expected += `"equity_tier_limit_config":{"short_term_order_equity_tiers":[], "stateful_order_equity_tiers":[]}}` require.JSONEq(t, expected, string(json)) @@ -241,9 +240,10 @@ func TestAppModuleBasic_GetTxCmd(t *testing.T) { cmd := am.GetTxCmd() require.Equal(t, "clob", cmd.Use) - require.Equal(t, 2, len(cmd.Commands())) - require.Equal(t, "cancel-order", cmd.Commands()[0].Name()) - require.Equal(t, "place-order", cmd.Commands()[1].Name()) + require.Equal(t, 3, len(cmd.Commands())) + require.Equal(t, "batch-cancel", cmd.Commands()[0].Name()) + require.Equal(t, "cancel-order", cmd.Commands()[1].Name()) + require.Equal(t, "place-order", cmd.Commands()[2].Name()) } func TestAppModuleBasic_GetQueryCmd(t *testing.T) { @@ -306,6 +306,7 @@ func TestAppModule_InitExportGenesis(t *testing.T) { uint32(100), uint64(5), constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.LiquidityTier, + constants.Perpetuals_DefaultGenesisState.Perpetuals[0].Params.MarketType, ), ), ).Once().Return() @@ -337,9 +338,9 @@ func TestAppModule_InitExportGenesis(t *testing.T) { require.Equal( t, clob_types.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []clob_types.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []clob_types.MaxPerNBlocksRateLimit{ { - Limit: 200, + Limit: 400, NumBlocks: 1, }, }, @@ -353,12 +354,6 @@ func TestAppModule_InitExportGenesis(t *testing.T) { NumBlocks: 100, }, }, - MaxShortTermOrderCancellationsPerNBlocks: []clob_types.MaxPerNBlocksRateLimit{ - { - Limit: 200, - NumBlocks: 1, - }, - }, }, blockRateLimitConfig, ) @@ -433,10 +428,10 @@ func TestAppModule_InitExportGenesis(t *testing.T) { expected += `{"max_notional_liquidated":"100000000000000","max_quantums_insurance_lost":"100000000000000"},` expected += `"fillable_price_config":{"bankruptcy_adjustment_ppm":1000000,` expected += `"spread_to_maintenance_margin_ratio_ppm":100000}},"block_rate_limit_config":` - expected += `{"max_short_term_orders_per_n_blocks":[{"limit": 200,"num_blocks":1}],` + expected += `{"max_short_term_orders_and_cancels_per_n_blocks":[{"limit": 400,"num_blocks":1}],` expected += `"max_stateful_orders_per_n_blocks":[{"limit": 2,"num_blocks":1},` - expected += `{"limit": 20,"num_blocks":100}],"max_short_term_order_cancellations_per_n_blocks":` - expected += `[{"limit": 200,"num_blocks":1}]},` + expected += `{"limit": 20,"num_blocks":100}],"max_short_term_order_cancellations_per_n_blocks":[],` + expected += `"max_short_term_orders_per_n_blocks":[]},` expected += `"equity_tier_limit_config":{"short_term_order_equity_tiers":[{"limit":0,"usd_tnc_required":"0"},` expected += `{"limit":1,"usd_tnc_required":"20"},{"limit":5,"usd_tnc_required":"100"},` expected += `{"limit":10,"usd_tnc_required":"1000"},{"limit":100,"usd_tnc_required":"10000"},` diff --git a/protocol/x/clob/rate_limit/multi_block_rate_limiter.go b/protocol/x/clob/rate_limit/multi_block_rate_limiter.go index a5f3e3638b..74173228be 100644 --- a/protocol/x/clob/rate_limit/multi_block_rate_limiter.go +++ b/protocol/x/clob/rate_limit/multi_block_rate_limiter.go @@ -1,10 +1,11 @@ package rate_limit import ( - errorsmod "cosmossdk.io/errors" "fmt" "sort" + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/dydxprotocol/v4-chain/protocol/lib" "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" @@ -66,6 +67,10 @@ func NewMultiBlockRateLimiter[K comparable](context string, config []types.MaxPe } func (r *multiBlockRateLimiter[K]) RateLimit(ctx sdk.Context, key K) error { + return r.RateLimitIncrBy(ctx, key, 1) +} + +func (r *multiBlockRateLimiter[K]) RateLimitIncrBy(ctx sdk.Context, key K, incrBy uint32) error { blockHeight := lib.MustConvertIntegerToUint32(ctx.BlockHeight()) offset := blockHeight % r.maxNumBlocks @@ -83,7 +88,7 @@ func (r *multiBlockRateLimiter[K]) RateLimit(ctx sdk.Context, key K) error { perBlockCounts = make(map[uint32]uint32) r.perKeyBlockCounts[key] = perBlockCounts } - count := perBlockCounts[blockHeight] + 1 + count := perBlockCounts[blockHeight] + incrBy perBlockCounts[blockHeight] = count // Update the per rate limit count. @@ -93,7 +98,7 @@ func (r *multiBlockRateLimiter[K]) RateLimit(ctx sdk.Context, key K) error { r.perKeyRateLimitCounts[key] = perRateLimitCounts } for i := range perRateLimitCounts { - perRateLimitCounts[i] += 1 + perRateLimitCounts[i] += incrBy } // Check the accumulated rate limit count to see if any rate limit has been exceeded. diff --git a/protocol/x/clob/rate_limit/noop_rate_limiter.go b/protocol/x/clob/rate_limit/noop_rate_limiter.go index 824020a80d..391251beec 100644 --- a/protocol/x/clob/rate_limit/noop_rate_limiter.go +++ b/protocol/x/clob/rate_limit/noop_rate_limiter.go @@ -19,5 +19,9 @@ func (n noOpRateLimiter[K]) RateLimit(ctx sdk.Context, key K) error { return nil } +func (n noOpRateLimiter[K]) RateLimitIncrBy(ctx sdk.Context, key K, incrBy uint32) error { + return nil +} + func (n noOpRateLimiter[K]) PruneRateLimits(ctx sdk.Context) { } diff --git a/protocol/x/clob/rate_limit/order_rate_limiter.go b/protocol/x/clob/rate_limit/order_rate_limiter.go index 19e3d03631..911e927424 100644 --- a/protocol/x/clob/rate_limit/order_rate_limiter.go +++ b/protocol/x/clob/rate_limit/order_rate_limiter.go @@ -8,22 +8,28 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" ) -// A RateLimiter which rate limits types.MsgPlaceOrder. +var ( + BATCH_CANCEL_RATE_LIMIT_WEIGHT = uint32(2) +) + +// A RateLimiter which rate limits types.MsgPlaceOrder, types.MsgCancelOrder, and +// types.MsgBatchCancel. // // The rate limiting keeps track of short term and stateful orders placed during // CheckTx. -type placeOrderRateLimiter struct { - checkStateShortTermOrderRateLimiter RateLimiter[string] - checkStateStatefulOrderRateLimiter RateLimiter[string] +type placeAndCancelOrderRateLimiter struct { + checkStateShortTermOrderPlaceCancelRateLimiter RateLimiter[string] + checkStateStatefulOrderRateLimiter RateLimiter[string] // The set of rate limited accounts is only stored for telemetry purposes. rateLimitedAccounts map[string]bool } -var _ RateLimiter[*types.MsgPlaceOrder] = (*placeOrderRateLimiter)(nil) +var _ RateLimiter[sdk.Msg] = (*placeAndCancelOrderRateLimiter)(nil) -// NewPlaceOrderRateLimiter returns a RateLimiter which rate limits types.MsgPlaceOrder based upon the provided -// types.BlockRateLimitConfiguration. The rate limiter currently supports limiting based upon: -// - how many short term orders per account (by using string). +// NewPlaceCancelOrderRateLimiter returns a RateLimiter which rate limits types.MsgPlaceOrder, types.MsgCancelOrder, +// types.MsgBatchCancel based upon the provided types.BlockRateLimitConfiguration. The rate limiter currently +// supports limiting based upon: +// - how many short term place/cancel orders per account (by using string). // - how many stateful order per account (by using string). // // The rate limiting must only be used during `CheckTx` because the rate limiting information is not recovered @@ -34,31 +40,31 @@ var _ RateLimiter[*types.MsgPlaceOrder] = (*placeOrderRateLimiter)(nil) // - `ctx.BlockHeight()` in PruneRateLimits and should be invoked during `EndBlocker`. If invoked // during `PrepareCheckState` one must supply a `ctx` with the previous block height via // `ctx.WithBlockHeight(ctx.BlockHeight()-1)`. -func NewPlaceOrderRateLimiter(config types.BlockRateLimitConfiguration) RateLimiter[*types.MsgPlaceOrder] { +func NewPlaceCancelOrderRateLimiter(config types.BlockRateLimitConfiguration) RateLimiter[sdk.Msg] { if err := config.Validate(); err != nil { panic(err) } // Return the no-op rate limiter if the configuration is empty. - if len(config.MaxShortTermOrdersPerNBlocks)+len(config.MaxStatefulOrdersPerNBlocks) == 0 { - return noOpRateLimiter[*types.MsgPlaceOrder]{} + if len(config.MaxShortTermOrdersAndCancelsPerNBlocks)+len(config.MaxStatefulOrdersPerNBlocks) == 0 { + return noOpRateLimiter[sdk.Msg]{} } - r := placeOrderRateLimiter{ + r := placeAndCancelOrderRateLimiter{ rateLimitedAccounts: make(map[string]bool, 0), } - if len(config.MaxShortTermOrdersPerNBlocks) == 0 { - r.checkStateShortTermOrderRateLimiter = NewNoOpRateLimiter[string]() - } else if len(config.MaxShortTermOrdersPerNBlocks) == 1 && - config.MaxShortTermOrdersPerNBlocks[0].NumBlocks == 1 { - r.checkStateShortTermOrderRateLimiter = NewSingleBlockRateLimiter[string]( - "MaxShortTermOrdersPerNBlocks", - config.MaxShortTermOrdersPerNBlocks[0], + if len(config.MaxShortTermOrdersAndCancelsPerNBlocks) == 0 { + r.checkStateShortTermOrderPlaceCancelRateLimiter = NewNoOpRateLimiter[string]() + } else if len(config.MaxShortTermOrdersAndCancelsPerNBlocks) == 1 && + config.MaxShortTermOrdersAndCancelsPerNBlocks[0].NumBlocks == 1 { + r.checkStateShortTermOrderPlaceCancelRateLimiter = NewSingleBlockRateLimiter[string]( + "MaxShortTermOrdersAndCancelsPerNBlocks", + config.MaxShortTermOrdersAndCancelsPerNBlocks[0], ) } else { - r.checkStateShortTermOrderRateLimiter = NewMultiBlockRateLimiter[string]( - "MaxShortTermOrdersPerNBlocks", - config.MaxShortTermOrdersPerNBlocks, + r.checkStateShortTermOrderPlaceCancelRateLimiter = NewMultiBlockRateLimiter[string]( + "MaxShortTermOrdersAndCancelsPerNBlocks", + config.MaxShortTermOrdersAndCancelsPerNBlocks, ) } if len(config.MaxStatefulOrdersPerNBlocks) == 0 { @@ -79,11 +85,27 @@ func NewPlaceOrderRateLimiter(config types.BlockRateLimitConfiguration) RateLimi return &r } -func (r *placeOrderRateLimiter) RateLimit(ctx sdk.Context, msg *types.MsgPlaceOrder) (err error) { +func (r *placeAndCancelOrderRateLimiter) RateLimit(ctx sdk.Context, msg sdk.Msg) (err error) { lib.AssertCheckTxMode(ctx) + switch castedMsg := (msg).(type) { + case *types.MsgCancelOrder: + err = r.RateLimitCancelOrder(ctx, *castedMsg) + case *types.MsgPlaceOrder: + err = r.RateLimitPlaceOrder(ctx, *castedMsg) + case *types.MsgBatchCancel: + err = r.RateLimitBatchCancelOrder(ctx, *castedMsg) + } + return err +} +func (r *placeAndCancelOrderRateLimiter) RateLimitIncrBy(ctx sdk.Context, msg sdk.Msg, incrBy uint32) (err error) { + panic("PlaceAndCancelOrderRateLimiter is a top-level rate limiter. It should not use IncrBy.") +} + +func (r *placeAndCancelOrderRateLimiter) RateLimitPlaceOrder(ctx sdk.Context, msg types.MsgPlaceOrder) (err error) { + lib.AssertCheckTxMode(ctx) if msg.Order.IsShortTermOrder() { - err = r.checkStateShortTermOrderRateLimiter.RateLimit( + err = r.checkStateShortTermOrderPlaceCancelRateLimiter.RateLimit( ctx, msg.Order.OrderId.SubaccountId.Owner, ) @@ -103,81 +125,14 @@ func (r *placeOrderRateLimiter) RateLimit(ctx sdk.Context, msg *types.MsgPlaceOr return err } -func (r *placeOrderRateLimiter) PruneRateLimits(ctx sdk.Context) { - telemetry.IncrCounter( - float32(len(r.rateLimitedAccounts)), - types.ModuleName, - metrics.RateLimit, - metrics.PlaceOrderAccounts, - metrics.Count, - ) - // Note that this method for clearing the map is optimized by the go compiler significantly - // and will leave the relative size of the map the same so that it doesn't need to be resized - // often. - for key := range r.rateLimitedAccounts { - delete(r.rateLimitedAccounts, key) - } - r.checkStateShortTermOrderRateLimiter.PruneRateLimits(ctx) - r.checkStateStatefulOrderRateLimiter.PruneRateLimits(ctx) -} - -// A RateLimiter which rate limits types.MsgCancelOrder. -// -// The rate limiting keeps track of short term order cancellations during CheckTx. -type cancelOrderRateLimiter struct { - checkStateShortTermRateLimiter RateLimiter[string] - // The set of rate limited accounts is only stored for telemetry purposes. - rateLimitedAccounts map[string]bool -} - -var _ RateLimiter[*types.MsgCancelOrder] = (*cancelOrderRateLimiter)(nil) - -// NewCancelOrderRateLimiter returns a RateLimiter which rate limits types.MsgCancelOrder based upon the provided -// types.BlockRateLimitConfiguration. The rate limiter currently supports limiting based upon: -// - how many short term order cancellations per account (by using string). -// -// The rate limiting must only be used during `CheckTx` because the rate limiting information is not recovered -// on application restart preventing it from being deterministic during `DeliverTx`. -// -// Depending upon the provided types.BlockRateLimitConfiguration, the returned RateLimiter may rely on: -// - `ctx.BlockHeight()` in RateLimit to track which block the rate limit should apply to. -// - `ctx.BlockHeight()` in PruneRateLimits and should be invoked during `EndBlocker`. If invoked -// during `PrepareCheckState` one must supply a `ctx` with the previous block height via -// `ctx.WithBlockHeight(ctx.BlockHeight()-1)`. -func NewCancelOrderRateLimiter(config types.BlockRateLimitConfiguration) RateLimiter[*types.MsgCancelOrder] { - if err := config.Validate(); err != nil { - panic(err) - } - - // Return the no-op rate limiter if the configuration is empty. - if len(config.MaxShortTermOrderCancellationsPerNBlocks) == 0 { - return noOpRateLimiter[*types.MsgCancelOrder]{} - } - - rateLimiter := cancelOrderRateLimiter{ - rateLimitedAccounts: make(map[string]bool, 0), - } - if len(config.MaxShortTermOrderCancellationsPerNBlocks) == 1 && - config.MaxShortTermOrderCancellationsPerNBlocks[0].NumBlocks == 1 { - rateLimiter.checkStateShortTermRateLimiter = NewSingleBlockRateLimiter[string]( - "MaxShortTermOrdersPerNBlocks", - config.MaxShortTermOrderCancellationsPerNBlocks[0], - ) - return &rateLimiter - } else { - rateLimiter.checkStateShortTermRateLimiter = NewMultiBlockRateLimiter[string]( - "MaxShortTermOrdersPerNBlocks", - config.MaxShortTermOrderCancellationsPerNBlocks, - ) - return &rateLimiter - } -} - -func (r *cancelOrderRateLimiter) RateLimit(ctx sdk.Context, msg *types.MsgCancelOrder) (err error) { +func (r *placeAndCancelOrderRateLimiter) RateLimitCancelOrder( + ctx sdk.Context, + msg types.MsgCancelOrder, +) (err error) { lib.AssertCheckTxMode(ctx) if msg.OrderId.IsShortTermOrder() { - err = r.checkStateShortTermRateLimiter.RateLimit( + err = r.checkStateShortTermOrderPlaceCancelRateLimiter.RateLimit( ctx, msg.OrderId.SubaccountId.Owner, ) @@ -193,12 +148,36 @@ func (r *cancelOrderRateLimiter) RateLimit(ctx sdk.Context, msg *types.MsgCancel return err } -func (r *cancelOrderRateLimiter) PruneRateLimits(ctx sdk.Context) { +func (r *placeAndCancelOrderRateLimiter) RateLimitBatchCancelOrder( + ctx sdk.Context, + msg types.MsgBatchCancel, +) (err error) { + lib.AssertCheckTxMode(ctx) + + // TODO(CT-688) Use a scaling function such as (1 + ceil(0.1 * #cancels)) to calculate batch + // cancel rate limit weights. + err = r.checkStateShortTermOrderPlaceCancelRateLimiter.RateLimitIncrBy( + ctx, + msg.SubaccountId.Owner, + BATCH_CANCEL_RATE_LIMIT_WEIGHT, + ) + if err != nil { + telemetry.IncrCounterWithLabels( + []string{types.ModuleName, metrics.RateLimit, metrics.PlaceOrder, metrics.Count}, + 1, + []metrics.Label{}, + ) + r.rateLimitedAccounts[msg.SubaccountId.Owner] = true + } + return err +} + +func (r *placeAndCancelOrderRateLimiter) PruneRateLimits(ctx sdk.Context) { telemetry.IncrCounter( float32(len(r.rateLimitedAccounts)), types.ModuleName, metrics.RateLimit, - metrics.CancelOrderAccounts, + metrics.PlaceOrderAccounts, metrics.Count, ) // Note that this method for clearing the map is optimized by the go compiler significantly @@ -207,5 +186,6 @@ func (r *cancelOrderRateLimiter) PruneRateLimits(ctx sdk.Context) { for key := range r.rateLimitedAccounts { delete(r.rateLimitedAccounts, key) } - r.checkStateShortTermRateLimiter.PruneRateLimits(ctx) + r.checkStateShortTermOrderPlaceCancelRateLimiter.PruneRateLimits(ctx) + r.checkStateStatefulOrderRateLimiter.PruneRateLimits(ctx) } diff --git a/protocol/x/clob/rate_limit/panic_rate_limiter.go b/protocol/x/clob/rate_limit/panic_rate_limiter.go index 310cd04dcc..3fd024ce1e 100644 --- a/protocol/x/clob/rate_limit/panic_rate_limiter.go +++ b/protocol/x/clob/rate_limit/panic_rate_limiter.go @@ -19,6 +19,10 @@ func (n panicRateLimiter[K]) RateLimit(ctx sdk.Context, key K) error { panic("Unexpected invocation of RateLimit") } +func (n panicRateLimiter[K]) RateLimitIncrBy(ctx sdk.Context, key K, incrBy uint32) error { + panic("Unexpected invocation of RateLimitIncrBy") +} + func (n panicRateLimiter[K]) PruneRateLimits(ctx sdk.Context) { panic("Unexpected invocation of PruneRateLimits") } diff --git a/protocol/x/clob/rate_limit/rate_limit.go b/protocol/x/clob/rate_limit/rate_limit.go index 32f673b62a..d0a5ae305a 100644 --- a/protocol/x/clob/rate_limit/rate_limit.go +++ b/protocol/x/clob/rate_limit/rate_limit.go @@ -8,6 +8,9 @@ import ( type RateLimiter[K any] interface { // Returns an error if the RateLimiter exceeds any configured rate limits for the key K and context state. RateLimit(ctx sdk.Context, key K) error + // Returns an error if the RateLimiter exceeds any configured rate limits for the key K and context state + // Increments the rate limit counter by incrBy. + RateLimitIncrBy(ctx sdk.Context, key K, incrBy uint32) error // Prunes rate limits for the provided context. PruneRateLimits(ctx sdk.Context) } diff --git a/protocol/x/clob/rate_limit/single_block_rate_limiter.go b/protocol/x/clob/rate_limit/single_block_rate_limiter.go index 8ba6f670c5..07da1cd5fe 100644 --- a/protocol/x/clob/rate_limit/single_block_rate_limiter.go +++ b/protocol/x/clob/rate_limit/single_block_rate_limiter.go @@ -1,9 +1,10 @@ package rate_limit import ( - errorsmod "cosmossdk.io/errors" "fmt" + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" ) @@ -33,7 +34,11 @@ func NewSingleBlockRateLimiter[K comparable](context string, config types.MaxPer } func (r *singleBlockRateLimiter[K]) RateLimit(ctx sdk.Context, key K) error { - count := r.perKeyCounts[key] + 1 + return r.RateLimitIncrBy(ctx, key, 1) +} + +func (r *singleBlockRateLimiter[K]) RateLimitIncrBy(ctx sdk.Context, key K, incrBy uint32) error { + count := r.perKeyCounts[key] + incrBy r.perKeyCounts[key] = count if count > r.config.Limit { return errorsmod.Wrapf( diff --git a/protocol/x/clob/types/block_rate_limit_config.go b/protocol/x/clob/types/block_rate_limit_config.go index 7970be3353..9d784eb9c6 100644 --- a/protocol/x/clob/types/block_rate_limit_config.go +++ b/protocol/x/clob/types/block_rate_limit_config.go @@ -5,32 +5,27 @@ import ( ) const ( - MaxShortTermOrdersPerNBlocksNumBlocks = 1_000 - MaxShortTermOrdersPerNBlocksLimit = 10_000_000 - MaxShortTermOrderCancellationsPerNBlocksNumBlocks = 1_000 - MaxShortTermOrderCancellationsPerNBlocksLimit = 10_000_000 - MaxStatefulOrdersPerNBlocksNumBlocks = 10_000 - MaxStatefulOrdersPerNBlocksLimit = 1_000_000 + MaxShortTermOrdersAndCancelsPerNBlocksNumBlocks = 2_000 + MaxShortTermOrdersAndCancelsPerNBlocksLimit = 10_000_000 + MaxStatefulOrdersPerNBlocksNumBlocks = 10_000 + MaxStatefulOrdersPerNBlocksLimit = 1_000_000 ) // Validate validates each individual MaxPerNBlocksRateLimit. // It returns an error if any of the rate limits fail the following validations: -// - `Limit == 0` || `Limit > MaxShortTermOrdersPerNBlocksLimit` for short term order rate limits. -// - `NumBlocks == 0` || `NumBlocks > MaxShortTermOrdersPerNBlocksNumBlocks` for short term order rate +// - `Limit == 0` || `Limit > MaxShortTermOrdersAndCancelsPerNBlocksLimit` for short term order/cancel rate limits. +// - `NumBlocks == 0` || `NumBlocks > MaxShortTermOrdersAndCancelsPerNBlocksNumBlocks` +// for short term order/cancel rate // limits. // - `Limit == 0` || `Limit > MaxStatefulOrdersPerNBlocksLimit` for stateful order rate limits. // - `NumBlocks == 0` || `NumBlocks > MaxStatefulOrdersPerNBlocksNumBlocks` for stateful order rate limits. -// - `Limit == 0` || `Limit > MaxShortTermOrderCancellationsPerNBlocksNumBlocks` for short term order -// cancellation rate limits. -// - `NumBlocks == 0` || `NumBlocks > MaxShortTermOrderCancellationsPerNBlocksLimit` for short term order -// cancellation rate limits. -// - There are multiple rate limits for the same `NumBlocks` in `MaxShortTermOrdersPerNBlocks`, -// `MaxStatefulOrdersPerNBlocks`, or `MaxShortTermOrderCancellationsPerNBlocks`. +// - There are multiple rate limits for the same `NumBlocks` in `MaxShortTermOrdersAndCancelsPerNBlocks`, +// `MaxStatefulOrdersPerNBlocks`. func (lc BlockRateLimitConfiguration) Validate() error { - if err := (maxPerNBlocksRateLimits)(lc.MaxShortTermOrdersPerNBlocks).validate( - "MaxShortTermOrdersPerNBlocks", - MaxShortTermOrdersPerNBlocksNumBlocks, - MaxShortTermOrdersPerNBlocksLimit, + if err := (maxPerNBlocksRateLimits)(lc.MaxShortTermOrdersAndCancelsPerNBlocks).validate( + "MaxShortTermOrdersAndCancelsPerNBlocks", + MaxShortTermOrdersAndCancelsPerNBlocksNumBlocks, + MaxShortTermOrdersAndCancelsPerNBlocksLimit, ); err != nil { return err } @@ -41,13 +36,6 @@ func (lc BlockRateLimitConfiguration) Validate() error { ); err != nil { return err } - if err := (maxPerNBlocksRateLimits)(lc.MaxShortTermOrderCancellationsPerNBlocks).validate( - "MaxShortTermOrderCancellationsPerNBlocks", - MaxShortTermOrderCancellationsPerNBlocksNumBlocks, - MaxShortTermOrderCancellationsPerNBlocksLimit, - ); err != nil { - return err - } return nil } diff --git a/protocol/x/clob/types/block_rate_limit_config.pb.go b/protocol/x/clob/types/block_rate_limit_config.pb.go index b1f0be069e..13cf4cf5b7 100644 --- a/protocol/x/clob/types/block_rate_limit_config.pb.go +++ b/protocol/x/clob/types/block_rate_limit_config.pb.go @@ -31,7 +31,9 @@ type BlockRateLimitConfiguration struct { // configurations. // // Specifying 0 values disables this rate limit. - MaxShortTermOrdersPerNBlocks []MaxPerNBlocksRateLimit `protobuf:"bytes,1,rep,name=max_short_term_orders_per_n_blocks,json=maxShortTermOrdersPerNBlocks,proto3" json:"max_short_term_orders_per_n_blocks"` + // Deprecated in favor of `max_short_term_orders_and_cancels_per_n_blocks` + // for v5.x onwards. + MaxShortTermOrdersPerNBlocks []MaxPerNBlocksRateLimit `protobuf:"bytes,1,rep,name=max_short_term_orders_per_n_blocks,json=maxShortTermOrdersPerNBlocks,proto3" json:"max_short_term_orders_per_n_blocks"` // Deprecated: Do not use. // How many stateful order attempts (successful and failed) are allowed for // an account per N blocks. Note that the rate limits are applied // in an AND fashion such that an order placement must pass all rate limit @@ -45,7 +47,16 @@ type BlockRateLimitConfiguration struct { // rate limit configurations. // // Specifying 0 values disables this rate limit. - MaxShortTermOrderCancellationsPerNBlocks []MaxPerNBlocksRateLimit `protobuf:"bytes,3,rep,name=max_short_term_order_cancellations_per_n_blocks,json=maxShortTermOrderCancellationsPerNBlocks,proto3" json:"max_short_term_order_cancellations_per_n_blocks"` + // Deprecated in favor of `max_short_term_orders_and_cancels_per_n_blocks` + // for v5.x onwards. + MaxShortTermOrderCancellationsPerNBlocks []MaxPerNBlocksRateLimit `protobuf:"bytes,3,rep,name=max_short_term_order_cancellations_per_n_blocks,json=maxShortTermOrderCancellationsPerNBlocks,proto3" json:"max_short_term_order_cancellations_per_n_blocks"` // Deprecated: Do not use. + // How many short term order place and cancel attempts (successful and failed) + // are allowed for an account per N blocks. Note that the rate limits are + // applied in an AND fashion such that an order placement must pass all rate + // limit configurations. + // + // Specifying 0 values disables this rate limit. + MaxShortTermOrdersAndCancelsPerNBlocks []MaxPerNBlocksRateLimit `protobuf:"bytes,4,rep,name=max_short_term_orders_and_cancels_per_n_blocks,json=maxShortTermOrdersAndCancelsPerNBlocks,proto3" json:"max_short_term_orders_and_cancels_per_n_blocks"` } func (m *BlockRateLimitConfiguration) Reset() { *m = BlockRateLimitConfiguration{} } @@ -81,6 +92,7 @@ func (m *BlockRateLimitConfiguration) XXX_DiscardUnknown() { var xxx_messageInfo_BlockRateLimitConfiguration proto.InternalMessageInfo +// Deprecated: Do not use. func (m *BlockRateLimitConfiguration) GetMaxShortTermOrdersPerNBlocks() []MaxPerNBlocksRateLimit { if m != nil { return m.MaxShortTermOrdersPerNBlocks @@ -95,6 +107,7 @@ func (m *BlockRateLimitConfiguration) GetMaxStatefulOrdersPerNBlocks() []MaxPerN return nil } +// Deprecated: Do not use. func (m *BlockRateLimitConfiguration) GetMaxShortTermOrderCancellationsPerNBlocks() []MaxPerNBlocksRateLimit { if m != nil { return m.MaxShortTermOrderCancellationsPerNBlocks @@ -102,6 +115,13 @@ func (m *BlockRateLimitConfiguration) GetMaxShortTermOrderCancellationsPerNBlock return nil } +func (m *BlockRateLimitConfiguration) GetMaxShortTermOrdersAndCancelsPerNBlocks() []MaxPerNBlocksRateLimit { + if m != nil { + return m.MaxShortTermOrdersAndCancelsPerNBlocks + } + return nil +} + // Defines a rate limit over a specific number of blocks. type MaxPerNBlocksRateLimit struct { // How many blocks the rate limit is over. @@ -169,29 +189,32 @@ func init() { } var fileDescriptor_0b7d196450032f13 = []byte{ - // 351 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x92, 0xc1, 0x4e, 0xc2, 0x40, - 0x10, 0x40, 0x5b, 0x50, 0x13, 0xd7, 0x78, 0xb0, 0x21, 0x86, 0x88, 0x56, 0xc2, 0x09, 0x0f, 0xb6, - 0x89, 0x1a, 0x3f, 0x00, 0xae, 0xa2, 0x04, 0x3d, 0x79, 0xd9, 0x6c, 0x97, 0xa5, 0x34, 0xee, 0xee, - 0x90, 0xed, 0x96, 0x94, 0xbf, 0x30, 0xfe, 0x83, 0xff, 0xc2, 0x91, 0xa3, 0x27, 0x63, 0xe0, 0x47, - 0xcc, 0x2e, 0x04, 0x50, 0x38, 0x71, 0x6b, 0xa7, 0x33, 0xef, 0xcd, 0x4c, 0x07, 0x85, 0xdd, 0x51, - 0x37, 0x1f, 0x28, 0xd0, 0x40, 0x81, 0x87, 0x94, 0x43, 0x14, 0x46, 0x1c, 0xe8, 0x1b, 0x56, 0x44, - 0x33, 0xcc, 0x13, 0x91, 0x68, 0x4c, 0x41, 0xf6, 0x92, 0x38, 0xb0, 0x59, 0xde, 0xc9, 0x7a, 0x41, - 0x60, 0x0a, 0xce, 0x4a, 0x31, 0xc4, 0x60, 0x43, 0xa1, 0x79, 0x9a, 0x27, 0xd6, 0x3e, 0x8b, 0xa8, - 0xd2, 0x30, 0xa8, 0x0e, 0xd1, 0xec, 0xc1, 0x80, 0x9a, 0x96, 0x93, 0x29, 0xa2, 0x13, 0x90, 0xde, - 0x08, 0xd5, 0x04, 0xc9, 0x71, 0xda, 0x07, 0xa5, 0xb1, 0x66, 0x4a, 0x60, 0x50, 0x5d, 0xa6, 0x52, - 0x3c, 0x60, 0x0a, 0x4b, 0x6c, 0xbb, 0x48, 0xcb, 0x6e, 0xb5, 0x58, 0x3f, 0xba, 0xb9, 0x0a, 0x36, - 0xac, 0x41, 0x8b, 0xe4, 0x6d, 0xa6, 0x1e, 0xad, 0x22, 0x5d, 0x3a, 0x1a, 0x7b, 0xe3, 0xef, 0x4b, - 0xa7, 0x73, 0x2e, 0x48, 0xfe, 0x6c, 0xc8, 0x2f, 0x4c, 0x89, 0x27, 0xcb, 0x5d, 0x25, 0x7b, 0x43, - 0x54, 0xb5, 0x6a, 0x4d, 0x34, 0xeb, 0x65, 0x7c, 0xab, 0xb8, 0xb0, 0x9b, 0xb8, 0x62, 0xc4, 0x0b, - 0xee, 0x86, 0xf7, 0xc3, 0x45, 0xe1, 0xb6, 0x99, 0x31, 0x25, 0x92, 0x32, 0xce, 0xed, 0x62, 0xfe, - 0xf5, 0x51, 0xdc, 0xad, 0x8f, 0xfa, 0xc6, 0x02, 0x9a, 0xeb, 0x8e, 0x55, 0x61, 0xad, 0x85, 0x4e, - 0xb7, 0x93, 0xbc, 0x0b, 0x84, 0x64, 0x26, 0x56, 0x7f, 0xc2, 0xad, 0x1f, 0x77, 0x0e, 0x65, 0x26, - 0x16, 0xd3, 0x94, 0xd0, 0xbe, 0xbd, 0x8f, 0x72, 0xc1, 0x7e, 0x99, 0xbf, 0x34, 0xda, 0xe3, 0xa9, - 0xef, 0x4e, 0xa6, 0xbe, 0xfb, 0x33, 0xf5, 0xdd, 0xf7, 0x99, 0xef, 0x4c, 0x66, 0xbe, 0xf3, 0x35, - 0xf3, 0x9d, 0xd7, 0xfb, 0x38, 0xd1, 0xfd, 0x2c, 0x0a, 0x28, 0x88, 0xbf, 0x57, 0x37, 0xbc, 0xbb, - 0xa6, 0x7d, 0x92, 0xc8, 0x70, 0x19, 0xc9, 0xe7, 0x97, 0xa8, 0x47, 0x03, 0x96, 0x46, 0x07, 0x36, - 0x7c, 0xfb, 0x1b, 0x00, 0x00, 0xff, 0xff, 0x43, 0x4b, 0x98, 0xcb, 0xab, 0x02, 0x00, 0x00, + // 391 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x93, 0xc1, 0x6e, 0xda, 0x30, + 0x18, 0xc7, 0x63, 0x60, 0x48, 0xf3, 0xb4, 0xc3, 0x22, 0x34, 0xa1, 0xb1, 0x65, 0x88, 0xc3, 0xc4, + 0x0e, 0x4b, 0xa4, 0x6d, 0xea, 0xbd, 0x70, 0x2d, 0x2d, 0x4a, 0x7b, 0xea, 0xc5, 0x72, 0x1c, 0x13, + 0xa2, 0xc6, 0x36, 0x72, 0x1c, 0x14, 0xd4, 0x87, 0x68, 0x0f, 0x7d, 0x9b, 0xbe, 0x00, 0x47, 0x8e, + 0x3d, 0x55, 0x15, 0xbc, 0x48, 0x15, 0x07, 0x41, 0x4a, 0x72, 0x29, 0xb7, 0xe4, 0xf3, 0xe7, 0xff, + 0xef, 0xef, 0xbf, 0xfd, 0x41, 0xc7, 0x5f, 0xf8, 0xe9, 0x4c, 0x0a, 0x25, 0x88, 0x88, 0x1c, 0x12, + 0x09, 0xcf, 0xf1, 0x22, 0x41, 0x6e, 0x90, 0xc4, 0x8a, 0xa2, 0x28, 0x64, 0xa1, 0x42, 0x44, 0xf0, + 0x49, 0x18, 0xd8, 0xba, 0xcb, 0xfc, 0x52, 0xdc, 0x60, 0x67, 0x1b, 0xbe, 0xb5, 0x02, 0x11, 0x08, + 0x5d, 0x72, 0xb2, 0xaf, 0xbc, 0xb1, 0xf7, 0xd8, 0x80, 0x9d, 0x41, 0x26, 0xe5, 0x62, 0x45, 0xcf, + 0x32, 0xa1, 0xa1, 0xd6, 0x49, 0x24, 0x56, 0xa1, 0xe0, 0xe6, 0x2d, 0xec, 0x31, 0x9c, 0xa2, 0x78, + 0x2a, 0xa4, 0x42, 0x8a, 0x4a, 0x86, 0x84, 0xf4, 0xa9, 0x8c, 0xd1, 0x8c, 0x4a, 0xc4, 0x91, 0x76, + 0x11, 0xb7, 0x41, 0xb7, 0xde, 0xff, 0xf4, 0xf7, 0xb7, 0x5d, 0xa2, 0xda, 0x23, 0x9c, 0x8e, 0xa9, + 0x3c, 0xd7, 0x88, 0x78, 0xc7, 0x18, 0x34, 0x97, 0xcf, 0x3f, 0x8d, 0x36, 0x70, 0xbf, 0x33, 0x9c, + 0x5e, 0x66, 0xda, 0x57, 0x54, 0xb2, 0x0b, 0xad, 0xbc, 0x6f, 0x37, 0xe7, 0xb0, 0xab, 0xe1, 0x0a, + 0x2b, 0x3a, 0x49, 0xa2, 0x4a, 0x74, 0xed, 0xbd, 0xe8, 0x46, 0x86, 0x76, 0x3b, 0x19, 0x78, 0xab, + 0x5b, 0xe2, 0x3e, 0x00, 0xe8, 0x54, 0x9d, 0x1a, 0x11, 0xcc, 0x09, 0x8d, 0x22, 0x1d, 0xcd, 0x81, + 0x8f, 0xfa, 0xb1, 0x11, 0xf4, 0x4b, 0x11, 0x0c, 0x8b, 0x94, 0x82, 0xad, 0x3b, 0x00, 0xed, 0xea, + 0xcb, 0xc0, 0xdc, 0xdf, 0x7a, 0x3b, 0x70, 0xd5, 0x38, 0x2e, 0x9d, 0x5f, 0xe5, 0x6b, 0x39, 0xe5, + 0x7e, 0xee, 0xab, 0xe0, 0xa8, 0x37, 0x82, 0x5f, 0xab, 0x75, 0xcc, 0x1f, 0x10, 0xf2, 0x84, 0xed, + 0xdf, 0x07, 0xe8, 0x7f, 0x76, 0x3f, 0xf2, 0x84, 0x6d, 0x8f, 0xd2, 0x82, 0x1f, 0xf4, 0xab, 0x6d, + 0xd7, 0xf4, 0x4a, 0xfe, 0x33, 0x18, 0x2f, 0xd7, 0x16, 0x58, 0xad, 0x2d, 0xf0, 0xb2, 0xb6, 0xc0, + 0xfd, 0xc6, 0x32, 0x56, 0x1b, 0xcb, 0x78, 0xda, 0x58, 0xc6, 0xf5, 0x49, 0x10, 0xaa, 0x69, 0xe2, + 0xd9, 0x44, 0xb0, 0xb7, 0xb3, 0x30, 0xff, 0xff, 0x87, 0x4c, 0x71, 0xc8, 0x9d, 0x5d, 0x25, 0xcd, + 0xe7, 0x43, 0x2d, 0x66, 0x34, 0xf6, 0x9a, 0xba, 0xfc, 0xef, 0x35, 0x00, 0x00, 0xff, 0xff, 0x04, + 0x33, 0x7f, 0xa5, 0x41, 0x03, 0x00, 0x00, } func (m *BlockRateLimitConfiguration) Marshal() (dAtA []byte, err error) { @@ -214,6 +237,20 @@ func (m *BlockRateLimitConfiguration) MarshalToSizedBuffer(dAtA []byte) (int, er _ = i var l int _ = l + if len(m.MaxShortTermOrdersAndCancelsPerNBlocks) > 0 { + for iNdEx := len(m.MaxShortTermOrdersAndCancelsPerNBlocks) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.MaxShortTermOrdersAndCancelsPerNBlocks[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintBlockRateLimitConfig(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x22 + } + } if len(m.MaxShortTermOrderCancellationsPerNBlocks) > 0 { for iNdEx := len(m.MaxShortTermOrderCancellationsPerNBlocks) - 1; iNdEx >= 0; iNdEx-- { { @@ -327,6 +364,12 @@ func (m *BlockRateLimitConfiguration) Size() (n int) { n += 1 + l + sovBlockRateLimitConfig(uint64(l)) } } + if len(m.MaxShortTermOrdersAndCancelsPerNBlocks) > 0 { + for _, e := range m.MaxShortTermOrdersAndCancelsPerNBlocks { + l = e.Size() + n += 1 + l + sovBlockRateLimitConfig(uint64(l)) + } + } return n } @@ -482,6 +525,40 @@ func (m *BlockRateLimitConfiguration) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 4: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field MaxShortTermOrdersAndCancelsPerNBlocks", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowBlockRateLimitConfig + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthBlockRateLimitConfig + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthBlockRateLimitConfig + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.MaxShortTermOrdersAndCancelsPerNBlocks = append(m.MaxShortTermOrdersAndCancelsPerNBlocks, MaxPerNBlocksRateLimit{}) + if err := m.MaxShortTermOrdersAndCancelsPerNBlocks[len(m.MaxShortTermOrdersAndCancelsPerNBlocks)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipBlockRateLimitConfig(dAtA[iNdEx:]) diff --git a/protocol/x/clob/types/clob_keeper.go b/protocol/x/clob/types/clob_keeper.go index 795b3d3f61..d96b046ad5 100644 --- a/protocol/x/clob/types/clob_keeper.go +++ b/protocol/x/clob/types/clob_keeper.go @@ -40,6 +40,14 @@ type ClobKeeper interface { ClobPair, error, ) + HandleMsgCancelOrder( + ctx sdk.Context, + msg *MsgCancelOrder, + ) (err error) + HandleMsgPlaceOrder( + ctx sdk.Context, + msg *MsgPlaceOrder, + ) (err error) GetAllClobPairs(ctx sdk.Context) (list []ClobPair) GetClobPair(ctx sdk.Context, id ClobPairId) (val ClobPair, found bool) HasAuthority(authority string) bool @@ -49,6 +57,7 @@ type ClobKeeper interface { err error, ) PlaceStatefulOrder(ctx sdk.Context, msg *MsgPlaceOrder) error + PruneStateFillAmountsForShortTermOrders( ctx sdk.Context, ) @@ -123,7 +132,11 @@ type ClobKeeper interface { GetIndexerEventManager() indexer_manager.IndexerEventManager RateLimitCancelOrder(ctx sdk.Context, order *MsgCancelOrder) error RateLimitPlaceOrder(ctx sdk.Context, order *MsgPlaceOrder) error + RateLimitBatchCancel(ctx sdk.Context, order *MsgBatchCancel) error InitializeBlockRateLimit(ctx sdk.Context, config BlockRateLimitConfiguration) error + GetBlockRateLimitConfiguration( + ctx sdk.Context, + ) (config BlockRateLimitConfiguration) InitializeEquityTierLimit(ctx sdk.Context, config EquityTierLimitConfiguration) error Logger(ctx sdk.Context) log.Logger UpdateClobPair( @@ -131,5 +144,10 @@ type ClobKeeper interface { clobPair ClobPair, ) error UpdateLiquidationsConfig(ctx sdk.Context, config LiquidationsConfig) error + // Gprc streaming InitializeNewGrpcStreams(ctx sdk.Context) + SendOrderbookUpdates( + offchainUpdates *OffchainUpdates, + snapshot bool, + ) } diff --git a/protocol/x/clob/types/genesis_test.go b/protocol/x/clob/types/genesis_test.go index 8cededd9cc..2bb266fd95 100644 --- a/protocol/x/clob/types/genesis_test.go +++ b/protocol/x/clob/types/genesis_test.go @@ -25,14 +25,14 @@ func TestGenesisState_Validate(t *testing.T) { "valid genesis state": { genState: &types.GenesisState{ BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []types.MaxPerNBlocksRateLimit{ { NumBlocks: 1, Limit: 1, }, { - NumBlocks: types.MaxShortTermOrdersPerNBlocksNumBlocks, - Limit: types.MaxShortTermOrdersPerNBlocksLimit, + NumBlocks: types.MaxShortTermOrdersAndCancelsPerNBlocksNumBlocks, + Limit: types.MaxShortTermOrdersAndCancelsPerNBlocksLimit, }, }, MaxStatefulOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{ @@ -45,16 +45,6 @@ func TestGenesisState_Validate(t *testing.T) { Limit: types.MaxStatefulOrdersPerNBlocksLimit, }, }, - MaxShortTermOrderCancellationsPerNBlocks: []types.MaxPerNBlocksRateLimit{ - { - NumBlocks: 1, - Limit: 1, - }, - { - NumBlocks: types.MaxShortTermOrderCancellationsPerNBlocksNumBlocks, - Limit: types.MaxShortTermOrderCancellationsPerNBlocksLimit, - }, - }, }, ClobPairs: []types.ClobPair{ { @@ -269,7 +259,7 @@ func TestGenesisState_Validate(t *testing.T) { "max num blocks for short term order rate limit is zero": { genState: &types.GenesisState{ BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []types.MaxPerNBlocksRateLimit{ { NumBlocks: 0, Limit: 1, @@ -277,12 +267,12 @@ func TestGenesisState_Validate(t *testing.T) { }, }, }, - expectedError: errors.New("0 is not a valid NumBlocks for MaxShortTermOrdersPerNBlocks"), + expectedError: errors.New("0 is not a valid NumBlocks for MaxShortTermOrdersAndCancelsPerNBlocks"), }, "max limit for short term order rate limit is zero": { genState: &types.GenesisState{ BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []types.MaxPerNBlocksRateLimit{ { NumBlocks: 1, Limit: 0, @@ -290,7 +280,7 @@ func TestGenesisState_Validate(t *testing.T) { }, }, }, - expectedError: errors.New("0 is not a valid Limit for MaxShortTermOrdersPerNBlocks"), + expectedError: errors.New("0 is not a valid Limit for MaxShortTermOrdersAndCancelsPerNBlocks"), }, "max num blocks for stateful order rate limit is zero": { genState: &types.GenesisState{ @@ -318,59 +308,33 @@ func TestGenesisState_Validate(t *testing.T) { }, expectedError: errors.New("0 is not a valid Limit for MaxStatefulOrdersPerNBlocks"), }, - "max num blocks for short term order cancellation rate limit is zero": { - genState: &types.GenesisState{ - BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrderCancellationsPerNBlocks: []types.MaxPerNBlocksRateLimit{ - { - NumBlocks: 0, - Limit: 1, - }, - }, - }, - }, - expectedError: errors.New("0 is not a valid NumBlocks for MaxShortTermOrderCancellationsPerNBlocks"), - }, - "max limit for short term order cancellation rate limit is zero": { - genState: &types.GenesisState{ - BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrderCancellationsPerNBlocks: []types.MaxPerNBlocksRateLimit{ - { - NumBlocks: 1, - Limit: 0, - }, - }, - }, - }, - expectedError: errors.New("0 is not a valid Limit for MaxShortTermOrderCancellationsPerNBlocks"), - }, "max num blocks for short term order rate limit is greater than max": { genState: &types.GenesisState{ BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []types.MaxPerNBlocksRateLimit{ { - NumBlocks: types.MaxShortTermOrdersPerNBlocksNumBlocks + 1, + NumBlocks: types.MaxShortTermOrdersAndCancelsPerNBlocksNumBlocks + 1, Limit: 1, }, }, }, }, - expectedError: fmt.Errorf("%d is not a valid NumBlocks for MaxShortTermOrdersPerNBlocks", - types.MaxShortTermOrdersPerNBlocksNumBlocks+1), + expectedError: fmt.Errorf("%d is not a valid NumBlocks for MaxShortTermOrdersAndCancelsPerNBlocks", + types.MaxShortTermOrdersAndCancelsPerNBlocksNumBlocks+1), }, "max limit for short term order rate limit is greater than max": { genState: &types.GenesisState{ BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []types.MaxPerNBlocksRateLimit{ { NumBlocks: 1, - Limit: types.MaxShortTermOrdersPerNBlocksLimit + 1, + Limit: types.MaxShortTermOrdersAndCancelsPerNBlocksLimit + 1, }, }, }, }, - expectedError: fmt.Errorf("%d is not a valid Limit for MaxShortTermOrdersPerNBlocks", - types.MaxShortTermOrdersPerNBlocksLimit+1), + expectedError: fmt.Errorf("%d is not a valid Limit for MaxShortTermOrdersAndCancelsPerNBlocks", + types.MaxShortTermOrdersAndCancelsPerNBlocksLimit+1), }, "max num blocks for stateful order rate limit is greater than max": { genState: &types.GenesisState{ @@ -400,38 +364,10 @@ func TestGenesisState_Validate(t *testing.T) { expectedError: fmt.Errorf("%d is not a valid Limit for MaxStatefulOrdersPerNBlocks", types.MaxStatefulOrdersPerNBlocksLimit+1), }, - "max num blocks for short term order cancellation rate limit is greater than max": { - genState: &types.GenesisState{ - BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrderCancellationsPerNBlocks: []types.MaxPerNBlocksRateLimit{ - { - NumBlocks: types.MaxShortTermOrderCancellationsPerNBlocksNumBlocks + 1, - Limit: 1, - }, - }, - }, - }, - expectedError: fmt.Errorf("%d is not a valid NumBlocks for MaxShortTermOrderCancellationsPerNBlocks", - types.MaxShortTermOrdersPerNBlocksNumBlocks+1), - }, - "max limit for short term order cancellation rate limit is greater than max": { - genState: &types.GenesisState{ - BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrderCancellationsPerNBlocks: []types.MaxPerNBlocksRateLimit{ - { - NumBlocks: 1, - Limit: types.MaxShortTermOrderCancellationsPerNBlocksLimit + 1, - }, - }, - }, - }, - expectedError: fmt.Errorf("%d is not a valid Limit for MaxShortTermOrderCancellationsPerNBlocks", - types.MaxShortTermOrdersPerNBlocksLimit+1), - }, "duplicate short term order rate limit NumBlocks not allowed": { genState: &types.GenesisState{ BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrdersPerNBlocks: []types.MaxPerNBlocksRateLimit{ + MaxShortTermOrdersAndCancelsPerNBlocks: []types.MaxPerNBlocksRateLimit{ { NumBlocks: 1, Limit: 1, @@ -462,23 +398,6 @@ func TestGenesisState_Validate(t *testing.T) { }, expectedError: fmt.Errorf("Multiple rate limits"), }, - "duplicate short term order cancellation rate limit NumBlocks not allowed": { - genState: &types.GenesisState{ - BlockRateLimitConfig: types.BlockRateLimitConfiguration{ - MaxShortTermOrderCancellationsPerNBlocks: []types.MaxPerNBlocksRateLimit{ - { - NumBlocks: 1, - Limit: 1, - }, - { - NumBlocks: 1, - Limit: 2, - }, - }, - }, - }, - expectedError: fmt.Errorf("Multiple rate limits"), - }, "out of order short term order equity tier limit UsdTncRequired not allowed": { genState: &types.GenesisState{ EquityTierLimitConfig: types.EquityTierLimitConfiguration{ diff --git a/protocol/x/clob/types/mem_clob_keeper.go b/protocol/x/clob/types/mem_clob_keeper.go index 298e701cb9..7ca02f2569 100644 --- a/protocol/x/clob/types/mem_clob_keeper.go +++ b/protocol/x/clob/types/mem_clob_keeper.go @@ -115,4 +115,8 @@ type MemClobKeeper interface { Logger( ctx sdk.Context, ) log.Logger + SendOrderbookUpdates( + offchainUpdates *OffchainUpdates, + snapshot bool, + ) } diff --git a/protocol/x/clob/types/memclob.go b/protocol/x/clob/types/memclob.go index feb4ab6f49..3196320e39 100644 --- a/protocol/x/clob/types/memclob.go +++ b/protocol/x/clob/types/memclob.go @@ -137,4 +137,16 @@ type MemClob interface { ctx sdk.Context, clobPairId ClobPairId, ) (offchainUpdates *OffchainUpdates) + GetOrderbookUpdatesForOrderPlacement( + ctx sdk.Context, + order Order, + ) (offchainUpdates *OffchainUpdates) + GetOrderbookUpdatesForOrderRemoval( + ctx sdk.Context, + orderId OrderId, + ) (offchainUpdates *OffchainUpdates) + GetOrderbookUpdatesForOrderUpdate( + ctx sdk.Context, + orderId OrderId, + ) (offchainUpdates *OffchainUpdates) } diff --git a/protocol/x/clob/types/message_batch_cancel.go b/protocol/x/clob/types/message_batch_cancel.go index 675f348577..81c79eee1a 100644 --- a/protocol/x/clob/types/message_batch_cancel.go +++ b/protocol/x/clob/types/message_batch_cancel.go @@ -37,7 +37,19 @@ func (msg *MsgBatchCancel) ValidateBasic() (err error) { ) } totalNumberCancels := 0 + seenClobPairIds := map[uint32]struct{}{} for _, cancelBatch := range cancelBatches { + // Check for duplicate clob pair ids across all cancel batches + clobPairId := cancelBatch.GetClobPairId() + if _, exists := seenClobPairIds[clobPairId]; exists { + return errorsmod.Wrapf( + ErrInvalidBatchCancel, + "Batch cancel cannot have 2 order batches with the same clob pair id: %+v", + clobPairId, + ) + } + seenClobPairIds[clobPairId] = struct{}{} + numClientIds := len(cancelBatch.GetClientIds()) if numClientIds == 0 { return errorsmod.Wrapf( @@ -46,12 +58,14 @@ func (msg *MsgBatchCancel) ValidateBasic() (err error) { ) } totalNumberCancels += numClientIds + + // Check for duplicate clob client ids across this cancel batch seenClientIds := map[uint32]struct{}{} for _, clientId := range cancelBatch.GetClientIds() { if _, seen := seenClientIds[clientId]; seen { return errorsmod.Wrapf( ErrInvalidBatchCancel, - "Batch cancel cannot have duplicate cancels. Duplicate clob pair id: %+v, client id: %+v", + "Batch Cancel Clob pair id %+v has duplicate client ids: %+v", cancelBatch.GetClobPairId(), clientId, ) diff --git a/protocol/x/clob/types/message_batch_cancel_test.go b/protocol/x/clob/types/message_batch_cancel_test.go index 504ee7c992..b2bab0df8c 100644 --- a/protocol/x/clob/types/message_batch_cancel_test.go +++ b/protocol/x/clob/types/message_batch_cancel_test.go @@ -95,6 +95,27 @@ func TestMsgBatchCancel_ValidateBasic(t *testing.T) { err: nil, }, "duplicate clob pair ids": { + msg: *types.NewMsgBatchCancel( + constants.Alice_Num0, + []types.OrderBatch{ + { + ClobPairId: 0, + ClientIds: []uint32{ + 0, 1, 2, 3, + }, + }, + { + ClobPairId: 0, + ClientIds: []uint32{ + 2, 3, 4, + }, + }, + }, + 10, + ), + err: types.ErrInvalidBatchCancel, + }, + "duplicate client ids": { msg: *types.NewMsgBatchCancel( constants.Alice_Num0, []types.OrderBatch{ diff --git a/protocol/x/perpetuals/keeper/msg_server_update_perpetual_params_test.go b/protocol/x/perpetuals/keeper/msg_server_update_perpetual_params_test.go index 4589405fd7..b562b8a621 100644 --- a/protocol/x/perpetuals/keeper/msg_server_update_perpetual_params_test.go +++ b/protocol/x/perpetuals/keeper/msg_server_update_perpetual_params_test.go @@ -24,8 +24,8 @@ func TestUpdatePerpetualParams(t *testing.T) { perptest.WithTicker("ETH-USD"), perptest.WithLiquidityTier(1), ) - testMarket1 := *pricestest.GenerateMarketParamPrice(pricestest.WithId(1)) - testMarket4 := *pricestest.GenerateMarketParamPrice(pricestest.WithId(4)) + testMarket1 := *pricestest.GenerateMarketParamPrice(pricestest.WithId(1), pricestest.WithPair("0-0")) + testMarket4 := *pricestest.GenerateMarketParamPrice(pricestest.WithId(4), pricestest.WithPair("1-1")) tests := map[string]struct { setup func(*testing.T, sdk.Context, *perpkeeper.Keeper, *priceskeeper.Keeper) diff --git a/protocol/x/prices/client/cli/prices_cli_test.go b/protocol/x/prices/client/cli/prices_cli_test.go index f4560775a3..76e61cbf2c 100644 --- a/protocol/x/prices/client/cli/prices_cli_test.go +++ b/protocol/x/prices/client/cli/prices_cli_test.go @@ -95,6 +95,7 @@ func (s *PricesIntegrationTestSuite) SetupTest() { // Enable the Price daemon. appOptions.Set(daemonflags.FlagPriceDaemonEnabled, true) appOptions.Set(daemonflags.FlagPriceDaemonLoopDelayMs, 1_000) + appOptions.Set(daemonflags.FlagOracleEnabled, false) homeDir := filepath.Join(testval.Dir, "simd") configs.WriteDefaultPricefeedExchangeToml(homeDir) // must manually create config file. diff --git a/protocol/x/prices/genesis_test.go b/protocol/x/prices/genesis_test.go index aef348f7db..3d76af63ac 100644 --- a/protocol/x/prices/genesis_test.go +++ b/protocol/x/prices/genesis_test.go @@ -33,7 +33,7 @@ func TestExportGenesis(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, k, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, k, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) prices.InitGenesis(ctx, *k, *tc.genesisState) @@ -50,7 +50,7 @@ func TestExportGenesis(t *testing.T) { } func TestExportGenesis_WithMutation(t *testing.T) { - ctx, k, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, k, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) prices.InitGenesis(ctx, *k, *types.DefaultGenesis()) @@ -94,7 +94,7 @@ func invalidGenesis() types.GenesisState { } func TestInitGenesis_Panics(t *testing.T) { - ctx, k, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, k, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) // Verify InitGenesis panics when given an invalid genesis state. @@ -104,7 +104,7 @@ func TestInitGenesis_Panics(t *testing.T) { } func TestInitGenesisEmitsMarketUpdates(t *testing.T) { - ctx, k, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, k, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) prices.InitGenesis(ctx, *k, constants.Prices_DefaultGenesisState) diff --git a/protocol/x/prices/keeper/grpc_query_market_test.go b/protocol/x/prices/keeper/grpc_query_market_test.go index d32772495a..959cd646e0 100644 --- a/protocol/x/prices/keeper/grpc_query_market_test.go +++ b/protocol/x/prices/keeper/grpc_query_market_test.go @@ -15,7 +15,7 @@ import ( ) func TestMarketPriceQuerySingle(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) msgs := keepertest.CreateNMarkets(t, ctx, keeper, 2) for _, tc := range []struct { @@ -66,7 +66,7 @@ func TestMarketPriceQuerySingle(t *testing.T) { } func TestMarketPriceQueryPaginated(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) msgs := keepertest.CreateNMarkets(t, ctx, keeper, 5) prices := make([]types.MarketPrice, len(msgs)) @@ -126,7 +126,7 @@ func TestMarketPriceQueryPaginated(t *testing.T) { } func TestMarketParamQuerySingle(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) msgs := keepertest.CreateNMarkets(t, ctx, keeper, 2) for _, tc := range []struct { @@ -177,7 +177,7 @@ func TestMarketParamQuerySingle(t *testing.T) { } func TestMarketParamQueryPaginated(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) msgs := keepertest.CreateNMarkets(t, ctx, keeper, 5) params := make([]types.MarketParam, len(msgs)) diff --git a/protocol/x/prices/keeper/keeper.go b/protocol/x/prices/keeper/keeper.go index 0673574ff0..96a95c74ce 100644 --- a/protocol/x/prices/keeper/keeper.go +++ b/protocol/x/prices/keeper/keeper.go @@ -17,14 +17,13 @@ import ( type ( Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey - indexPriceCache *pricefeedtypes.MarketToExchangePrices - marketToSmoothedPrices types.MarketToSmoothedPrices - timeProvider libtime.TimeProvider - indexerEventManager indexer_manager.IndexerEventManager - marketToCreatedAt map[uint32]time.Time - authorities map[string]struct{} + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + indexPriceCache *pricefeedtypes.MarketToExchangePrices + timeProvider libtime.TimeProvider + indexerEventManager indexer_manager.IndexerEventManager + marketToCreatedAt map[uint32]time.Time + authorities map[string]struct{} } ) @@ -34,20 +33,18 @@ func NewKeeper( cdc codec.BinaryCodec, storeKey storetypes.StoreKey, indexPriceCache *pricefeedtypes.MarketToExchangePrices, - marketToSmoothedPrices types.MarketToSmoothedPrices, timeProvider libtime.TimeProvider, indexerEventManager indexer_manager.IndexerEventManager, authorities []string, ) *Keeper { return &Keeper{ - cdc: cdc, - storeKey: storeKey, - indexPriceCache: indexPriceCache, - marketToSmoothedPrices: marketToSmoothedPrices, - timeProvider: timeProvider, - indexerEventManager: indexerEventManager, - marketToCreatedAt: map[uint32]time.Time{}, - authorities: lib.UniqueSliceToSet(authorities), + cdc: cdc, + storeKey: storeKey, + indexPriceCache: indexPriceCache, + timeProvider: timeProvider, + indexerEventManager: indexerEventManager, + marketToCreatedAt: map[uint32]time.Time{}, + authorities: lib.UniqueSliceToSet(authorities), } } diff --git a/protocol/x/prices/keeper/keeper_test.go b/protocol/x/prices/keeper/keeper_test.go index 5eaeb54de4..a97d761df8 100644 --- a/protocol/x/prices/keeper/keeper_test.go +++ b/protocol/x/prices/keeper/keeper_test.go @@ -8,7 +8,7 @@ import ( ) func TestLogger(t *testing.T) { - ctx, keeper, _, _, _, _ := keepertest.PricesKeepers(t) + ctx, keeper, _, _, _ := keepertest.PricesKeepers(t) logger := keeper.Logger(ctx) require.NotNil(t, logger) } diff --git a/protocol/x/prices/keeper/market.go b/protocol/x/prices/keeper/market.go index fa55d6e869..cf7bb08f2e 100644 --- a/protocol/x/prices/keeper/market.go +++ b/protocol/x/prices/keeper/market.go @@ -36,6 +36,15 @@ func (k Keeper) CreateMarket( if err := marketPrice.ValidateFromParam(marketParam); err != nil { return types.MarketParam{}, err } + // Stateful Validation + for _, market := range k.GetAllMarketParams(ctx) { + if market.Pair == marketParam.Pair { + return types.MarketParam{}, errorsmod.Wrapf( + types.ErrMarketParamPairAlreadyExists, + marketParam.Pair, + ) + } + } paramBytes := k.cdc.MustMarshal(&marketParam) priceBytes := k.cdc.MustMarshal(&marketPrice) diff --git a/protocol/x/prices/keeper/market_param.go b/protocol/x/prices/keeper/market_param.go index be3eaab810..fe3ff3c6fa 100644 --- a/protocol/x/prices/keeper/market_param.go +++ b/protocol/x/prices/keeper/market_param.go @@ -43,6 +43,11 @@ func (k Keeper) ModifyMarketParam( return types.MarketParam{}, errorsmod.Wrapf(types.ErrMarketExponentCannotBeUpdated, lib.UintToString(updatedMarketParam.Id)) } + for _, market := range k.GetAllMarketParams(ctx) { + if market.Pair == updatedMarketParam.Pair && market.Id != updatedMarketParam.Id { + return types.MarketParam{}, errorsmod.Wrapf(types.ErrMarketParamPairAlreadyExists, updatedMarketParam.Pair) + } + } // Store the modified market param. marketParamStore := k.getMarketParamStore(ctx) diff --git a/protocol/x/prices/keeper/market_param_test.go b/protocol/x/prices/keeper/market_param_test.go index 709a82a438..2c1ce910b1 100644 --- a/protocol/x/prices/keeper/market_param_test.go +++ b/protocol/x/prices/keeper/market_param_test.go @@ -15,7 +15,7 @@ import ( ) func TestModifyMarketParam(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) ctx = ctx.WithTxBytes(constants.TestTxBytes) items := keepertest.CreateNMarkets(t, ctx, keeper, 10) @@ -26,7 +26,7 @@ func TestModifyMarketParam(t *testing.T) { ctx, types.MarketParam{ Id: item.Param.Id, - Pair: fmt.Sprintf("foo_%v", i), + Pair: item.Param.Pair, MinExchanges: uint32(2), Exponent: item.Param.Exponent, MinPriceChangePpm: uint32(9_999 - i), @@ -35,11 +35,11 @@ func TestModifyMarketParam(t *testing.T) { ) require.NoError(t, err) require.Equal(t, uint32(i), newItem.Id) - require.Equal(t, fmt.Sprintf("foo_%v", i), newItem.Pair) + require.Equal(t, fmt.Sprintf("%v-%v", i, i), newItem.Pair) require.Equal(t, item.Param.Exponent, newItem.Exponent) require.Equal(t, uint32(2), newItem.MinExchanges) require.Equal(t, uint32(9999-i), newItem.MinPriceChangePpm) - require.Equal(t, fmt.Sprintf("foo_%v", i), metrics.GetMarketPairForTelemetry(item.Param.Id)) + require.Equal(t, fmt.Sprintf("%v-%v", i, i), metrics.GetMarketPairForTelemetry(item.Param.Id)) require.Equal(t, fmt.Sprintf(`{"id":"%v"}`, i), newItem.ExchangeConfigJson) keepertest.AssertMarketModifyEventInIndexerBlock(t, keeper, ctx, newItem) } @@ -109,13 +109,24 @@ func TestModifyMarketParam_Errors(t *testing.T) { "", ).Error(), }, + "Updating pair fails": { + targetId: 0, + pair: "1-1", + minExchanges: uint32(1), + minPriceChangePpm: uint32(50), + exchangeConfigJson: validExchangeConfigJson, + expectedErr: errorsmod.Wrapf( + types.ErrMarketParamPairAlreadyExists, + "1-1", + ).Error(), + }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, _, _, _, mockTimeKeeper := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeKeeper := keepertest.PricesKeepers(t) mockTimeKeeper.On("Now").Return(constants.TimeT) ctx = ctx.WithTxBytes(constants.TestTxBytes) - keepertest.CreateNMarkets(t, ctx, keeper, 1) + keepertest.CreateNMarkets(t, ctx, keeper, 2) _, err := keeper.ModifyMarketParam( ctx, types.MarketParam{ @@ -132,7 +143,7 @@ func TestModifyMarketParam_Errors(t *testing.T) { } func TestGetMarketParam(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) items := keepertest.CreateNMarkets(t, ctx, keeper, 10) for _, item := range items { @@ -147,13 +158,13 @@ func TestGetMarketParam(t *testing.T) { } func TestGetMarketParam_NotFound(t *testing.T) { - ctx, keeper, _, _, _, _ := keepertest.PricesKeepers(t) + ctx, keeper, _, _, _ := keepertest.PricesKeepers(t) _, exists := keeper.GetMarketParam(ctx, uint32(0)) require.False(t, exists) } func TestGetAllMarketParams(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) items := keepertest.CreateNMarkets(t, ctx, keeper, 10) params := make([]types.MarketParam, len(items)) diff --git a/protocol/x/prices/keeper/market_price_test.go b/protocol/x/prices/keeper/market_price_test.go index c41be0114b..35b720c59f 100644 --- a/protocol/x/prices/keeper/market_price_test.go +++ b/protocol/x/prices/keeper/market_price_test.go @@ -25,7 +25,7 @@ func createNMarketPriceUpdates( } func TestUpdateMarketPrices(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) ctx = ctx.WithTxBytes(constants.TestTxBytes) items := keepertest.CreateNMarkets(t, ctx, keeper, 10) @@ -60,7 +60,7 @@ func TestUpdateMarketPrices(t *testing.T) { } func TestUpdateMarketPrices_NotFound(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) ctx = ctx.WithTxBytes(constants.TestTxBytes) priceUpdates := createNMarketPriceUpdates(10) @@ -89,7 +89,7 @@ func TestUpdateMarketPrices_NotFound(t *testing.T) { } func TestGetMarketPrice(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) items := keepertest.CreateNMarkets(t, ctx, keeper, 10) for _, item := range items { @@ -104,13 +104,13 @@ func TestGetMarketPrice(t *testing.T) { } func TestGetMarketPrice_NotFound(t *testing.T) { - ctx, keeper, _, _, _, _ := keepertest.PricesKeepers(t) + ctx, keeper, _, _, _ := keepertest.PricesKeepers(t) _, err := keeper.GetMarketPrice(ctx, uint32(0)) require.EqualError(t, err, "0: Market price does not exist") } func TestGetAllMarketPrices(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) items := keepertest.CreateNMarkets(t, ctx, keeper, 10) prices := make([]types.MarketPrice, len(items)) @@ -126,7 +126,7 @@ func TestGetAllMarketPrices(t *testing.T) { } func TestGetMarketIdToValidIndexPrice(t *testing.T) { - ctx, keeper, _, indexPriceCache, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) // Now() is used by `GetMarketIdToValidIndexPrice` internally compare with the cutoff time // of each price. mockTimeProvider.On("Now").Return(constants.TimeT) @@ -135,19 +135,23 @@ func TestGetMarketIdToValidIndexPrice(t *testing.T) { keeper, []types.MarketParamPrice{ *pricestest.GenerateMarketParamPrice( + pricestest.WithPair("0-0"), pricestest.WithId(6), pricestest.WithMinExchanges(2), ), *pricestest.GenerateMarketParamPrice( + pricestest.WithPair("1-1"), pricestest.WithId(7), pricestest.WithMinExchanges(2), ), *pricestest.GenerateMarketParamPrice( + pricestest.WithPair("2-2"), pricestest.WithId(8), pricestest.WithMinExchanges(2), pricestest.WithExponent(-8), ), *pricestest.GenerateMarketParamPrice( + pricestest.WithPair("3-3"), pricestest.WithId(9), pricestest.WithMinExchanges(2), pricestest.WithExponent(-9), diff --git a/protocol/x/prices/keeper/market_test.go b/protocol/x/prices/keeper/market_test.go index 448ca37755..35872d4091 100644 --- a/protocol/x/prices/keeper/market_test.go +++ b/protocol/x/prices/keeper/market_test.go @@ -14,7 +14,7 @@ import ( ) func TestCreateMarket(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) ctx = ctx.WithTxBytes(constants.TestTxBytes) @@ -88,7 +88,7 @@ func TestMarketIsRecentlyAvailable(t *testing.T) { } for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) // Create market with TimeT creation timestamp. mockTimeProvider.On("Now").Return(constants.TimeT).Once() @@ -158,7 +158,7 @@ func TestCreateMarket_Errors(t *testing.T) { exchangeConfigJson: validExchangeConfigJson, expectedErr: errorsmod.Wrap( types.ErrInvalidInput, - "market param id 0 does not match market price id 1", + "market param id 1 does not match market price id 2", ).Error(), }, "Market param and price exponents don't match": { @@ -170,15 +170,29 @@ func TestCreateMarket_Errors(t *testing.T) { exchangeConfigJson: validExchangeConfigJson, expectedErr: errorsmod.Wrap( types.ErrInvalidInput, - "market param 0 exponent -6 does not match market price 0 exponent -5", + "market param 1 exponent -6 does not match market price 1 exponent -5", + ).Error(), + }, + "Pair already exists": { + pair: "0-0", + minExchanges: uint32(2), + minPriceChangePpm: uint32(50), + price: constants.FiveBillion, + exchangeConfigJson: validExchangeConfigJson, + expectedErr: errorsmod.Wrap( + types.ErrMarketParamPairAlreadyExists, + "0-0", ).Error(), }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, keeper, _, _, _, _ := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeKeeper := keepertest.PricesKeepers(t) ctx = ctx.WithTxBytes(constants.TestTxBytes) + mockTimeKeeper.On("Now").Return(constants.TimeT) + keepertest.CreateNMarkets(t, ctx, keeper, 1) + marketPriceIdOffset := uint32(0) if tc.marketPriceIdDoesntMatchMarketParamId { marketPriceIdOffset = uint32(1) @@ -192,7 +206,7 @@ func TestCreateMarket_Errors(t *testing.T) { _, err := keeper.CreateMarket( ctx, types.MarketParam{ - Id: 0, + Id: 1, Pair: tc.pair, Exponent: int32(-6), MinExchanges: tc.minExchanges, @@ -200,7 +214,7 @@ func TestCreateMarket_Errors(t *testing.T) { ExchangeConfigJson: tc.exchangeConfigJson, }, types.MarketPrice{ - Id: 0 + marketPriceIdOffset, + Id: 1 + marketPriceIdOffset, Exponent: int32(-6) + marketPriceExponentOffset, Price: tc.price, }, @@ -208,21 +222,21 @@ func TestCreateMarket_Errors(t *testing.T) { require.EqualError(t, err, tc.expectedErr) // Verify no new MarketPrice created. - _, err = keeper.GetMarketPrice(ctx, 0) + _, err = keeper.GetMarketPrice(ctx, 1) require.EqualError( t, err, - errorsmod.Wrap(types.ErrMarketPriceDoesNotExist, lib.UintToString(uint32(0))).Error(), + errorsmod.Wrap(types.ErrMarketPriceDoesNotExist, lib.UintToString(uint32(1))).Error(), ) // Verify no new market event. - keepertest.AssertMarketEventsNotInIndexerBlock(t, keeper, ctx) + keepertest.AssertNMarketEventsNotInIndexerBlock(t, keeper, ctx, 1) }) } } func TestGetAllMarketParamPrices(t *testing.T) { - ctx, keeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) items := keepertest.CreateNMarkets(t, ctx, keeper, 10) diff --git a/protocol/x/prices/keeper/msg_server_create_oracle_market_test.go b/protocol/x/prices/keeper/msg_server_create_oracle_market_test.go index b9b15738cc..ed9bd80919 100644 --- a/protocol/x/prices/keeper/msg_server_create_oracle_market_test.go +++ b/protocol/x/prices/keeper/msg_server_create_oracle_market_test.go @@ -90,7 +90,7 @@ func TestCreateOracleMarket(t *testing.T) { for name, tc := range testCases { t.Run(name, func(t *testing.T) { - ctx, pricesKeeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, pricesKeeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) msgServer := keeper.NewMsgServerImpl(pricesKeeper) tc.setup(t, ctx, pricesKeeper) diff --git a/protocol/x/prices/keeper/msg_server_update_market_param_test.go b/protocol/x/prices/keeper/msg_server_update_market_param_test.go index b3d57424f7..fb2613f082 100644 --- a/protocol/x/prices/keeper/msg_server_update_market_param_test.go +++ b/protocol/x/prices/keeper/msg_server_update_market_param_test.go @@ -30,7 +30,7 @@ func TestUpdateMarketParam(t *testing.T) { Authority: lib.GovModuleAddress.String(), MarketParam: pricestypes.MarketParam{ Id: testMarketParam.Id, - Pair: "PIKACHU-XXX", + Pair: testMarketParam.Pair, Exponent: testMarketParam.Exponent, MinExchanges: 72, MinPriceChangePpm: 2_023, @@ -143,7 +143,7 @@ func TestUpdateMarketParam(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { - ctx, pricesKeeper, _, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, pricesKeeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) msgServer := keeper.NewMsgServerImpl(pricesKeeper) initialMarketParam, err := pricesKeeper.CreateMarket(ctx, testMarketParam, testMarketPrice) diff --git a/protocol/x/prices/keeper/msg_server_update_market_prices_test.go b/protocol/x/prices/keeper/msg_server_update_market_prices_test.go index 145d363cbe..01cf2aabc1 100644 --- a/protocol/x/prices/keeper/msg_server_update_market_prices_test.go +++ b/protocol/x/prices/keeper/msg_server_update_market_prices_test.go @@ -48,6 +48,7 @@ func TestUpdateMarketPrices_Valid(t *testing.T) { constants.MarketId1: constants.ThreeBillion, // no change constants.MarketId2: constants.FiveBillion, // no change constants.MarketId3: constants.FiveBillion, // no change + constants.MarketId4: constants.ThreeBillion, // no change }, }, "Multiple updates": { @@ -58,6 +59,7 @@ func TestUpdateMarketPrices_Valid(t *testing.T) { constants.MarketId1: constants.Price6, constants.MarketId2: constants.Price7, constants.MarketId3: constants.Price4, + constants.MarketId4: constants.Price3, }, }, "Towards index price = true (current < update < index price)": { @@ -81,6 +83,7 @@ func TestUpdateMarketPrices_Valid(t *testing.T) { constants.MarketId1: constants.ThreeBillion, // no change constants.MarketId2: constants.FiveBillion, // no change constants.MarketId3: constants.FiveBillion, // no change + constants.MarketId4: constants.ThreeBillion, // no change }, }, "Index price crossing = true (price increase), old_ticks > 1, new_ticks <= sqrt(old_ticks) = true": { @@ -104,6 +107,7 @@ func TestUpdateMarketPrices_Valid(t *testing.T) { constants.MarketId1: constants.ThreeBillion, // no change constants.MarketId2: constants.FiveBillion, // no change constants.MarketId3: constants.FiveBillion, // no change + constants.MarketId4: constants.ThreeBillion, // no change }, }, "Index price crossing = true (price decrease), old_ticks > 1, new_ticks <= sqrt(old_ticks) = true": { @@ -127,6 +131,7 @@ func TestUpdateMarketPrices_Valid(t *testing.T) { constants.MarketId1: constants.ThreeBillion, // no change constants.MarketId2: constants.FiveBillion, // no change constants.MarketId3: constants.FiveBillion, // no change + constants.MarketId4: constants.ThreeBillion, // no change }, }, "Index price crossing = true (price increase), old_ticks <= 1, new_ticks <= old_ticks = true": { @@ -150,6 +155,7 @@ func TestUpdateMarketPrices_Valid(t *testing.T) { constants.MarketId1: constants.ThreeBillion, // no change constants.MarketId2: constants.FiveBillion, // no change constants.MarketId3: constants.FiveBillion, // no change + constants.MarketId4: constants.ThreeBillion, // no change }, }, "Index price crossing = true (price decrease), old_ticks <= 1, new_ticks <= old_ticks = true": { @@ -173,13 +179,14 @@ func TestUpdateMarketPrices_Valid(t *testing.T) { constants.MarketId1: constants.ThreeBillion, // no change constants.MarketId2: constants.FiveBillion, // no change constants.MarketId3: constants.FiveBillion, // no change + constants.MarketId4: constants.ThreeBillion, // no change }, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, k, _, indexPriceCache, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, k, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) msgServer := keeper.NewMsgServerImpl(k) @@ -226,6 +233,7 @@ func TestUpdateMarketPrices_SkipNonDeterministicCheck_Valid(t *testing.T) { constants.MarketId1: constants.ThreeBillion, // no change constants.MarketId2: constants.FiveBillion, // no change constants.MarketId3: constants.FiveBillion, // no change + constants.MarketId4: constants.ThreeBillion, // no change }, }, "Index price trends in the opposite direction of update price from current price, but still updates state": { @@ -249,6 +257,7 @@ func TestUpdateMarketPrices_SkipNonDeterministicCheck_Valid(t *testing.T) { constants.MarketId1: constants.ThreeBillion, // no change constants.MarketId2: constants.FiveBillion, // no change constants.MarketId3: constants.FiveBillion, // no change + constants.MarketId4: constants.ThreeBillion, // no change }, }, "Index price crossing = true, old_ticks > 1, new_ticks <= sqrt(old_ticks) = false": { @@ -272,6 +281,7 @@ func TestUpdateMarketPrices_SkipNonDeterministicCheck_Valid(t *testing.T) { constants.MarketId1: constants.ThreeBillion, // no change constants.MarketId2: constants.FiveBillion, // no change constants.MarketId3: constants.FiveBillion, // no change + constants.MarketId4: constants.ThreeBillion, // no change }, }, "Index price crossing = true, old_ticks <= 1, new_ticks <= old_ticks = false": { @@ -295,13 +305,14 @@ func TestUpdateMarketPrices_SkipNonDeterministicCheck_Valid(t *testing.T) { constants.MarketId1: constants.ThreeBillion, // no change constants.MarketId2: constants.FiveBillion, // no change constants.MarketId3: constants.FiveBillion, // no change + constants.MarketId4: constants.ThreeBillion, // no change }, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, k, _, indexPriceCache, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, k, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) msgServer := keeper.NewMsgServerImpl(k) @@ -365,7 +376,7 @@ func TestUpdateMarketPrices_Error(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, k, _, _, _, mockTimeKeeper := keepertest.PricesKeepers(t) + ctx, k, _, _, mockTimeKeeper := keepertest.PricesKeepers(t) mockTimeKeeper.On("Now").Return(constants.TimeT) msgServer := keeper.NewMsgServerImpl(k) keepertest.CreateTestMarkets(t, ctx, k) @@ -388,7 +399,7 @@ func TestUpdateMarketPrices_Error(t *testing.T) { func TestUpdateMarketPrices_Panic(t *testing.T) { // Init. - ctx, _, _, _, _, _ := keepertest.PricesKeepers(t) + ctx, _, _, _, _ := keepertest.PricesKeepers(t) mockKeeper := &mocks.PricesKeeper{} msgServer := keeper.NewMsgServerImpl(mockKeeper) diff --git a/protocol/x/prices/keeper/slinky_adapter.go b/protocol/x/prices/keeper/slinky_adapter.go new file mode 100644 index 0000000000..bdc735cebb --- /dev/null +++ b/protocol/x/prices/keeper/slinky_adapter.go @@ -0,0 +1,63 @@ +package keeper + +import ( + "fmt" + "strings" + + "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + slinkytypes "github.com/skip-mev/slinky/pkg/types" + oracletypes "github.com/skip-mev/slinky/x/oracle/types" + + "github.com/dydxprotocol/v4-chain/protocol/lib/slinky" +) + +/* + * This package implements the OracleKeeper interface from Slinky's currencypair package + * + * It is required in order to convert between x/prices types and the data which slinky stores on chain + * via Vote Extensions. Using this compatibility layer, we can now use the x/prices keeper as the backing + * store for converting to and from Slinky's on chain price data. + */ + +func (k Keeper) GetCurrencyPairFromID(ctx sdk.Context, id uint64) (cp slinkytypes.CurrencyPair, found bool) { + mp, found := k.GetMarketParam(ctx, uint32(id)) + if !found { + return cp, false + } + cp, err := slinky.MarketPairToCurrencyPair(mp.Pair) + if err != nil { + k.Logger(ctx).Error("CurrencyPairFromString", "error", err) + return cp, false + } + return cp, true +} + +func (k Keeper) GetIDForCurrencyPair(ctx sdk.Context, cp slinkytypes.CurrencyPair) (uint64, bool) { + mps := k.GetAllMarketParams(ctx) + for _, mp := range mps { + mpCp, err := slinky.MarketPairToCurrencyPair(mp.Pair) + if err != nil { + k.Logger(ctx).Error("market param pair invalid format", "pair", mp.Pair) + continue + } + if strings.EqualFold(mpCp.String(), cp.String()) { + return uint64(mp.Id), true + } + } + return 0, false +} + +func (k Keeper) GetPriceForCurrencyPair(ctx sdk.Context, cp slinkytypes.CurrencyPair) (oracletypes.QuotePrice, error) { + id, found := k.GetIDForCurrencyPair(ctx, cp) + if !found { + return oracletypes.QuotePrice{}, fmt.Errorf("id for currency pair %s not found", cp.String()) + } + mp, err := k.GetMarketPrice(ctx, uint32(id)) + if err != nil { + return oracletypes.QuotePrice{}, fmt.Errorf("market price not found for currency pair %s", cp.String()) + } + return oracletypes.QuotePrice{ + Price: math.NewIntFromUint64(mp.Price), + }, nil +} diff --git a/protocol/x/prices/keeper/slinky_adapter_test.go b/protocol/x/prices/keeper/slinky_adapter_test.go new file mode 100644 index 0000000000..8eb41077d2 --- /dev/null +++ b/protocol/x/prices/keeper/slinky_adapter_test.go @@ -0,0 +1,104 @@ +package keeper_test + +import ( + "fmt" + "testing" + + oracletypes "github.com/skip-mev/slinky/pkg/types" + "github.com/stretchr/testify/require" + + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" + keepertest "github.com/dydxprotocol/v4-chain/protocol/testutil/keeper" + "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" +) + +func TestGetCurrencyPairFromID(t *testing.T) { + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + mockTimeProvider.On("Now").Return(constants.TimeT) + + marketNumber := 10 + items := keepertest.CreateNMarkets(t, ctx, keeper, marketNumber) + marketParams := keeper.GetAllMarketParams(ctx) + require.Equal(t, len(marketParams), marketNumber) + for _, mpp := range items { + mpId := mpp.Param.Id + _, found := keeper.GetCurrencyPairFromID(ctx, uint64(mpId)) + require.True(t, found) + } + _, found := keeper.GetCurrencyPairFromID(ctx, uint64(marketNumber+1)) + require.True(t, !found) +} + +func TestIDForCurrencyPair(t *testing.T) { + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + mockTimeProvider.On("Now").Return(constants.TimeT) + + marketNumber := 10 + _ = keepertest.CreateNMarkets(t, ctx, keeper, marketNumber) + marketParams := keeper.GetAllMarketParams(ctx) + require.Equal(t, len(marketParams), marketNumber) + for i := 0; i < marketNumber; i++ { + pair := oracletypes.CurrencyPair{ + Base: fmt.Sprint(i), + Quote: fmt.Sprint(i), + } + id, found := keeper.GetIDForCurrencyPair(ctx, pair) + require.True(t, found) + require.Equal(t, uint64(i), id) + } + _, found := keeper.GetIDForCurrencyPair(ctx, oracletypes.CurrencyPair{ + Base: fmt.Sprint(marketNumber + 1), + Quote: fmt.Sprint(marketNumber + 1), + }) + require.True(t, !found) +} + +func TestGetPriceForCurrencyPair(t *testing.T) { + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + mockTimeProvider.On("Now").Return(constants.TimeT) + + marketNumber := 10 + items := keepertest.CreateNMarkets(t, ctx, keeper, marketNumber) + marketParams := keeper.GetAllMarketParams(ctx) + require.Equal(t, len(marketParams), marketNumber) + for i := 0; i < marketNumber; i++ { + pair := oracletypes.CurrencyPair{ + Base: fmt.Sprint(i), + Quote: fmt.Sprint(i), + } + price, err := keeper.GetPriceForCurrencyPair(ctx, pair) + require.NoError(t, err) + require.Equal(t, items[i].Price.Price, price.Price.Uint64()) + } + _, err := keeper.GetPriceForCurrencyPair(ctx, oracletypes.CurrencyPair{ + Base: fmt.Sprint(marketNumber + 1), + Quote: fmt.Sprint(marketNumber + 1), + }) + require.Error(t, err) +} + +func TestBadMarketData(t *testing.T) { + ctx, keeper, _, _, mockTimeProvider := keepertest.PricesKeepers(t) + mockTimeProvider.On("Now").Return(constants.TimeT) + + _, err := keeper.CreateMarket( + ctx, + types.MarketParam{ + Id: uint32(0), + Pair: "00", + MinExchanges: 1, + MinPriceChangePpm: 1, + ExchangeConfigJson: "{}", + }, + types.MarketPrice{}) + require.NoError(t, err) + + _, found := keeper.GetCurrencyPairFromID(ctx, uint64(0)) + require.False(t, found) + + _, found = keeper.GetIDForCurrencyPair(ctx, oracletypes.CurrencyPair{}) + require.False(t, found) + + _, err = keeper.GetPriceForCurrencyPair(ctx, oracletypes.CurrencyPair{}) + require.Error(t, err) +} diff --git a/protocol/x/prices/keeper/smoothed_price.go b/protocol/x/prices/keeper/smoothed_price.go deleted file mode 100644 index cce78e84fd..0000000000 --- a/protocol/x/prices/keeper/smoothed_price.go +++ /dev/null @@ -1,54 +0,0 @@ -package keeper - -import ( - "errors" - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" -) - -// UpdateSmoothedPrices updates the internal map of smoothed prices for all markets. -// The smoothing is calculated with Basic Exponential Smoothing, see -// https://en.wikipedia.org/wiki/Exponential_smoothing -// If there is no valid index price for a market at this time, the smoothed price does not change. -func (k Keeper) UpdateSmoothedPrices( - ctx sdk.Context, - linearInterpolateFunc func(v0 uint64, v1 uint64, ppm uint32) (uint64, error), -) error { - allMarketParams := k.GetAllMarketParams(ctx) - indexPrices := k.indexPriceCache.GetValidMedianPrices(allMarketParams, k.timeProvider.Now()) - - // Track errors for each market. - updateErrors := make([]error, 0) - - // Iterate through allMarketParams instead of indexPrices to ensure that we generate deterministic error messages - // in the case of failed updates. - for _, marketParam := range allMarketParams { - indexPrice, exists := indexPrices[marketParam.Id] - if !exists { - continue - } - - smoothedPrice, ok := k.marketToSmoothedPrices.GetSmoothedPrice(marketParam.Id) - if !ok { - smoothedPrice = indexPrice - } - update, err := linearInterpolateFunc( - smoothedPrice, - indexPrice, - types.PriceSmoothingPpm, - ) - if err != nil { - updateErrors = append( - updateErrors, - fmt.Errorf("Error updating smoothed price for market %v: %w", marketParam.Id, err), - ) - continue - } - - k.marketToSmoothedPrices.PushSmoothedPrice(marketParam.Id, update) - } - - return errors.Join(updateErrors...) -} diff --git a/protocol/x/prices/keeper/smoothed_price_test.go b/protocol/x/prices/keeper/smoothed_price_test.go deleted file mode 100644 index a8ce09400e..0000000000 --- a/protocol/x/prices/keeper/smoothed_price_test.go +++ /dev/null @@ -1,125 +0,0 @@ -package keeper_test - -import ( - "fmt" - "testing" - - "github.com/dydxprotocol/v4-chain/protocol/daemons/pricefeed/api" - "github.com/dydxprotocol/v4-chain/protocol/lib" - "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" - keepertest "github.com/dydxprotocol/v4-chain/protocol/testutil/keeper" - "github.com/stretchr/testify/require" -) - -var ( - emptySmoothedPrices = map[uint32]uint64{} -) - -func errInterpolator(v0 uint64, v1 uint64, ppm uint32) (uint64, error) { - return 0, fmt.Errorf("error while interpolating") -} - -func alternatingErrInterpolator() func(v0 uint64, v1 uint64, ppm uint32) (uint64, error) { - var i int - return func(v0 uint64, v1 uint64, ppm uint32) (uint64, error) { - i++ - if i%2 == 0 { - return 0, fmt.Errorf("error while interpolating") - } - return lib.Uint64LinearInterpolate(v0, v1, ppm) - } -} - -func TestUpdateSmoothedPrices(t *testing.T) { - tests := map[string]struct { - smoothedPrices map[uint32]uint64 - indexPrices []*api.MarketPriceUpdate - expectedResult map[uint32]uint64 - linearInterpolateFunc func(v0 uint64, v1 uint64, ppm uint32) (uint64, error) - expectedErr string - }{ - "Empty result - No index prices, no smoothed prices": { - expectedResult: emptySmoothedPrices, - linearInterpolateFunc: lib.Uint64LinearInterpolate, - }, - "Unchanged - No index prices": { - smoothedPrices: constants.AtTimeTSingleExchangeSmoothedPrices, - expectedResult: constants.AtTimeTSingleExchangeSmoothedPrices, - linearInterpolateFunc: lib.Uint64LinearInterpolate, - }, - "Mixed updates and additions - mix of present and missing index prices, smoothed prices": { - indexPrices: constants.AtTimeTSingleExchangePriceUpdate, - smoothedPrices: map[uint32]uint64{ - constants.MarketId1: constants.Exchange1_Price1_TimeT.Price + 10, - constants.MarketId2: constants.Exchange2_Price2_TimeT.Price + 50, - constants.MarketId7: constants.Price1, - }, - expectedResult: map[uint32]uint64{ - constants.MarketId0: constants.Exchange0_Price4_TimeT.Price, - constants.MarketId1: constants.Exchange1_Price1_TimeT.Price + 7, - constants.MarketId2: constants.Exchange2_Price2_TimeT.Price + 35, - constants.MarketId3: constants.Exchange3_Price3_TimeT.Price, - constants.MarketId7: constants.Price1, - }, - linearInterpolateFunc: lib.Uint64LinearInterpolate, - }, - "Initializes smoothed prices with index prices": { - indexPrices: constants.AtTimeTSingleExchangePriceUpdate, - expectedResult: constants.AtTimeTSingleExchangeSmoothedPrices, - linearInterpolateFunc: lib.Uint64LinearInterpolate, - }, - "All updated - multiple existing overlapped index and smoothed prices": { - indexPrices: constants.AtTimeTSingleExchangePriceUpdate, - smoothedPrices: constants.AtTimeTSingleExchangeSmoothedPricesPlus10, - expectedResult: constants.AtTimeTSingleExchangeSmoothedPricesPlus7, - linearInterpolateFunc: lib.Uint64LinearInterpolate, - }, - "Interpolation errors - returns error": { - indexPrices: constants.AtTimeTSingleExchangePriceUpdate, - smoothedPrices: constants.AtTimeTSingleExchangeSmoothedPricesPlus10, - linearInterpolateFunc: errInterpolator, - expectedErr: "Error updating smoothed price for market 0: error while interpolating\n" + - "Error updating smoothed price for market 1: error while interpolating\n" + - "Error updating smoothed price for market 2: error while interpolating\n" + - "Error updating smoothed price for market 3: error while interpolating", - expectedResult: constants.AtTimeTSingleExchangeSmoothedPricesPlus10, // no change - }, - "Single interpolation error - returns error, continues updating other markets": { - indexPrices: constants.AtTimeTSingleExchangePriceUpdate, - smoothedPrices: constants.AtTimeTSingleExchangeSmoothedPricesPlus10, - linearInterpolateFunc: alternatingErrInterpolator(), - expectedErr: "Error updating smoothed price for market 1: error while interpolating\n" + - "Error updating smoothed price for market 3: error while interpolating", - expectedResult: map[uint32]uint64{ - constants.MarketId0: constants.AtTimeTSingleExchangeSmoothedPricesPlus7[constants.MarketId0], // update - constants.MarketId1: constants.AtTimeTSingleExchangeSmoothedPricesPlus10[constants.MarketId1], // no change - constants.MarketId2: constants.AtTimeTSingleExchangeSmoothedPricesPlus7[constants.MarketId2], // update - constants.MarketId3: constants.AtTimeTSingleExchangeSmoothedPricesPlus10[constants.MarketId3], // update - }, // no change - }, - } - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - // Setup. - ctx, k, _, indexPriceCache, marketToSmoothedPrices, mockTimeProvider := keepertest.PricesKeepers(t) - mockTimeProvider.On("Now").Return(constants.TimeT) - - keepertest.CreateTestMarkets(t, ctx, k) - indexPriceCache.UpdatePrices(tc.indexPrices) - for market, smoothedPrice := range tc.smoothedPrices { - marketToSmoothedPrices.PushSmoothedPrice(market, smoothedPrice) - } - - // Run. - err := k.UpdateSmoothedPrices(ctx, tc.linearInterpolateFunc) - if tc.expectedErr != "" { - require.EqualError(t, err, tc.expectedErr) - } else { - require.NoError(t, err) - } - - // Validate. - require.Equal(t, tc.expectedResult, marketToSmoothedPrices.GetSmoothedPricesForTest()) - }) - } -} diff --git a/protocol/x/prices/keeper/update_price.go b/protocol/x/prices/keeper/update_price.go index 6f4a5162a2..c7621e5c6b 100644 --- a/protocol/x/prices/keeper/update_price.go +++ b/protocol/x/prices/keeper/update_price.go @@ -96,33 +96,9 @@ func (k Keeper) GetValidMarketPriceUpdates( continue } - historicalSmoothedPrices := k.marketToSmoothedPrices.GetHistoricalSmoothedPrices(marketId) - // We generally expect to have a smoothed price history for each market, except during the first few blocks - // after network genesis or a network restart. In this scenario, we use the index price as the smoothed price. - if len(historicalSmoothedPrices) == 0 { - // Conditionally log missing smoothed prices at least 20s after genesis/restart/market creation. We expect - // that there will be a delay in populating historical smoothed prices after network genesis or a network - // restart, or when a market is created, because they depend on present index prices, and it takes the - // daemon some time to warm up. - if !k.IsRecentlyAvailable(ctx, marketId) { - log.ErrorLog( - ctx, - "Smoothed price for market does not exist", - constants.MarketIdLogKey, - marketId, - ) - } - historicalSmoothedPrices = []uint64{indexPrice} - } - smoothedPrice := historicalSmoothedPrices[0] - - proposalPrice := getProposalPrice(smoothedPrice, indexPrice, marketParamPrice.Price.Price) - shouldPropose, reasons := shouldProposePrice( - proposalPrice, - marketParamPrice, indexPrice, - historicalSmoothedPrices, + marketParamPrice, ) // If the index price would have updated, track how the proposal price changes the update @@ -131,7 +107,6 @@ func (k Keeper) GetValidMarketPriceUpdates( logPriceUpdateBehavior( ctx, marketParamPrice, - proposalPrice, indexPrice, marketMetricsLabel, shouldPropose, @@ -145,7 +120,7 @@ func (k Keeper) GetValidMarketPriceUpdates( updates, &types.MsgUpdateMarketPrices_MarketPrice{ MarketId: marketId, - Price: proposalPrice, + Price: indexPrice, }, ) } @@ -163,7 +138,6 @@ func logPriceUpdateBehavior( ctx sdk.Context, marketParamPrice types.MarketParamPrice, proposalPrice uint64, - indexPrice uint64, marketMetricsLabel gometrics.Label, shouldPropose bool, reasons []proposeCancellationReason, @@ -189,11 +163,10 @@ func logPriceUpdateBehavior( log.InfoLog( ctx, fmt.Sprintf( - "Proposal price (%v) %v for market (%v), index price (%v), oracle price (%v), min price change (%v)", + "Proposal price (%v) %v for market (%v), oracle price (%v), min price change (%v)", proposalPrice, loggingVerb, marketParamPrice.Param.Id, - indexPrice, marketParamPrice.Price.Price, getMinPriceChangeAmountForMarket(marketParamPrice), ), @@ -212,8 +185,6 @@ type proposeCancellationReason struct { func shouldProposePrice( proposalPrice uint64, marketParamPrice types.MarketParamPrice, - indexPrice uint64, - historicalSmoothedPrices []uint64, ) ( shouldPropose bool, reasons []proposeCancellationReason, @@ -221,58 +192,6 @@ func shouldProposePrice( reasons = make([]proposeCancellationReason, 0, 4) shouldPropose = true - // If any smoothed price crosses the old price compared to the index price, do not update. - reasons = append( - reasons, - proposeCancellationReason{ - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: false, - }, - ) - for _, smoothedPrice := range historicalSmoothedPrices { - if isCrossingOldPrice(PriceTuple{ - OldPrice: marketParamPrice.Price.Price, - IndexPrice: indexPrice, - NewPrice: smoothedPrice, - }) { - shouldPropose = false - reasons[len(reasons)-1].Value = true - break - } - } - - // If the proposal price crosses the old price compared to the index price, do not update. - reasons = append( - reasons, - proposeCancellationReason{ - Reason: metrics.ProposedPriceCrossesOraclePrice, - }, - ) - if isCrossingOldPrice(PriceTuple{ - OldPrice: marketParamPrice.Price.Price, - IndexPrice: indexPrice, - NewPrice: proposalPrice, - }) { - shouldPropose = false - reasons[len(reasons)-1].Value = true - } - - // If any smoothed price does not meet the min price change, do not update. - reasons = append( - reasons, - proposeCancellationReason{ - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: false, - }, - ) - for _, smoothedPrice := range historicalSmoothedPrices { - if !isAboveRequiredMinPriceChange(marketParamPrice, smoothedPrice) { - shouldPropose = false - reasons[len(reasons)-1].Value = true - break - } - } - // If the proposal price does not meet the min price change, do not update. reasons = append( reasons, diff --git a/protocol/x/prices/keeper/update_price_test.go b/protocol/x/prices/keeper/update_price_test.go index c339289509..db2b017ce4 100644 --- a/protocol/x/prices/keeper/update_price_test.go +++ b/protocol/x/prices/keeper/update_price_test.go @@ -62,62 +62,13 @@ var ( } invalidMarket9DoesNotExistUpdate = constants.Market9_SingleExchange_AtTimeUpdate[0] - - // SmoothedPrice test constants. - validMarket0SmoothedPrices = map[uint32][]uint64{ - constants.MarketId0: {fiveBillionAndFiveMillion + 1}, - } - - invalidMarket0HistoricalSmoothedPricesCrossesOraclePrice = map[uint32][]uint64{ - constants.MarketId0: { - fiveBillionAndFiveMillion + 1, // Valid - fiveBillionMinusFiveMillionAndOne, // Invalid: crosses oracle price. - }, - } - - invalidMarket0HistoricalSmoothedPricesDoesNotMeetMinPriceChange = map[uint32][]uint64{ - constants.MarketId0: { - fiveBillionAndFiveMillion + 1, // Valid - constants.FiveBillion + 1, // Invalid: does not meet min price change. - }, - } - - invalidMarket2HistoricalSmoothedPricesCrossesOraclePrice = map[uint32][]uint64{ - constants.MarketId2: { - fiveBillionAndFiveMillion + 1, // Valid - fiveBillionMinusFiveMillionAndOne, // Invalid: crosses oracle price. - }, - } - - invalidMarket0SmoothedPriceTrendsAwayFromIndexPrice = map[uint32][]uint64{ - constants.MarketId0: {fiveBillionMinusFiveMillionAndOne}, - } - - invalidMarket2SmoothedPriceTrendsAwayFromIndexPrice = map[uint32][]uint64{ - constants.MarketId2: {constants.FiveBillion - 2}, - } - - market2SmoothedPriceNotProposed = map[uint32][]uint64{ - constants.MarketId2: {constants.FiveBillion + 3}, - } - - market2SmoothedPriceDoesNotMeetMinChangeUpdate = map[uint32][]uint64{ - constants.MarketId2: {constants.FiveBillion + 1}, - } - - invalidMarket9DoesNotExistSmoothedPrice = map[uint32][]uint64{ - constants.MarketId9: {1_000_000}, - } ) // Note: markets and exchanges are created by `CreateTestMarketsAndExchanges`. func TestGetValidMarketPriceUpdates(t *testing.T) { tests := map[string]struct { // Setup. - indexPrices []*api.MarketPriceUpdate - // historicalSmoothedIndexPrice prices for each market are expected to be ordered from most recent to least - // recent. - historicalSmoothedIndexPrices map[uint32][]uint64 + indexPrices []*api.MarketPriceUpdate skipCreateMarketsAndExchanges bool // Expected. @@ -139,70 +90,13 @@ func TestGetValidMarketPriceUpdates(t *testing.T) { indexPrices: []*api.MarketPriceUpdate{invalidMarket9DoesNotExistUpdate}, expectedMsg: emptyResult, }, - "Single result: index price used when no smoothed prices": { - indexPrices: []*api.MarketPriceUpdate{validMarket0Update}, - expectedMsg: validMarket0UpdateResult, - }, - "Single result: no overlap between markets for index prices and smoothed prices": { - indexPrices: []*api.MarketPriceUpdate{validMarket0Update}, - historicalSmoothedIndexPrices: invalidMarket9DoesNotExistSmoothedPrice, - expectedMsg: validMarket0UpdateResult, - }, - "Empty result: propose price is index price, does not meet min price change": { - indexPrices: []*api.MarketPriceUpdate{invalidMarket2PriceDoesNotMeetMinChangeUpdate}, - historicalSmoothedIndexPrices: market2SmoothedPriceNotProposed, - expectedMsg: emptyResult, - }, - "Empty result: propose price is smoothed price, does not meet min price change": { - indexPrices: []*api.MarketPriceUpdate{invalidMarket2PriceDoesNotMeetMinChangeUpdate}, - historicalSmoothedIndexPrices: market2SmoothedPriceDoesNotMeetMinChangeUpdate, - expectedMsg: emptyResult, - }, - "Empty result: propose price is good, but historical smoothed price does not meet min price change": { - indexPrices: []*api.MarketPriceUpdate{validMarket0Update}, - historicalSmoothedIndexPrices: invalidMarket0HistoricalSmoothedPricesDoesNotMeetMinPriceChange, - expectedMsg: emptyResult, - }, - "Empty result: propose price is good, but historical smoothed price crosses oracle price": { - indexPrices: []*api.MarketPriceUpdate{validMarket0Update}, - historicalSmoothedIndexPrices: invalidMarket0HistoricalSmoothedPricesCrossesOraclePrice, - expectedMsg: emptyResult, - }, - "Empty result: proposed price is smoothed price, meets min change but trends away from index price": { - indexPrices: []*api.MarketPriceUpdate{validMarket0Update}, - historicalSmoothedIndexPrices: invalidMarket0SmoothedPriceTrendsAwayFromIndexPrice, - expectedMsg: emptyResult, - }, - "Empty result: proposed price does not meet min change and historical smoothed price crosses oracle price": { - indexPrices: []*api.MarketPriceUpdate{invalidMarket2PriceDoesNotMeetMinChangeUpdate}, - historicalSmoothedIndexPrices: invalidMarket2HistoricalSmoothedPricesCrossesOraclePrice, - expectedMsg: emptyResult, - }, - "Empty result: proposed price does not meet min change and smoothed price is trending away from index price": { - indexPrices: []*api.MarketPriceUpdate{invalidMarket2PriceDoesNotMeetMinChangeUpdate}, - historicalSmoothedIndexPrices: invalidMarket2SmoothedPriceTrendsAwayFromIndexPrice, - expectedMsg: emptyResult, + "Empty result: propose price does not meet min price change": { + indexPrices: []*api.MarketPriceUpdate{invalidMarket2PriceDoesNotMeetMinChangeUpdate}, + expectedMsg: emptyResult, }, "Single market price update": { - indexPrices: []*api.MarketPriceUpdate{validMarket0Update}, - historicalSmoothedIndexPrices: validMarket0SmoothedPrices, - expectedMsg: validMarket0UpdateResult, - }, - "Multiple market price updates, some from smoothed price and some from index price": { - indexPrices: constants.AtTimeTSingleExchangePriceUpdate, - historicalSmoothedIndexPrices: map[uint32][]uint64{ - constants.MarketId0: {constants.Price4 - 1}, - constants.MarketId1: {constants.Price1 + 1}, - constants.MarketId2: {constants.Price2}, - }, - expectedMsg: &types.MsgUpdateMarketPrices{ - MarketPriceUpdates: []*types.MsgUpdateMarketPrices_MarketPrice{ - types.NewMarketPriceUpdate(constants.MarketId0, constants.Price4), - types.NewMarketPriceUpdate(constants.MarketId1, constants.Price1+1), - types.NewMarketPriceUpdate(constants.MarketId2, constants.Price2), - types.NewMarketPriceUpdate(constants.MarketId3, constants.Price3), - }, - }, + indexPrices: []*api.MarketPriceUpdate{validMarket0Update}, + expectedMsg: validMarket0UpdateResult, }, "Mix of valid and invalid index prices": { indexPrices: []*api.MarketPriceUpdate{ @@ -211,45 +105,17 @@ func TestGetValidMarketPriceUpdates(t *testing.T) { invalidMarket2PriceDoesNotMeetMinChangeUpdate, // Price does not meet min price change req. invalidMarket9DoesNotExistUpdate, // Market with id 9 does not exist. }, - historicalSmoothedIndexPrices: map[uint32][]uint64{ - constants.MarketId0: {validMarket0Update.ExchangePrices[0].Price}, - constants.MarketId1: {constants.Price4}, - constants.MarketId2: {constants.Price2}, - constants.MarketId9: {constants.Price4}, - }, expectedMsg: validMarket0UpdateResult, }, "Mix of valid, invalid, and missing smoothed prices": { indexPrices: constants.AtTimeTSingleExchangePriceUpdate, - historicalSmoothedIndexPrices: map[uint32][]uint64{ - constants.MarketId0: {constants.Price4}, // Same as index price. - constants.MarketId1: {0}, // Invalid price, so index price is used. - constants.MarketId9: {constants.Price1}, // Invalid market. - }, expectedMsg: &types.MsgUpdateMarketPrices{ MarketPriceUpdates: []*types.MsgUpdateMarketPrices_MarketPrice{ types.NewMarketPriceUpdate(constants.MarketId0, constants.Price4), types.NewMarketPriceUpdate(constants.MarketId1, constants.Price1), types.NewMarketPriceUpdate(constants.MarketId2, constants.Price2), types.NewMarketPriceUpdate(constants.MarketId3, constants.Price3), - }, - }, - }, - "Mix of valid, invalid, and invalid historical smoothed prices": { - indexPrices: constants.AtTimeTSingleExchangePriceUpdate, - historicalSmoothedIndexPrices: map[uint32][]uint64{ - constants.MarketId0: { - constants.Price4, // Same as index price. - fiveBillionAndFiveMillion, // Invalid: crosses oracle price. - }, - constants.MarketId1: {constants.Price1}, // Valid: same as index price. - constants.MarketId9: {constants.Price1}, // Invalid market. - }, - expectedMsg: &types.MsgUpdateMarketPrices{ - MarketPriceUpdates: []*types.MsgUpdateMarketPrices_MarketPrice{ - types.NewMarketPriceUpdate(constants.MarketId1, constants.Price1), - types.NewMarketPriceUpdate(constants.MarketId2, constants.Price2), - types.NewMarketPriceUpdate(constants.MarketId3, constants.Price3), + types.NewMarketPriceUpdate(constants.MarketId4, constants.Price3), }, }, }, @@ -257,7 +123,7 @@ func TestGetValidMarketPriceUpdates(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, k, _, indexPriceCache, marketSmoothedPrices, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, k, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) if !tc.skipCreateMarketsAndExchanges { @@ -265,14 +131,6 @@ func TestGetValidMarketPriceUpdates(t *testing.T) { } indexPriceCache.UpdatePrices(tc.indexPrices) - // Smoothed prices are listed in reverse chronological order for test case constant legibility. - // Therefore, add them in reverse order to the `marketSmoothedPrices` cache. - for market, historicalSmoothedPrices := range tc.historicalSmoothedIndexPrices { - for i := len(historicalSmoothedPrices) - 1; i >= 0; i-- { - marketSmoothedPrices.PushSmoothedPrice(market, historicalSmoothedPrices[i]) - } - } - // Run. result := k.GetValidMarketPriceUpdates(ctx) diff --git a/protocol/x/prices/keeper/update_price_whitebox_test.go b/protocol/x/prices/keeper/update_price_whitebox_test.go index 85f816ac79..952805044e 100644 --- a/protocol/x/prices/keeper/update_price_whitebox_test.go +++ b/protocol/x/prices/keeper/update_price_whitebox_test.go @@ -9,15 +9,10 @@ import ( ) const ( - fiveBillionAndFiveMillion = constants.FiveBillion + constants.FiveMillion - fiveBillionMinusFiveMillionAndOne = constants.FiveBillion - constants.FiveMillion - 1 - fiveBillionAndTenMillion = constants.FiveBillion + 2*constants.FiveMillion + fiveBillionAndFiveMillion = constants.FiveBillion + constants.FiveMillion - testPriceValidUpdate = fiveBillionAndFiveMillion - testPriceLargeValidUpdate = fiveBillionAndTenMillion - testPriceDoesNotMeetMinPriceChange = constants.FiveBillion + 2 - testPriceCrossesOraclePrice = fiveBillionMinusFiveMillionAndOne - testPriceCrossesAndDoesNotMeetMinChange = constants.FiveBillion - 1 + testPriceValidUpdate = fiveBillionAndFiveMillion + testPriceDoesNotMeetMinPriceChange = constants.FiveBillion + 2 ) var ( @@ -29,308 +24,14 @@ var ( func TestShouldProposePrice(t *testing.T) { tests := map[string]struct { - proposalPrice uint64 - indexPrice uint64 - historicalSmoothedPrices []uint64 - expectShouldPropose bool - expectReasons []proposeCancellationReason + proposalPrice uint64 + expectShouldPropose bool + expectReasons []proposeCancellationReason }{ - "Should not propose: proposal price is smoothed price, crosses index price": { - proposalPrice: testPriceCrossesOraclePrice, - indexPrice: testPriceLargeValidUpdate, - historicalSmoothedPrices: []uint64{ - testPriceCrossesOraclePrice, - testPriceValidUpdate, - }, - expectShouldPropose: false, - expectReasons: []proposeCancellationReason{ - // These are both true because the proposed price is the most recent smoothed price. - { - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: true, - }, - { - Reason: metrics.ProposedPriceCrossesOraclePrice, - Value: true, - }, - { - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: false, - }, - { - Reason: metrics.ProposedPriceDoesNotMeetMinPriceChange, - Value: false, - }, - }, - }, - "Should not propose: proposal price is smoothed price, does not meet min price change": { - proposalPrice: testPriceDoesNotMeetMinPriceChange, - indexPrice: testPriceLargeValidUpdate, - historicalSmoothedPrices: []uint64{ - testPriceDoesNotMeetMinPriceChange, - testPriceValidUpdate, - }, - expectShouldPropose: false, - expectReasons: []proposeCancellationReason{ - { - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.ProposedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: true, - }, - { - Reason: metrics.ProposedPriceDoesNotMeetMinPriceChange, - Value: true, - }, - }, - }, - "Should not propose: proposal price is index price, does not meet min price change": { - proposalPrice: testPriceDoesNotMeetMinPriceChange, - indexPrice: testPriceDoesNotMeetMinPriceChange, - historicalSmoothedPrices: []uint64{ - testPriceLargeValidUpdate, - testPriceValidUpdate, - }, - expectShouldPropose: false, - expectReasons: []proposeCancellationReason{ - { - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.ProposedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: false, - }, - { - Reason: metrics.ProposedPriceDoesNotMeetMinPriceChange, - Value: true, - }, - }, - }, - "Should not propose: a historical smoothed price crosses index price": { - proposalPrice: testPriceValidUpdate, - indexPrice: testPriceValidUpdate, - historicalSmoothedPrices: []uint64{ - testPriceValidUpdate, - testPriceDoesNotMeetMinPriceChange, - }, - expectShouldPropose: false, - expectReasons: []proposeCancellationReason{ - { - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.ProposedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: true, - }, - { - Reason: metrics.ProposedPriceDoesNotMeetMinPriceChange, - Value: false, - }, - }, - }, - "Should not propose: multiple historical smoothed prices cross index price": { - proposalPrice: testPriceValidUpdate, - indexPrice: testPriceValidUpdate, - historicalSmoothedPrices: []uint64{ - testPriceValidUpdate, - testPriceCrossesOraclePrice, - testPriceCrossesOraclePrice, - }, - expectShouldPropose: false, - expectReasons: []proposeCancellationReason{ - { - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: true, - }, - { - Reason: metrics.ProposedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: false, - }, - { - Reason: metrics.ProposedPriceDoesNotMeetMinPriceChange, - Value: false, - }, - }, - }, - "Should not propose: a historical smoothed price does not meet min price change": { - proposalPrice: testPriceValidUpdate, - indexPrice: testPriceValidUpdate, - historicalSmoothedPrices: []uint64{ - testPriceValidUpdate, - testPriceDoesNotMeetMinPriceChange, - }, - expectShouldPropose: false, - expectReasons: []proposeCancellationReason{ - { - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.ProposedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: true, - }, - { - Reason: metrics.ProposedPriceDoesNotMeetMinPriceChange, - Value: false, - }, - }, - }, - "Should not propose: multiple historical smoothed prices do not meet min price change": { - proposalPrice: testPriceValidUpdate, - indexPrice: testPriceValidUpdate, - historicalSmoothedPrices: []uint64{ - testPriceValidUpdate, - testPriceDoesNotMeetMinPriceChange, - testPriceDoesNotMeetMinPriceChange, - }, - expectShouldPropose: false, - expectReasons: []proposeCancellationReason{ - { - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.ProposedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: true, - }, - { - Reason: metrics.ProposedPriceDoesNotMeetMinPriceChange, - Value: false, - }, - }, - }, - "Should not propose: historical smoothed price crosses and does not meet min price change": { - proposalPrice: testPriceValidUpdate, - indexPrice: testPriceValidUpdate, - historicalSmoothedPrices: []uint64{ - testPriceValidUpdate, - testPriceCrossesAndDoesNotMeetMinChange, - }, - expectShouldPropose: false, - expectReasons: []proposeCancellationReason{ - { - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: true, - }, - { - Reason: metrics.ProposedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: true, - }, - { - Reason: metrics.ProposedPriceDoesNotMeetMinPriceChange, - Value: false, - }, - }, - }, - "Should not propose: proposal price crosses and does not meet min price change": { - proposalPrice: testPriceCrossesAndDoesNotMeetMinChange, - indexPrice: testPriceValidUpdate, - historicalSmoothedPrices: []uint64{ - testPriceValidUpdate, - testPriceLargeValidUpdate, - }, - expectShouldPropose: false, - expectReasons: []proposeCancellationReason{ - { - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.ProposedPriceCrossesOraclePrice, - Value: true, - }, - { - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: false, - }, - { - Reason: metrics.ProposedPriceDoesNotMeetMinPriceChange, - Value: true, - }, - }, - }, - "Should not propose: multiple historical smoothed prices issues": { - proposalPrice: testPriceValidUpdate, - indexPrice: testPriceValidUpdate, - historicalSmoothedPrices: []uint64{ - testPriceValidUpdate, - testPriceDoesNotMeetMinPriceChange, - testPriceCrossesOraclePrice, - }, - expectShouldPropose: false, - expectReasons: []proposeCancellationReason{ - { - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: true, - }, - { - Reason: metrics.ProposedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: true, - }, - { - Reason: metrics.ProposedPriceDoesNotMeetMinPriceChange, - Value: false, - }, - }, - }, - "Should not propose: multiple issues": { - proposalPrice: testPriceDoesNotMeetMinPriceChange, - indexPrice: testPriceValidUpdate, - historicalSmoothedPrices: []uint64{ - testPriceValidUpdate, - testPriceDoesNotMeetMinPriceChange, - testPriceCrossesOraclePrice, - }, + "Should not propose: proposal price does not meet min price change": { + proposalPrice: testPriceDoesNotMeetMinPriceChange, expectShouldPropose: false, expectReasons: []proposeCancellationReason{ - { - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: true, - }, - { - Reason: metrics.ProposedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: true, - }, { Reason: metrics.ProposedPriceDoesNotMeetMinPriceChange, Value: true, @@ -338,27 +39,9 @@ func TestShouldProposePrice(t *testing.T) { }, }, "Should propose": { - proposalPrice: testPriceValidUpdate, - indexPrice: testPriceLargeValidUpdate, - historicalSmoothedPrices: []uint64{ - testPriceValidUpdate, - testPriceLargeValidUpdate, - testPriceValidUpdate, - }, + proposalPrice: testPriceValidUpdate, expectShouldPropose: true, expectReasons: []proposeCancellationReason{ - { - Reason: metrics.RecentSmoothedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.ProposedPriceCrossesOraclePrice, - Value: false, - }, - { - Reason: metrics.RecentSmoothedPriceDoesNotMeetMinPriceChange, - Value: false, - }, { Reason: metrics.ProposedPriceDoesNotMeetMinPriceChange, Value: false, @@ -371,8 +54,6 @@ func TestShouldProposePrice(t *testing.T) { actualShouldPropose, actualReasons := shouldProposePrice( tc.proposalPrice, testMarketParamPrice, - tc.indexPrice, - tc.historicalSmoothedPrices, ) require.Equal(t, tc.expectShouldPropose, actualShouldPropose) require.Equal(t, tc.expectReasons, actualReasons) diff --git a/protocol/x/prices/keeper/util.go b/protocol/x/prices/keeper/util.go index 459e951462..ba23e77405 100644 --- a/protocol/x/prices/keeper/util.go +++ b/protocol/x/prices/keeper/util.go @@ -9,16 +9,6 @@ import ( "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" ) -// getProposalPrice returns the proposed price update for the next block, which is either the smoothed price or the -// index price - whichever is closer to the current market price. In cases where the smoothed price and the index price -// are equidistant from the current market price, the smoothed price is chosen. -func getProposalPrice(smoothedPrice uint64, indexPrice uint64, marketPrice uint64) uint64 { - if lib.AbsDiffUint64(smoothedPrice, marketPrice) > lib.AbsDiffUint64(indexPrice, marketPrice) { - return indexPrice - } - return smoothedPrice -} - // isAboveRequiredMinPriceChange returns true if the new price meets the required min price change // for the market. Otherwise, returns false. func isAboveRequiredMinPriceChange(marketParamPrice types.MarketParamPrice, newPrice uint64) bool { diff --git a/protocol/x/prices/keeper/util_test.go b/protocol/x/prices/keeper/util_test.go index 6f82cab19d..c9cc826fe4 100644 --- a/protocol/x/prices/keeper/util_test.go +++ b/protocol/x/prices/keeper/util_test.go @@ -15,82 +15,6 @@ const ( maxUint32 = uint32(4_294_967_295) // 2 ^ 32 - 1 ) -func TestGetProposalPrice(t *testing.T) { - tests := map[string]struct { - smoothedPrice uint64 - indexPrice uint64 - marketPrice uint64 - expectedPrice uint64 - }{ - "smoothedPrice: marketPrice < smoothedPrice < indexPrice": { - smoothedPrice: uint64(1_000_000), - indexPrice: uint64(1_000_005), - marketPrice: uint64(900_000), - expectedPrice: uint64(1_000_000), - }, - "smoothedPrice: indexPrice < smoothedPrice < marketPrice": { - smoothedPrice: uint64(800_500), - indexPrice: uint64(800_000), - marketPrice: uint64(900_000), - expectedPrice: uint64(800_500), - }, - "indexPrice: marketPrice < indexPrice < smoothedPrice": { - smoothedPrice: uint64(1_000_000), - indexPrice: uint64(900_000), - marketPrice: uint64(800_000), - expectedPrice: uint64(900_000), - }, - "indexPrice: smoothedPrice < indexPrice < marketPrice": { - smoothedPrice: uint64(800_000), - indexPrice: uint64(900_000), - marketPrice: uint64(1_000_000), - expectedPrice: uint64(900_000), - }, - "indexPrice: smoothedPrice << marketPrice < indexPrice": { - smoothedPrice: uint64(500_000), - indexPrice: uint64(1_100_000), - marketPrice: uint64(1_000_000), - expectedPrice: uint64(1_100_000), - }, - "smoothedPrice: smoothedPrice < marketPrice << indexPrice": { - smoothedPrice: uint64(900_000), - indexPrice: uint64(1_500_000), - marketPrice: uint64(1_000_000), - expectedPrice: uint64(900_000), - }, - "smoothedPrice: smoothedPrice < marketPrice < indexPrice": { - smoothedPrice: uint64(900_000), - indexPrice: uint64(1_100_000), - marketPrice: uint64(1_000_000), - expectedPrice: uint64(900_000), - }, - "indexPrice: indexPrice < marketPrice << smoothedPrice": { - smoothedPrice: uint64(1_500_000), - indexPrice: uint64(900_000), - marketPrice: uint64(1_000_000), - expectedPrice: uint64(900_000), - }, - "smoothedPrice: indexPrice << marketPrice < smoothedPrice": { - smoothedPrice: uint64(1_100_000), - indexPrice: uint64(500_000), - marketPrice: uint64(1_000_000), - expectedPrice: uint64(1_100_000), - }, - "smoothedPrice: indexPrice < marketPrice < smoothedPrice": { - smoothedPrice: uint64(1_100_000), - indexPrice: uint64(900_000), - marketPrice: uint64(1_000_000), - expectedPrice: uint64(1_100_000), - }, - } - for name, tc := range tests { - t.Run(name, func(t *testing.T) { - price := getProposalPrice(tc.smoothedPrice, tc.indexPrice, tc.marketPrice) - require.Equal(t, tc.expectedPrice, price) - }) - } -} - func TestGetMinPriceChangeAmountForMarket(t *testing.T) { tests := map[string]struct { // Setup. diff --git a/protocol/x/prices/keeper/validate_market_price_updates_test.go b/protocol/x/prices/keeper/validate_market_price_updates_test.go index 58e44ef5ff..a89998d90e 100644 --- a/protocol/x/prices/keeper/validate_market_price_updates_test.go +++ b/protocol/x/prices/keeper/validate_market_price_updates_test.go @@ -122,7 +122,7 @@ func TestPerformStatefulPriceUpdateValidation_Valid(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, k, _, indexPriceCache, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, k, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) keepertest.CreateTestMarkets(t, ctx, k) @@ -207,7 +207,7 @@ func TestPerformStatefulPriceUpdateValidation_SkipNonDeterministicCheck_Valid(t for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, k, _, indexPriceCache, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, k, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) keepertest.CreateTestMarkets(t, ctx, k) @@ -340,7 +340,7 @@ func TestPerformStatefulPriceUpdateValidation_Error(t *testing.T) { for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, k, _, indexPriceCache, _, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, k, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) keepertest.CreateTestMarkets(t, ctx, k) @@ -374,7 +374,7 @@ func TestGetMarketsMissingFromPriceUpdates(t *testing.T) { smoothedIndexPrices: constants.AtTimeTSingleExchangeSmoothedPrices, // The returned market ids must be sorted. expectedMarketIds: []uint32{ - constants.MarketId0, constants.MarketId1, constants.MarketId2, constants.MarketId3, + constants.MarketId0, constants.MarketId1, constants.MarketId2, constants.MarketId3, constants.MarketId4, }, }, "Non-empty proposed updates, Empty local updates": { @@ -392,6 +392,7 @@ func TestGetMarketsMissingFromPriceUpdates(t *testing.T) { types.NewMarketPriceUpdate(constants.MarketId0, constants.Price5), types.NewMarketPriceUpdate(constants.MarketId1, constants.Price6), types.NewMarketPriceUpdate(constants.MarketId3, constants.Price7), + types.NewMarketPriceUpdate(constants.MarketId4, constants.Price4), }, indexPrices: constants.AtTimeTSingleExchangePriceUpdate, smoothedIndexPrices: constants.AtTimeTSingleExchangeSmoothedPrices, @@ -404,19 +405,16 @@ func TestGetMarketsMissingFromPriceUpdates(t *testing.T) { indexPrices: constants.AtTimeTSingleExchangePriceUpdate, smoothedIndexPrices: constants.AtTimeTSingleExchangeSmoothedPrices, // The returned market ids must be sorted. - expectedMarketIds: []uint32{constants.MarketId0, constants.MarketId2, constants.MarketId3}, + expectedMarketIds: []uint32{constants.MarketId0, constants.MarketId2, constants.MarketId3, constants.MarketId4}, }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { // Setup. - ctx, k, _, indexPriceCache, marketToSmoothedPrices, mockTimeProvider := keepertest.PricesKeepers(t) + ctx, k, _, indexPriceCache, mockTimeProvider := keepertest.PricesKeepers(t) mockTimeProvider.On("Now").Return(constants.TimeT) keepertest.CreateTestMarkets(t, ctx, k) - for market, price := range tc.smoothedIndexPrices { - marketToSmoothedPrices.PushSmoothedPrice(market, price) - } indexPriceCache.UpdatePrices(tc.indexPrices) // Run. diff --git a/protocol/x/prices/module_test.go b/protocol/x/prices/module_test.go index dde0745bdd..6dc781c82f 100644 --- a/protocol/x/prices/module_test.go +++ b/protocol/x/prices/module_test.go @@ -49,7 +49,7 @@ func createAppModule(t *testing.T) prices.AppModule { func createAppModuleWithKeeper(t *testing.T) (prices.AppModule, *prices_keeper.Keeper, sdk.Context) { appCodec := codec.NewProtoCodec(module.InterfaceRegistry) - ctx, keeper, _, _, _, mockTimeProvider := keeper.PricesKeepers(t) + ctx, keeper, _, _, mockTimeProvider := keeper.PricesKeepers(t) // Mock the time provider response for market creation. mockTimeProvider.On("Now").Return(constants.TimeT) diff --git a/protocol/x/prices/types/errors.go b/protocol/x/prices/types/errors.go index aab94250b1..271bc76de3 100644 --- a/protocol/x/prices/types/errors.go +++ b/protocol/x/prices/types/errors.go @@ -24,6 +24,7 @@ var ( ErrMarketExponentCannotBeUpdated = errorsmod.Register(ModuleName, 202, "Market exponent cannot be updated") ErrMarketPricesAndParamsDontMatch = errorsmod.Register(ModuleName, 203, "Market prices and params don't match") ErrMarketParamAlreadyExists = errorsmod.Register(ModuleName, 204, "Market params already exists") + ErrMarketParamPairAlreadyExists = errorsmod.Register(ModuleName, 205, "Market params pair already exists") // 300 - 399: Price related errors. ErrIndexPriceNotAvailable = errorsmod.Register(ModuleName, 300, "Index price is not available") diff --git a/protocol/x/prices/types/types.go b/protocol/x/prices/types/types.go index 5c4f70f05c..2b7dd2e80d 100644 --- a/protocol/x/prices/types/types.go +++ b/protocol/x/prices/types/types.go @@ -3,6 +3,8 @@ package types import ( "cosmossdk.io/log" sdk "github.com/cosmos/cosmos-sdk/types" + slinkytypes "github.com/skip-mev/slinky/pkg/types" + oracletypes "github.com/skip-mev/slinky/x/oracle/types" ) type PricesKeeper interface { @@ -38,12 +40,15 @@ type PricesKeeper interface { performNonDeterministicValidation bool, ) error - // Proposal related. - UpdateSmoothedPrices( + GetValidMarketPriceUpdates( ctx sdk.Context, - linearInterpolateFunc func(v0 uint64, v1 uint64, ppm uint32) (uint64, error), - ) error + ) *MsgUpdateMarketPrices // Misc. Logger(ctx sdk.Context) log.Logger + + // Slinky compat + GetCurrencyPairFromID(ctx sdk.Context, id uint64) (cp slinkytypes.CurrencyPair, found bool) + GetIDForCurrencyPair(ctx sdk.Context, cp slinkytypes.CurrencyPair) (uint64, bool) + GetPriceForCurrencyPair(ctx sdk.Context, cp slinkytypes.CurrencyPair) (oracletypes.QuotePrice, error) } diff --git a/protocol/x/sending/e2e/isolated_subaccount_transfers_test.go b/protocol/x/sending/e2e/isolated_subaccount_transfers_test.go new file mode 100644 index 0000000000..244fee11e2 --- /dev/null +++ b/protocol/x/sending/e2e/isolated_subaccount_transfers_test.go @@ -0,0 +1,425 @@ +package sending_test + +import ( + "math/big" + "testing" + + "github.com/cosmos/gogoproto/proto" + + sdkmath "cosmossdk.io/math" + abcitypes "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/types" + sdktypes "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/dydxprotocol/v4-chain/protocol/dtypes" + "github.com/dydxprotocol/v4-chain/protocol/lib" + testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" + assettypes "github.com/dydxprotocol/v4-chain/protocol/x/assets/types" + clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + feetiertypes "github.com/dydxprotocol/v4-chain/protocol/x/feetiers/types" + perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" + sendingtypes "github.com/dydxprotocol/v4-chain/protocol/x/sending/types" + satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" + "github.com/stretchr/testify/require" +) + +func TestTransfer_Isolated_Non_Isolated_Subaccounts(t *testing.T) { + tests := map[string]struct { + // State. + subaccounts []satypes.Subaccount + collateralPoolBalances map[string]int64 + + // Parameters. + senderSubaccountId satypes.SubaccountId + receiverSubaccountId satypes.SubaccountId + quantums uint64 + + // Configuration. + liquidityTiers []perptypes.LiquidityTier + perpetuals []perptypes.Perpetual + clobPairs []clobtypes.ClobPair + + // Expectations. + expectedSubaccounts []satypes.Subaccount + expectedCollateralPoolBalances map[string]int64 + expectedErr string + }{ + `Can transfer from isolated subaccount to non-isolated subaccount, and coins are sent from + isolated subaccount collateral pool to cross collateral pool`: { + subaccounts: []satypes.Subaccount{ + constants.Alice_Num0_1ISO_LONG_10_000USD, + constants.Bob_Num0_10_000USD, + }, + collateralPoolBalances: map[string]int64{ + satypes.ModuleAddress.String(): 10_000_000_000, // $10,000 USDC + authtypes.NewModuleAddress( + satypes.ModuleName + ":" + lib.UintToString(constants.IsoUsd_IsolatedMarket.Params.Id), + ).String(): 10_000_000_000, // $10,000 USDC + }, + senderSubaccountId: constants.Alice_Num0, + receiverSubaccountId: constants.Bob_Num0, + quantums: 100_000_000, // $100 + liquidityTiers: constants.LiquidityTiers, + perpetuals: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + constants.EthUsd_20PercentInitial_10PercentMaintenance, + constants.IsoUsd_IsolatedMarket, + }, + expectedSubaccounts: []satypes.Subaccount{ + changeUsdcBalance(constants.Alice_Num0_1ISO_LONG_10_000USD, -100_000_000), + changeUsdcBalance(constants.Bob_Num0_10_000USD, 100_000_000), + }, + expectedCollateralPoolBalances: map[string]int64{ + satypes.ModuleAddress.String(): 10_100_000_000, // $10,100 USDC + authtypes.NewModuleAddress( + satypes.ModuleName + ":" + lib.UintToString(constants.IsoUsd_IsolatedMarket.Params.Id), + ).String(): 9_900_000_000, // $9,900 USDC + }, + expectedErr: "", + }, + `Can transfer from non-isolated subaccount to isolated subaccount, and coins are sent from + cross collateral pool to isolated subaccount collateral pool`: { + subaccounts: []satypes.Subaccount{ + constants.Alice_Num0_1ISO_LONG_10_000USD, + constants.Bob_Num0_10_000USD, + }, + collateralPoolBalances: map[string]int64{ + satypes.ModuleAddress.String(): 10_000_000_000, // $10,000 USDC + authtypes.NewModuleAddress( + satypes.ModuleName + ":" + lib.UintToString(constants.IsoUsd_IsolatedMarket.Params.Id), + ).String(): 10_000_000_000, // $10,000 USDC + }, + senderSubaccountId: constants.Bob_Num0, + receiverSubaccountId: constants.Alice_Num0, + quantums: 100_000_000, // $100 + liquidityTiers: constants.LiquidityTiers, + perpetuals: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + constants.EthUsd_20PercentInitial_10PercentMaintenance, + constants.IsoUsd_IsolatedMarket, + }, + expectedSubaccounts: []satypes.Subaccount{ + changeUsdcBalance(constants.Alice_Num0_1ISO_LONG_10_000USD, 100_000_000), + changeUsdcBalance(constants.Bob_Num0_10_000USD, -100_000_000), + }, + expectedCollateralPoolBalances: map[string]int64{ + satypes.ModuleAddress.String(): 9_900_000_000, // $9,900 USDC + authtypes.NewModuleAddress( + satypes.ModuleName + ":" + lib.UintToString(constants.IsoUsd_IsolatedMarket.Params.Id), + ).String(): 10_100_000_000, // $10,100 USDC + }, + expectedErr: "", + }, + `Can transfer from isolated subaccount to isolated subaccount in different isolated markets, and + coins are sent from one isolated collateral pool to the other isolated collateral pool`: { + subaccounts: []satypes.Subaccount{ + constants.Alice_Num0_1ISO_LONG_10_000USD, + constants.Bob_Num0_1ISO2_LONG_10_000USD, + }, + collateralPoolBalances: map[string]int64{ + authtypes.NewModuleAddress( + satypes.ModuleName + ":" + lib.UintToString(constants.Iso2Usd_IsolatedMarket.Params.Id), + ).String(): 10_000_000_000, // $10,000 USDC + authtypes.NewModuleAddress( + satypes.ModuleName + ":" + lib.UintToString(constants.IsoUsd_IsolatedMarket.Params.Id), + ).String(): 10_000_000_000, // $10,000 USDC + }, + senderSubaccountId: constants.Alice_Num0, + receiverSubaccountId: constants.Bob_Num0, + quantums: 100_000_000, // $100 + liquidityTiers: constants.LiquidityTiers, + perpetuals: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + constants.EthUsd_20PercentInitial_10PercentMaintenance, + constants.IsoUsd_IsolatedMarket, + constants.Iso2Usd_IsolatedMarket, + }, + expectedSubaccounts: []satypes.Subaccount{ + changeUsdcBalance(constants.Alice_Num0_1ISO_LONG_10_000USD, -100_000_000), + changeUsdcBalance(constants.Bob_Num0_1ISO2_LONG_10_000USD, 100_000_000), + }, + expectedCollateralPoolBalances: map[string]int64{ + authtypes.NewModuleAddress( + satypes.ModuleName + ":" + lib.UintToString(constants.IsoUsd_IsolatedMarket.Params.Id), + ).String(): 9_900_000_000, // $9,900 USDC + authtypes.NewModuleAddress( + satypes.ModuleName + ":" + lib.UintToString(constants.Iso2Usd_IsolatedMarket.Params.Id), + ).String(): 10_100_000_000, // $10,100 USDC + }, + expectedErr: "", + }, + `Can't transfer from isolated subaccount to non-isolated subaccount if collateral pool has + insufficient funds`: { + subaccounts: []satypes.Subaccount{ + constants.Alice_Num0_1ISO_LONG_10_000USD, + constants.Bob_Num0_10_000USD, + }, + collateralPoolBalances: map[string]int64{ + satypes.ModuleAddress.String(): 10_000_000_000, // $10,000 USDC + // Isolated perpetual collateral pool has no entry and is therefore empty ($0). + }, + senderSubaccountId: constants.Alice_Num0, + receiverSubaccountId: constants.Bob_Num0, + quantums: 100_000_000, // $100 + liquidityTiers: constants.LiquidityTiers, + perpetuals: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + constants.EthUsd_20PercentInitial_10PercentMaintenance, + constants.IsoUsd_IsolatedMarket, + }, + expectedSubaccounts: []satypes.Subaccount{ + constants.Alice_Num0_1ISO_LONG_10_000USD, // No changes as transfer should fail. + constants.Bob_Num0_10_000USD, + }, + expectedCollateralPoolBalances: map[string]int64{ + satypes.ModuleAddress.String(): 10_000_000_000, // No changes to collateral pools as transfer fails. + }, + expectedErr: "insufficient funds", + }, + `Can't transfer from isolated subaccount to isolated subaccount with different isolated + perpetuals if collateral pool has insufficient funds`: { + subaccounts: []satypes.Subaccount{ + constants.Alice_Num0_1ISO_LONG_10_000USD, + constants.Bob_Num0_1ISO2_LONG_10_000USD, + }, + collateralPoolBalances: map[string]int64{ + authtypes.NewModuleAddress( + satypes.ModuleName + ":" + lib.UintToString(constants.Iso2Usd_IsolatedMarket.Params.Id), + ).String(): 10_000_000_000, // $10,000 USDC + // Isolated perpetual collateral pool has no entry and is therefore empty ($0). + }, + senderSubaccountId: constants.Alice_Num0, + receiverSubaccountId: constants.Bob_Num0, + quantums: 100_000_000, // $100 + liquidityTiers: constants.LiquidityTiers, + perpetuals: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + constants.EthUsd_20PercentInitial_10PercentMaintenance, + constants.IsoUsd_IsolatedMarket, + constants.Iso2Usd_IsolatedMarket, + }, + expectedSubaccounts: []satypes.Subaccount{ + constants.Alice_Num0_1ISO_LONG_10_000USD, // No changes as transfer should fail. + constants.Bob_Num0_1ISO2_LONG_10_000USD, + }, + expectedCollateralPoolBalances: map[string]int64{ + authtypes.NewModuleAddress( + satypes.ModuleName + ":" + lib.UintToString(constants.Iso2Usd_IsolatedMarket.Params.Id), + ).String(): 10_000_000_000, // No changes to collateral pools as transfer fails. + }, + expectedErr: "insufficient funds", + }, + `Can transfer from isolated subaccount to isolated subaccount in the same isolated markets, no + coins are sent`: { + subaccounts: []satypes.Subaccount{ + constants.Alice_Num0_1ISO_LONG_10_000USD, + constants.Bob_Num0_1ISO_LONG_10_000USD, + }, + collateralPoolBalances: map[string]int64{ + authtypes.NewModuleAddress( + satypes.ModuleName + ":" + lib.UintToString(constants.IsoUsd_IsolatedMarket.Params.Id), + ).String(): 10_000_000_000, // $10,000 USDC + }, + senderSubaccountId: constants.Alice_Num0, + receiverSubaccountId: constants.Bob_Num0, + quantums: 100_000_000, // $100 + liquidityTiers: constants.LiquidityTiers, + perpetuals: []perptypes.Perpetual{ + constants.BtcUsd_20PercentInitial_10PercentMaintenance, + constants.EthUsd_20PercentInitial_10PercentMaintenance, + constants.IsoUsd_IsolatedMarket, + }, + expectedSubaccounts: []satypes.Subaccount{ + changeUsdcBalance(constants.Alice_Num0_1ISO_LONG_10_000USD, -100_000_000), + changeUsdcBalance(constants.Bob_Num0_1ISO_LONG_10_000USD, 100_000_000), + }, + expectedCollateralPoolBalances: map[string]int64{ + authtypes.NewModuleAddress( + satypes.ModuleName + ":" + lib.UintToString(constants.IsoUsd_IsolatedMarket.Params.Id), + ).String(): 10_000_000_000, // No change + }, + expectedErr: "", + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // Configure the test application. + tApp := testapp.NewTestAppBuilder(t).WithGenesisDocFn(func() (genesis types.GenesisDoc) { + genesis = testapp.DefaultGenesis() + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *assettypes.GenesisState) { + genesisState.Assets = []assettypes.Asset{ + *constants.Usdc, + } + }, + ) + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *banktypes.GenesisState) { + // If the collateral pool address is already in bank genesis state, update it. + foundPools := make(map[string]struct{}) + for i, bal := range genesisState.Balances { + usdcBal, exists := tc.collateralPoolBalances[bal.Address] + if exists { + foundPools[bal.Address] = struct{}{} + genesisState.Balances[i] = banktypes.Balance{ + Address: bal.Address, + Coins: sdktypes.Coins{ + sdktypes.NewCoin(constants.Usdc.Denom, sdkmath.NewInt(usdcBal)), + }, + } + } + } + // If the collateral pool address is not in the bank genesis state, add it. + for addr, bal := range tc.collateralPoolBalances { + _, exists := foundPools[addr] + if exists { + continue + } + genesisState.Balances = append(genesisState.Balances, banktypes.Balance{ + Address: addr, + Coins: sdktypes.Coins{ + sdktypes.NewCoin(constants.Usdc.Denom, sdkmath.NewInt(bal)), + }, + }) + } + }, + ) + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *perptypes.GenesisState) { + genesisState.Params = constants.PerpetualsGenesisParams + genesisState.LiquidityTiers = tc.liquidityTiers + genesisState.Perpetuals = tc.perpetuals + }, + ) + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *satypes.GenesisState) { + genesisState.Subaccounts = tc.subaccounts + }, + ) + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *feetiertypes.GenesisState) { + genesisState.Params = constants.PerpetualFeeParamsNoFee + }, + ) + return genesis + }).Build() + + ctx := tApp.AdvanceToBlock(2, testapp.AdvanceToBlockOptions{}) + + // Send transfer message. + var msg proto.Message + transferMsg := sendingtypes.MsgCreateTransfer{ + Transfer: &sendingtypes.Transfer{ + Sender: tc.senderSubaccountId, + Recipient: tc.receiverSubaccountId, + AssetId: constants.Usdc.Id, + Amount: tc.quantums, + }, + } + msg = &transferMsg + for _, checkTx := range testapp.MustMakeCheckTxsWithSdkMsg( + ctx, + tApp.App, + testapp.MustMakeCheckTxOptions{ + AccAddressForSigning: tc.senderSubaccountId.Owner, + Gas: 1000000, + FeeAmt: constants.TestFeeCoins_5Cents, + }, + msg, + ) { + resp := tApp.CheckTx(checkTx) + require.Conditionf(t, resp.IsOK, "Expected CheckTx to succeed. Response: %+v", resp) + } + + // Verify test expectations, subaccount and collateral pools should be updated. + ctx = tApp.AdvanceToBlock( + 3, + testapp.AdvanceToBlockOptions{ + ValidateFinalizeBlock: func( + ctx sdktypes.Context, + request abcitypes.RequestFinalizeBlock, + response abcitypes.ResponseFinalizeBlock, + ) (haltchain bool) { + execResult := response.TxResults[1] + if tc.expectedErr != "" { + // Note the first TX is MsgProposedOperations, the second is all other TXs. + execResult := response.TxResults[1] + require.True(t, execResult.IsErr()) + require.Equal(t, sdkerrors.ErrInsufficientFunds.ABCICode(), execResult.Code) + require.Contains(t, execResult.Log, tc.expectedErr) + } else { + require.False(t, execResult.IsErr()) + } + return false + }, + }, + ) + for _, expectedSubaccount := range tc.expectedSubaccounts { + require.Equal( + t, + expectedSubaccount, + tApp.App.SubaccountsKeeper.GetSubaccount(ctx, *expectedSubaccount.Id), + ) + } + for addr, expectedBalance := range tc.expectedCollateralPoolBalances { + require.Equal( + t, + sdkmath.NewIntFromBigInt(big.NewInt(expectedBalance)), + tApp.App.BankKeeper.GetBalance(ctx, sdktypes.MustAccAddressFromBech32(addr), constants.Usdc.Denom).Amount, + ) + } + }) + } +} + +// Creates a copy of a subaccount and changes the USDC asset position size of the subaccount by the passed in delta. +func changeUsdcBalance(subaccount satypes.Subaccount, deltaQuantums int64) satypes.Subaccount { + subaccountId := satypes.SubaccountId{ + Owner: subaccount.Id.Owner, + Number: subaccount.Id.Number, + } + assetPositions := make([]*satypes.AssetPosition, 0) + for _, ap := range subaccount.AssetPositions { + if ap.AssetId != constants.Usdc.Id { + assetPositions = append(assetPositions, &satypes.AssetPosition{ + AssetId: ap.AssetId, + Quantums: dtypes.NewInt(ap.Quantums.BigInt().Int64()), + }) + } else { + assetPositions = append(assetPositions, &satypes.AssetPosition{ + AssetId: ap.AssetId, + Quantums: dtypes.NewInt(ap.Quantums.BigInt().Int64() + deltaQuantums), + }) + } + } + if len(assetPositions) == 0 { + assetPositions = nil + } + perpetualPositions := make([]*satypes.PerpetualPosition, 0) + for _, pp := range subaccount.PerpetualPositions { + perpetualPositions = append(perpetualPositions, &satypes.PerpetualPosition{ + PerpetualId: pp.PerpetualId, + Quantums: dtypes.NewInt(pp.Quantums.BigInt().Int64()), + FundingIndex: dtypes.NewInt(pp.FundingIndex.BigInt().Int64()), + }) + } + if len(perpetualPositions) == 0 { + perpetualPositions = nil + } + return satypes.Subaccount{ + Id: &subaccountId, + AssetPositions: assetPositions, + PerpetualPositions: perpetualPositions, + MarginEnabled: subaccount.MarginEnabled, + } +} diff --git a/protocol/x/sending/keeper/transfer.go b/protocol/x/sending/keeper/transfer.go index 99205460b6..d1ad32f452 100644 --- a/protocol/x/sending/keeper/transfer.go +++ b/protocol/x/sending/keeper/transfer.go @@ -22,21 +22,17 @@ func (k Keeper) ProcessTransfer( ) (err error) { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), metrics.ProcessTransfer, metrics.Latency) - updates := []satypes.Update{ - pendingTransfer.GetSenderSubaccountUpdate(), - pendingTransfer.GetRecipientSubaccountUpdate(), - } - - success, successPerUpdate, err := k.subaccountsKeeper.UpdateSubaccounts(ctx, updates, satypes.Transfer) + err = k.subaccountsKeeper.TransferFundsFromSubaccountToSubaccount( + ctx, + pendingTransfer.Sender, + pendingTransfer.Recipient, + pendingTransfer.AssetId, + pendingTransfer.GetBigQuantums(), + ) if err != nil { return err } - // If not successful, return error indicating why. - if err := satypes.GetErrorFromUpdateResults(success, successPerUpdate, updates); err != nil { - return err - } - recipientAddr := pendingTransfer.Recipient.MustGetAccAddress() // Create an account for the recipient address if one does not exist. diff --git a/protocol/x/sending/types/expected_keepers.go b/protocol/x/sending/types/expected_keepers.go index 87561154de..17678270e9 100644 --- a/protocol/x/sending/types/expected_keepers.go +++ b/protocol/x/sending/types/expected_keepers.go @@ -53,6 +53,13 @@ type SubaccountsKeeper interface { assetId uint32, amount *big.Int, ) (err error) + TransferFundsFromSubaccountToSubaccount( + ctx sdk.Context, + senderSubaccountId satypes.SubaccountId, + recipientSubaccountId satypes.SubaccountId, + assetId uint32, + quantums *big.Int, + ) (err error) SetSubaccount(ctx sdk.Context, subaccount satypes.Subaccount) GetSubaccount( ctx sdk.Context, diff --git a/protocol/x/subaccounts/keeper/isolated_subaccount.go b/protocol/x/subaccounts/keeper/isolated_subaccount.go new file mode 100644 index 0000000000..e5547d3986 --- /dev/null +++ b/protocol/x/subaccounts/keeper/isolated_subaccount.go @@ -0,0 +1,142 @@ +package keeper + +import ( + "math" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dydxprotocol/v4-chain/protocol/lib" + perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" + "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" +) + +// checkIsolatedSubaccountConstaints will validate all `updates` to the relevant subaccounts against +// isolated subaccount constraints. +// This function checks each update in isolation, so if multiple updates for the same subaccount id +// are passed in, they are not evaluated separately. +// The input subaccounts must be settled. +// +// Returns a `success` value of `true` if all updates are valid. +// Returns a `successPerUpdates` value, which is a slice of `UpdateResult`. +// These map to the updates and are used to indicate which of the updates +// caused a failure, if any. +func (k Keeper) checkIsolatedSubaccountConstraints( + ctx sdk.Context, + settledUpdates []settledUpdate, + perpetuals []perptypes.Perpetual, +) ( + success bool, + successPerUpdate []types.UpdateResult, + err error, +) { + success = true + successPerUpdate = make([]types.UpdateResult, len(settledUpdates)) + var perpIdToMarketType = make(map[uint32]perptypes.PerpetualMarketType) + + for _, perpetual := range perpetuals { + perpIdToMarketType[perpetual.GetId()] = perpetual.Params.MarketType + } + + for i, u := range settledUpdates { + result, err := isValidIsolatedPerpetualUpdates(u, perpIdToMarketType) + if err != nil { + return false, nil, err + } + if result != types.Success { + success = false + } + + successPerUpdate[i] = result + } + + return success, successPerUpdate, nil +} + +// Checks whether the perpetual updates to a settled subaccount violates constraints for isolated +// perpetuals. This function assumes the settled subaccount is valid and does not violate the +// the constraints. +// The constraint being checked is: +// - a subaccount with a position in an isolated perpetual cannot have updates for other +// perpetuals +// - a subaccount with a position in a non-isolated perpetual cannot have updates for isolated +// perpetuals +// - a subaccount with no positions cannot be updated to have positions in multiple isolated +// perpetuals or a combination of isolated and non-isolated perpetuals +func isValidIsolatedPerpetualUpdates( + settledUpdate settledUpdate, + perpIdToMarketType map[uint32]perptypes.PerpetualMarketType, +) (types.UpdateResult, error) { + // If there are no perpetual updates, then this update does not violate constraints for isolated + // markets. + if len(settledUpdate.PerpetualUpdates) == 0 { + return types.Success, nil + } + + // Check if the updates contain an update to an isolated perpetual. + hasIsolatedUpdate := false + isolatedUpdatePerpetualId := uint32(math.MaxUint32) + for _, perpetualUpdate := range settledUpdate.PerpetualUpdates { + marketType, exists := perpIdToMarketType[perpetualUpdate.PerpetualId] + if !exists { + return types.UpdateCausedError, errorsmod.Wrap( + perptypes.ErrPerpetualDoesNotExist, lib.UintToString(perpetualUpdate.PerpetualId), + ) + } + + if marketType == perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_ISOLATED { + hasIsolatedUpdate = true + isolatedUpdatePerpetualId = perpetualUpdate.PerpetualId + break + } + } + + // Check if the subaccount has a position in an isolated perpetual. + // Assumes the subaccount itself does not violate the isolated perpetual constraints. + isIsolatedSubaccount := false + isolatedPositionPerpetualId := uint32(math.MaxUint32) + hasPerpetualPositions := len(settledUpdate.SettledSubaccount.PerpetualPositions) > 0 + for _, perpetualPosition := range settledUpdate.SettledSubaccount.PerpetualPositions { + marketType, exists := perpIdToMarketType[perpetualPosition.PerpetualId] + if !exists { + return types.UpdateCausedError, errorsmod.Wrap( + perptypes.ErrPerpetualDoesNotExist, lib.UintToString(perpetualPosition.PerpetualId), + ) + } + + if marketType == perptypes.PerpetualMarketType_PERPETUAL_MARKET_TYPE_ISOLATED { + isIsolatedSubaccount = true + isolatedPositionPerpetualId = perpetualPosition.PerpetualId + break + } + } + + // A subaccount with a perpetual position in an isolated perpetual cannot have updates to other + // non-isolated perpetuals. + if isIsolatedSubaccount && !hasIsolatedUpdate { + return types.ViolatesIsolatedSubaccountConstraints, nil + } + + // A subaccount with perpetual positions in non-isolated perpetuals cannot have an update + // to an isolated perpetual. + if !isIsolatedSubaccount && hasPerpetualPositions && hasIsolatedUpdate { + return types.ViolatesIsolatedSubaccountConstraints, nil + } + + // There cannot be more than a single perpetual update if an update to an isolated perpetual + // exists in the slice of perpetual updates. + if hasIsolatedUpdate && len(settledUpdate.PerpetualUpdates) > 1 { + return types.ViolatesIsolatedSubaccountConstraints, nil + } + + // Note we can assume that if `hasIsolatedUpdate` is true, there is only a single perpetual + // update for the subaccount, given the above check. + // A subaccount with a perpetual position in an isolated perpetual cannot have an update to + // another isolated perpetual. + if isIsolatedSubaccount && + hasIsolatedUpdate && + isolatedPositionPerpetualId != isolatedUpdatePerpetualId { + return types.ViolatesIsolatedSubaccountConstraints, nil + } + + return types.Success, nil +} diff --git a/protocol/x/subaccounts/keeper/subaccount.go b/protocol/x/subaccounts/keeper/subaccount.go index c881a48064..ca9e978e80 100644 --- a/protocol/x/subaccounts/keeper/subaccount.go +++ b/protocol/x/subaccounts/keeper/subaccount.go @@ -280,13 +280,18 @@ func (k Keeper) UpdateSubaccounts( return false, nil, err } - success, successPerUpdate, err = k.internalCanUpdateSubaccounts(ctx, settledUpdates, updateType) + allPerps := k.perpetualsKeeper.GetAllPerpetuals(ctx) + success, successPerUpdate, err = k.internalCanUpdateSubaccounts( + ctx, + settledUpdates, + updateType, + allPerps, + ) if !success || err != nil { return success, successPerUpdate, err } // Get a mapping from perpetual Id to current perpetual funding index. - allPerps := k.perpetualsKeeper.GetAllPerpetuals(ctx) perpIdToFundingIndex := make(map[uint32]dtypes.SerializableInt) for _, perp := range allPerps { perpIdToFundingIndex[perp.Params.Id] = perp.FundingIndex @@ -378,7 +383,8 @@ func (k Keeper) CanUpdateSubaccounts( return false, nil, err } - return k.internalCanUpdateSubaccounts(ctx, settledUpdates, updateType) + allPerps := k.perpetualsKeeper.GetAllPerpetuals(ctx) + return k.internalCanUpdateSubaccounts(ctx, settledUpdates, updateType, allPerps) } // getSettledSubaccount returns 1. a new settled subaccount given an unsettled subaccount, @@ -524,13 +530,26 @@ func (k Keeper) internalCanUpdateSubaccounts( ctx sdk.Context, settledUpdates []settledUpdate, updateType types.UpdateType, + perpetuals []perptypes.Perpetual, ) ( success bool, successPerUpdate []types.UpdateResult, err error, ) { - success = true - successPerUpdate = make([]types.UpdateResult, len(settledUpdates)) + // TODO(TRA-99): Add integration / E2E tests on order placement / matching with this new + // constraint. + // Check if the updates satisfy the isolated perpetual constraints. + success, successPerUpdate, err = k.checkIsolatedSubaccountConstraints( + ctx, + settledUpdates, + perpetuals, + ) + if err != nil { + return false, nil, err + } + if !success { + return success, successPerUpdate, nil + } // Block all withdrawals and transfers if either of the following is true within the last // `WITHDRAWAL_AND_TRANSFERS_BLOCKED_AFTER_NEGATIVE_TNC_SUBACCOUNT_SEEN_BLOCKS`: diff --git a/protocol/x/subaccounts/keeper/subaccount_test.go b/protocol/x/subaccounts/keeper/subaccount_test.go index cd9e3d32e9..699ff4ce44 100644 --- a/protocol/x/subaccounts/keeper/subaccount_test.go +++ b/protocol/x/subaccounts/keeper/subaccount_test.go @@ -2019,10 +2019,11 @@ func TestUpdateSubaccounts(t *testing.T) { ), }, marketParamPrices: []pricestypes.MarketParamPrice{ - *pricestest.GenerateMarketParamPrice(pricestest.WithId(100)), + *pricestest.GenerateMarketParamPrice(pricestest.WithId(100), pricestest.WithPair("0")), *pricestest.GenerateMarketParamPrice( pricestest.WithId(101), pricestest.WithPriceValue(0), + pricestest.WithPair("1"), ), }, perpetualPositions: []*types.PerpetualPosition{ @@ -2072,6 +2073,186 @@ func TestUpdateSubaccounts(t *testing.T) { }, msgSenderEnabled: true, }, + "Isolated subaccounts - has update for both an isolated perpetual and non-isolated perpetual": { + assetPositions: testutil.CreateUsdcAssetPosition(big.NewInt(1_000_000_000_000)), + expectedSuccess: false, + expectedSuccessPerUpdate: []types.UpdateResult{types.ViolatesIsolatedSubaccountConstraints}, + perpetuals: []perptypes.Perpetual{ + constants.BtcUsd_NoMarginRequirement, + constants.IsoUsd_IsolatedMarket, + }, + expectedAssetPositions: []*types.AssetPosition{ + { + AssetId: uint32(0), + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + updates: []types.Update{ + { + PerpetualUpdates: []types.PerpetualUpdate{ + { + PerpetualId: uint32(0), + BigQuantumsDelta: big.NewInt(-100_000_000), // -1 BTC + }, + { + PerpetualId: uint32(3), + BigQuantumsDelta: big.NewInt(1_000_000_000), // 1 ISO + }, + }, + }, + }, + msgSenderEnabled: true, + }, + "Isolated subaccounts - has update for both 2 isolated perpetuals": { + assetPositions: testutil.CreateUsdcAssetPosition(big.NewInt(1_000_000_000_000)), + expectedSuccess: false, + expectedSuccessPerUpdate: []types.UpdateResult{types.ViolatesIsolatedSubaccountConstraints}, + perpetuals: []perptypes.Perpetual{ + constants.IsoUsd_IsolatedMarket, + constants.Iso2Usd_IsolatedMarket, + }, + expectedAssetPositions: []*types.AssetPosition{ + { + AssetId: uint32(0), + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + updates: []types.Update{ + { + PerpetualUpdates: []types.PerpetualUpdate{ + { + PerpetualId: uint32(3), + BigQuantumsDelta: big.NewInt(-1_000_000_000), // 1 ISO + }, + { + PerpetualId: uint32(4), + BigQuantumsDelta: big.NewInt(10_000_000), // 1 ISO2 + }, + }, + }, + }, + msgSenderEnabled: true, + }, + "Isolated subaccounts - subaccount with isolated perpetual position has update for non-isolated perpetual": { + assetPositions: testutil.CreateUsdcAssetPosition(big.NewInt(1_000_000_000_000)), + expectedSuccess: false, + expectedSuccessPerUpdate: []types.UpdateResult{types.ViolatesIsolatedSubaccountConstraints}, + perpetuals: []perptypes.Perpetual{ + constants.BtcUsd_NoMarginRequirement, + constants.IsoUsd_IsolatedMarket, + }, + perpetualPositions: []*types.PerpetualPosition{ + { + PerpetualId: uint32(3), + Quantums: dtypes.NewInt(1_000_000_000), // 1 ISO + FundingIndex: dtypes.NewInt(0), + }, + }, + expectedPerpetualPositions: []*types.PerpetualPosition{ + { + PerpetualId: uint32(3), + Quantums: dtypes.NewInt(1_000_000_000), + FundingIndex: dtypes.NewInt(0), + }, + }, + expectedAssetPositions: []*types.AssetPosition{ + { + AssetId: uint32(0), + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + updates: []types.Update{ + { + PerpetualUpdates: []types.PerpetualUpdate{ + { + PerpetualId: uint32(0), + BigQuantumsDelta: big.NewInt(-100_000_000), // -1 BTC + }, + }, + }, + }, + msgSenderEnabled: true, + }, + "Isolated subaccounts - subaccount with isolated perpetual position has update for another isolated perpetual": { + assetPositions: testutil.CreateUsdcAssetPosition(big.NewInt(1_000_000_000_000)), + expectedSuccess: false, + expectedSuccessPerUpdate: []types.UpdateResult{types.ViolatesIsolatedSubaccountConstraints}, + perpetuals: []perptypes.Perpetual{ + constants.IsoUsd_IsolatedMarket, + constants.Iso2Usd_IsolatedMarket, + }, + perpetualPositions: []*types.PerpetualPosition{ + { + PerpetualId: uint32(3), + Quantums: dtypes.NewInt(1_000_000_000), // 1 ISO + FundingIndex: dtypes.NewInt(0), + }, + }, + expectedPerpetualPositions: []*types.PerpetualPosition{ + { + PerpetualId: uint32(3), + Quantums: dtypes.NewInt(1_000_000_000), + FundingIndex: dtypes.NewInt(0), + }, + }, + expectedAssetPositions: []*types.AssetPosition{ + { + AssetId: uint32(0), + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + updates: []types.Update{ + { + PerpetualUpdates: []types.PerpetualUpdate{ + { + PerpetualId: uint32(4), + BigQuantumsDelta: big.NewInt(-10_000_000), // -1 ISO2 + }, + }, + }, + }, + msgSenderEnabled: true, + }, + "Isolated subaccounts - subaccount with non-isolated perpetual position has update for isolated perpetual": { + assetPositions: testutil.CreateUsdcAssetPosition(big.NewInt(1_000_000_000_000)), + expectedSuccess: false, + expectedSuccessPerUpdate: []types.UpdateResult{types.ViolatesIsolatedSubaccountConstraints}, + perpetuals: []perptypes.Perpetual{ + constants.BtcUsd_NoMarginRequirement, + constants.IsoUsd_IsolatedMarket, + }, + perpetualPositions: []*types.PerpetualPosition{ + { + PerpetualId: uint32(0), + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(0), + }, + }, + expectedPerpetualPositions: []*types.PerpetualPosition{ + { + PerpetualId: uint32(0), + Quantums: dtypes.NewInt(100_000_000), + FundingIndex: dtypes.NewInt(0), + }, + }, + expectedAssetPositions: []*types.AssetPosition{ + { + AssetId: uint32(0), + Quantums: dtypes.NewInt(1_000_000_000_000), + }, + }, + updates: []types.Update{ + { + PerpetualUpdates: []types.PerpetualUpdate{ + { + PerpetualId: uint32(3), + BigQuantumsDelta: big.NewInt(-1_000_000_000), // -1 ISO + }, + }, + }, + }, + msgSenderEnabled: true, + }, } for name, tc := range tests { @@ -3425,10 +3606,11 @@ func TestCanUpdateSubaccounts(t *testing.T) { ), }, marketParamPrices: []pricestypes.MarketParamPrice{ - *pricestest.GenerateMarketParamPrice(pricestest.WithId(100)), + *pricestest.GenerateMarketParamPrice(pricestest.WithId(100), pricestest.WithPair("0")), *pricestest.GenerateMarketParamPrice( pricestest.WithId(101), pricestest.WithPriceValue(0), + pricestest.WithPair("1"), ), }, perpetualPositions: []*types.PerpetualPosition{ @@ -3458,6 +3640,130 @@ func TestCanUpdateSubaccounts(t *testing.T) { }, }, }, + "Isolated subaccounts - has update for both an isolated perpetual and non-isolated perpetual": { + assetPositions: testutil.CreateUsdcAssetPosition(big.NewInt(1_000_000_000_000)), + expectedSuccess: false, + expectedSuccessPerUpdate: []types.UpdateResult{types.ViolatesIsolatedSubaccountConstraints}, + perpetuals: []perptypes.Perpetual{ + constants.BtcUsd_NoMarginRequirement, + constants.IsoUsd_IsolatedMarket, + }, + updates: []types.Update{ + { + PerpetualUpdates: []types.PerpetualUpdate{ + { + PerpetualId: uint32(0), + BigQuantumsDelta: big.NewInt(-100_000_000), // -1 BTC + }, + { + PerpetualId: uint32(3), + BigQuantumsDelta: big.NewInt(1_000_000_000), // 1 ISO + }, + }, + }, + }, + }, + "Isolated subaccounts - has update for both 2 isolated perpetuals": { + assetPositions: testutil.CreateUsdcAssetPosition(big.NewInt(1_000_000_000_000)), + expectedSuccess: false, + expectedSuccessPerUpdate: []types.UpdateResult{types.ViolatesIsolatedSubaccountConstraints}, + perpetuals: []perptypes.Perpetual{ + constants.IsoUsd_IsolatedMarket, + constants.Iso2Usd_IsolatedMarket, + }, + updates: []types.Update{ + { + PerpetualUpdates: []types.PerpetualUpdate{ + { + PerpetualId: uint32(3), + BigQuantumsDelta: big.NewInt(-1_000_000_000), // 1 ISO + }, + { + PerpetualId: uint32(4), + BigQuantumsDelta: big.NewInt(10_000_000), // 1 ISO2 + }, + }, + }, + }, + }, + "Isolated subaccounts - subaccount with isolated perpetual position has update for non-isolated perpetual": { + assetPositions: testutil.CreateUsdcAssetPosition(big.NewInt(1_000_000_000_000)), + expectedSuccess: false, + expectedSuccessPerUpdate: []types.UpdateResult{types.ViolatesIsolatedSubaccountConstraints}, + perpetuals: []perptypes.Perpetual{ + constants.BtcUsd_NoMarginRequirement, + constants.IsoUsd_IsolatedMarket, + }, + perpetualPositions: []*types.PerpetualPosition{ + { + PerpetualId: uint32(3), + Quantums: dtypes.NewInt(1_000_000_000), // 1 ISO + FundingIndex: dtypes.NewInt(0), + }, + }, + updates: []types.Update{ + { + PerpetualUpdates: []types.PerpetualUpdate{ + { + PerpetualId: uint32(0), + BigQuantumsDelta: big.NewInt(-100_000_000), // -1 BTC + }, + }, + }, + }, + }, + "Isolated subaccounts - subaccount with isolated perpetual position has update for another isolated perpetual": { + assetPositions: testutil.CreateUsdcAssetPosition(big.NewInt(1_000_000_000_000)), + expectedSuccess: false, + expectedSuccessPerUpdate: []types.UpdateResult{types.ViolatesIsolatedSubaccountConstraints}, + perpetuals: []perptypes.Perpetual{ + constants.IsoUsd_IsolatedMarket, + constants.Iso2Usd_IsolatedMarket, + }, + perpetualPositions: []*types.PerpetualPosition{ + { + PerpetualId: uint32(3), + Quantums: dtypes.NewInt(1_000_000_000), // 1 ISO + FundingIndex: dtypes.NewInt(0), + }, + }, + updates: []types.Update{ + { + PerpetualUpdates: []types.PerpetualUpdate{ + { + PerpetualId: uint32(4), + BigQuantumsDelta: big.NewInt(-10_000_000), // -1 ISO2 + }, + }, + }, + }, + }, + "Isolated subaccounts - subaccount with non-isolated perpetual position has update for isolated perpetual": { + assetPositions: testutil.CreateUsdcAssetPosition(big.NewInt(1_000_000_000_000)), + expectedSuccess: false, + expectedSuccessPerUpdate: []types.UpdateResult{types.ViolatesIsolatedSubaccountConstraints}, + perpetuals: []perptypes.Perpetual{ + constants.BtcUsd_NoMarginRequirement, + constants.IsoUsd_IsolatedMarket, + }, + perpetualPositions: []*types.PerpetualPosition{ + { + PerpetualId: uint32(0), + Quantums: dtypes.NewInt(100_000_000), // 1 BTC + FundingIndex: dtypes.NewInt(0), + }, + }, + updates: []types.Update{ + { + PerpetualUpdates: []types.PerpetualUpdate{ + { + PerpetualId: uint32(3), + BigQuantumsDelta: big.NewInt(-1_000_000_000), // -1 ISO + }, + }, + }, + }, + }, } for name, tc := range tests { diff --git a/protocol/x/subaccounts/keeper/transfer.go b/protocol/x/subaccounts/keeper/transfer.go index d556426f4b..ca6cb33c45 100644 --- a/protocol/x/subaccounts/keeper/transfer.go +++ b/protocol/x/subaccounts/keeper/transfer.go @@ -316,3 +316,90 @@ func (k Keeper) TransferInsuranceFundPayments( []sdk.Coin{coinToTransfer}, ) } + +// TransferFundsFromSubaccountToSubaccount returns an error if the call to `k.CanUpdateSubaccounts()` +// fails. Otherwise, updates the asset quantums in the subaccounts, translates the +// `assetId` and `quantums` into a `sdk.Coin`, and call `bankKeeper.SendCoins()` if the collateral +// pools for the two subaccounts are different. +// TODO(CORE-168): Change function interface to accept `denom` and `amount` instead of `assetId` and +// `quantums`. +func (k Keeper) TransferFundsFromSubaccountToSubaccount( + ctx sdk.Context, + senderSubaccountId types.SubaccountId, + recipientSubaccountId types.SubaccountId, + assetId uint32, + quantums *big.Int, +) error { + // TODO(DEC-715): Support non-USDC assets. + if assetId != assettypes.AssetUsdc.Id { + return types.ErrAssetTransferThroughBankNotImplemented + } + + updates := []types.Update{ + { + SubaccountId: senderSubaccountId, + AssetUpdates: []types.AssetUpdate{ + { + AssetId: assettypes.AssetUsdc.Id, + BigQuantumsDelta: new(big.Int).Neg(quantums), + }, + }, + }, + { + SubaccountId: recipientSubaccountId, + AssetUpdates: []types.AssetUpdate{ + { + AssetId: assettypes.AssetUsdc.Id, + BigQuantumsDelta: new(big.Int).Set(quantums), + }, + }, + }, + } + success, successPerUpdate, err := k.CanUpdateSubaccounts(ctx, updates, types.Transfer) + if err != nil { + return err + } + if err := types.GetErrorFromUpdateResults(success, successPerUpdate, updates); err != nil { + return err + } + + _, coinToTransfer, err := k.assetsKeeper.ConvertAssetToCoin( + ctx, + assetId, + quantums, + ) + if err != nil { + return err + } + + senderCollateralPoolAddr, err := k.GetCollateralPoolForSubaccount(ctx, senderSubaccountId) + if err != nil { + return err + } + + recipientCollateralPoolAddr, err := k.GetCollateralPoolForSubaccount(ctx, recipientSubaccountId) + if err != nil { + return err + } + + // Different collateral pool address, need to do a bank send. + if !senderCollateralPoolAddr.Equals(recipientCollateralPoolAddr) { + // Use SendCoins API instead of SendCoinsFromModuleToModule since we don't need the + // module account feature + if err := k.bankKeeper.SendCoins( + ctx, + senderCollateralPoolAddr, + recipientCollateralPoolAddr, + []sdk.Coin{coinToTransfer}, + ); err != nil { + return err + } + } + + // Apply subaccount updates. + return k.applyValidSubaccountUpdateForTransfer( + ctx, + updates, + types.Transfer, + ) +} diff --git a/protocol/x/subaccounts/keeper/transfer_test.go b/protocol/x/subaccounts/keeper/transfer_test.go index 831c963368..1ca9f61ef8 100644 --- a/protocol/x/subaccounts/keeper/transfer_test.go +++ b/protocol/x/subaccounts/keeper/transfer_test.go @@ -574,6 +574,599 @@ func TestWithdrawFundsFromSubaccountToAccount_DepositFundsFromAccountToSubaccoun } } +func TestTransferFundsFromSubaccountToSubaccount_Success(t *testing.T) { + tests := map[string]struct { + // transfer details + asset asstypes.Asset + quantums *big.Int + + // Subaccount state. + senderAssetPositions []*types.AssetPosition + senderPerpetualPositions []*types.PerpetualPosition + recipientAssetPositions []*types.AssetPosition + recipientPerpetualPositions []*types.PerpetualPosition + + // Module account state. + senderCollateralPoolBalance *big.Int + recipientCollateralPoolBalance *big.Int + + senderCollateralPoolAddr sdk.AccAddress + recipientCollateralPoolAddr sdk.AccAddress + + // Expectations. + expectedSenderAssetPositions []*types.AssetPosition + expectedRecipientAssetPositions []*types.AssetPosition + expectedSenderQuoteBalance *big.Int + expectedRecipientQuoteBalance *big.Int + expectedSenderCollateralPoolBalance *big.Int + expectedRecipientCollateralPoolBalance *big.Int + }{ + "Send USDC from non-isolated subaccount to non-isolated subaccount": { + asset: *constants.Usdc, + quantums: big.NewInt(500), + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(500)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneBTCLong, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneBTCLong, + }, + senderCollateralPoolBalance: big.NewInt(1100), // 500 + 600 + recipientCollateralPoolBalance: big.NewInt(1100), // same collateral pool, same balance + senderCollateralPoolAddr: types.ModuleAddress, + recipientCollateralPoolAddr: types.ModuleAddress, + expectedRecipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(1100)), + expectedSenderQuoteBalance: big.NewInt(0), // 500 - 500 + expectedRecipientQuoteBalance: big.NewInt(1100), // 500 + 600 + expectedSenderCollateralPoolBalance: big.NewInt(1100), // no changes to collateral pools + expectedRecipientCollateralPoolBalance: big.NewInt(1100), + }, + "Send USDC from isolated subaccount to non-isolated subaccount": { + asset: *constants.Usdc, + quantums: big.NewInt(500), + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(500)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOLong, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneBTCLong, + }, + senderCollateralPoolBalance: big.NewInt(600), + recipientCollateralPoolBalance: big.NewInt(700), + senderCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOLong.PerpetualId), + ), + recipientCollateralPoolAddr: types.ModuleAddress, + expectedRecipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(1100)), + expectedSenderQuoteBalance: big.NewInt(0), // 500 - 500 + expectedRecipientQuoteBalance: big.NewInt(1100), // 500 + 600 + expectedSenderCollateralPoolBalance: big.NewInt(100), // 600 - 500 + expectedRecipientCollateralPoolBalance: big.NewInt(1200), // 700 + 500 + }, + "Send USDC from non-isolated subaccount to isolated subaccount": { + asset: *constants.Usdc, + quantums: big.NewInt(500), + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(500)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneBTCLong, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOLong, + }, + senderCollateralPoolBalance: big.NewInt(600), + recipientCollateralPoolBalance: big.NewInt(700), + senderCollateralPoolAddr: types.ModuleAddress, + recipientCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOLong.PerpetualId), + ), + expectedRecipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(1100)), + expectedSenderQuoteBalance: big.NewInt(0), // 500 - 500 + expectedRecipientQuoteBalance: big.NewInt(1100), // 500 + 600 + expectedSenderCollateralPoolBalance: big.NewInt(100), // 600 - 500 + expectedRecipientCollateralPoolBalance: big.NewInt(1200), // 700 + 500 + }, + "Send USDC from isolated subaccount to isolated subaccount (same perp)": { + asset: *constants.Usdc, + quantums: big.NewInt(500), + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(500)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOLong, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOLong, + }, + senderCollateralPoolBalance: big.NewInt(1100), // 500 + 600 + recipientCollateralPoolBalance: big.NewInt(1100), // same collateral pool, same balance + senderCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOLong.PerpetualId), + ), + recipientCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOLong.PerpetualId), + ), + expectedRecipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(1100)), + expectedSenderQuoteBalance: big.NewInt(0), // 500 - 500 + expectedRecipientQuoteBalance: big.NewInt(1100), // 500 + 600 + expectedSenderCollateralPoolBalance: big.NewInt(1100), // no changes to collateral pools + expectedRecipientCollateralPoolBalance: big.NewInt(1100), + }, + "Send USDC from isolated subaccount to isolated subaccount (different perp)": { + asset: *constants.Usdc, + quantums: big.NewInt(500), + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(500)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOLong, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISO2Long, + }, + senderCollateralPoolBalance: big.NewInt(600), + recipientCollateralPoolBalance: big.NewInt(700), + senderCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOLong.PerpetualId), + ), + recipientCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISO2Long.PerpetualId), + ), + expectedRecipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(1100)), + expectedSenderQuoteBalance: big.NewInt(0), // 500 - 500 + expectedRecipientQuoteBalance: big.NewInt(1100), // 500 + 600 + expectedSenderCollateralPoolBalance: big.NewInt(100), // 600 - 500 + expectedRecipientCollateralPoolBalance: big.NewInt(1200), // 700 + 500 + }, + // TODO(DEC-715): Add more test for non-USDC assets, after asset update + // is implemented. + // TODO(CORE-169): Add tests for when the input quantums is rounded down to + // a integer denom amount. + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + ctx, keeper, pricesKeeper, perpetualsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, _ := + keepertest.SubaccountsKeepers(t, true) + keepertest.CreateTestMarkets(t, ctx, pricesKeeper) + + keepertest.CreateTestLiquidityTiers(t, ctx, perpetualsKeeper) + + keepertest.CreateTestPerpetuals(t, ctx, perpetualsKeeper) + + // Set up Subaccounts module account. + auth_testutil.CreateTestModuleAccount(ctx, accountKeeper, types.ModuleName, []string{}) + + // Set up test account address. + addressStr := sample_testutil.AccAddress() + testAccAddress, err := sdk.AccAddressFromBech32(addressStr) + require.NoError(t, err) + + testAcc := authtypes.NewBaseAccount(testAccAddress, nil, accountKeeper.NextAccountNumber(ctx), 0) + accountKeeper.SetAccount(ctx, testAcc) + + _, err = assetsKeeper.CreateAsset( + ctx, + tc.asset.Id, + tc.asset.Symbol, + tc.asset.Denom, + tc.asset.DenomExponent, + tc.asset.HasMarket, + tc.asset.MarketId, + tc.asset.AtomicResolution, + ) + require.NoError(t, err) + + subaccounts := createNSubaccount(keeper, ctx, 2, big.NewInt(1_000)) + senderSubaccount := subaccounts[0] + recipientSubaccount := subaccounts[1] + senderSubaccount.AssetPositions = tc.senderAssetPositions + senderSubaccount.PerpetualPositions = tc.senderPerpetualPositions + recipientSubaccount.AssetPositions = tc.recipientAssetPositions + recipientSubaccount.PerpetualPositions = tc.recipientPerpetualPositions + + keeper.SetSubaccount(ctx, senderSubaccount) + keeper.SetSubaccount(ctx, recipientSubaccount) + + if tc.senderCollateralPoolBalance.Sign() > 0 { + err := bank_testutil.FundAccount( + ctx, + tc.senderCollateralPoolAddr, + sdk.Coins{ + sdk.NewCoin(tc.asset.Denom, sdkmath.NewIntFromBigInt(tc.senderCollateralPoolBalance)), + }, + *bankKeeper, + ) + require.NoError(t, err) + } + if tc.recipientCollateralPoolBalance.Sign() > 0 && + !tc.recipientCollateralPoolAddr.Equals(tc.senderCollateralPoolAddr) { + err := bank_testutil.FundAccount( + ctx, + tc.recipientCollateralPoolAddr, + sdk.Coins{ + sdk.NewCoin(tc.asset.Denom, sdkmath.NewIntFromBigInt(tc.recipientCollateralPoolBalance)), + }, + *bankKeeper, + ) + require.NoError(t, err) + } + + err = keeper.TransferFundsFromSubaccountToSubaccount( + ctx, + *senderSubaccount.Id, + *recipientSubaccount.Id, + tc.asset.Id, + tc.quantums, + ) + + require.NoError(t, err) + + // Check the subaccount has been updated as expected. + updatedSenderSubaccount := keeper.GetSubaccount(ctx, *senderSubaccount.Id) + if tc.expectedSenderAssetPositions != nil { + require.Equal(t, + tc.expectedSenderAssetPositions, + updatedSenderSubaccount.AssetPositions, + ) + } + require.Equal(t, + tc.expectedSenderQuoteBalance, + updatedSenderSubaccount.GetUsdcPosition(), + ) + + updatedRecipientSubaccount := keeper.GetSubaccount(ctx, *recipientSubaccount.Id) + if tc.expectedRecipientAssetPositions != nil { + require.Equal(t, + tc.expectedRecipientAssetPositions, + updatedRecipientSubaccount.AssetPositions, + ) + } + require.Equal(t, + tc.expectedRecipientQuoteBalance, + updatedRecipientSubaccount.GetUsdcPosition(), + ) + + // Check the subaccount module balance. + senderCollateralPoolAddrBalance := bankKeeper.GetBalance(ctx, tc.senderCollateralPoolAddr, tc.asset.Denom) + require.Equal(t, + sdk.NewCoin(tc.asset.Denom, sdkmath.NewIntFromBigInt(tc.expectedSenderCollateralPoolBalance)), + senderCollateralPoolAddrBalance, + ) + recipientCollateralPoolAddrBalance := bankKeeper.GetBalance(ctx, tc.recipientCollateralPoolAddr, tc.asset.Denom) + require.Equal(t, + sdk.NewCoin(tc.asset.Denom, sdkmath.NewIntFromBigInt(tc.expectedRecipientCollateralPoolBalance)), + recipientCollateralPoolAddrBalance, + ) + }) + } +} + +func TestTransferFundsFromSubaccountToSubaccount_Failure(t *testing.T) { + tests := map[string]struct { + skipSetUpUsdc bool + asset asstypes.Asset + + // Subaccount state. + senderAssetPositions []*types.AssetPosition + senderPerpetualPositions []*types.PerpetualPosition + recipientAssetPositions []*types.AssetPosition + recipientPerpetualPositions []*types.PerpetualPosition + + // Module account state. + senderCollateralPoolBalance *big.Int + recipientCollateralPoolBalance *big.Int + + senderCollateralPoolAddr sdk.AccAddress + recipientCollateralPoolAddr sdk.AccAddress + + // Transfer details + quantums *big.Int + + // Expectations. + expectedErr error + }{ + "Send from non-isolated subaccount to non-isolated subaccount, sender does not have enough balance": { + asset: *constants.Usdc, + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(100)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneBTCShort, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneBTCShort, + }, + senderCollateralPoolBalance: big.NewInt(1100), + recipientCollateralPoolBalance: big.NewInt(1100), + senderCollateralPoolAddr: types.ModuleAddress, + recipientCollateralPoolAddr: types.ModuleAddress, + quantums: big.NewInt(500), + expectedErr: types.ErrFailedToUpdateSubaccounts, + }, + "Send between isolated subaccounts (same perp), sender does not have enough balance": { + asset: *constants.Usdc, + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(100)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOShort, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOShort, + }, + senderCollateralPoolBalance: big.NewInt(1100), + recipientCollateralPoolBalance: big.NewInt(1100), + senderCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOShort.PerpetualId), + ), + recipientCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOShort.PerpetualId), + ), + quantums: big.NewInt(500), + expectedErr: types.ErrFailedToUpdateSubaccounts, + }, + "Send between isolated subaccounts (different perp), sender does not have enough balance": { + asset: *constants.Usdc, + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(100)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOShort, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISO2Short, + }, + senderCollateralPoolBalance: big.NewInt(500), + recipientCollateralPoolBalance: big.NewInt(600), + senderCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOShort.PerpetualId), + ), + recipientCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISO2Short.PerpetualId), + ), + quantums: big.NewInt(500), + expectedErr: types.ErrFailedToUpdateSubaccounts, + }, + "Send from isolated subaccount to non-isolated subaccount, sender does not have enough balance": { + asset: *constants.Usdc, + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(100)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOShort, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneBTCShort, + }, + senderCollateralPoolBalance: big.NewInt(500), + recipientCollateralPoolBalance: big.NewInt(600), + senderCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOShort.PerpetualId), + ), + recipientCollateralPoolAddr: types.ModuleAddress, + quantums: big.NewInt(500), + expectedErr: types.ErrFailedToUpdateSubaccounts, + }, + "Send from non-isolated subaccount to isolated subaccount, collateral pool does not have enough balance": { + asset: *constants.Usdc, + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(500)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneBTCLong, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOLong, + }, + senderCollateralPoolBalance: big.NewInt(100), + recipientCollateralPoolBalance: big.NewInt(600), + senderCollateralPoolAddr: types.ModuleAddress, + recipientCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOLong.PerpetualId), + ), + quantums: big.NewInt(500), + expectedErr: sdkerrors.ErrInsufficientFunds, + }, + "Send from isolated subaccount to non-isolated subaccount, collateral pool does not have enough balance": { + asset: *constants.Usdc, + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(500)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOLong, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneBTCLong, + }, + senderCollateralPoolBalance: big.NewInt(100), + recipientCollateralPoolBalance: big.NewInt(600), + senderCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOLong.PerpetualId), + ), + recipientCollateralPoolAddr: types.ModuleAddress, + quantums: big.NewInt(500), + expectedErr: sdkerrors.ErrInsufficientFunds, + }, + "Send between isolated subaccounts (different perp), collateral pool does not have enough balance": { + asset: *constants.Usdc, + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(500)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOLong, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISO2Long, + }, + senderCollateralPoolBalance: big.NewInt(100), + recipientCollateralPoolBalance: big.NewInt(600), + senderCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOLong.PerpetualId), + ), + recipientCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISO2Long.PerpetualId), + ), + quantums: big.NewInt(500), + expectedErr: sdkerrors.ErrInsufficientFunds, + }, + "Do not support assets other than USDC": { + asset: *constants.BtcUsd, + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(500)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOLong, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISO2Long, + }, + senderCollateralPoolBalance: big.NewInt(100), + recipientCollateralPoolBalance: big.NewInt(600), + senderCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOLong.PerpetualId), + ), + recipientCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISO2Long.PerpetualId), + ), + quantums: big.NewInt(500), + expectedErr: types.ErrAssetTransferThroughBankNotImplemented, + }, + "Asset ID doesn't exist": { + skipSetUpUsdc: true, + asset: *constants.Usdc, + senderAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(500)), + senderPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISOLong, + }, + recipientAssetPositions: keepertest.CreateUsdcAssetPosition(big.NewInt(600)), + recipientPerpetualPositions: []*types.PerpetualPosition{ + &constants.PerpetualPosition_OneISO2Long, + }, + senderCollateralPoolBalance: big.NewInt(100), + recipientCollateralPoolBalance: big.NewInt(600), + senderCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISOLong.PerpetualId), + ), + recipientCollateralPoolAddr: authtypes.NewModuleAddress( + types.ModuleName + ":" + lib.UintToString(constants.PerpetualPosition_OneISO2Long.PerpetualId), + ), + quantums: big.NewInt(500), + expectedErr: asstypes.ErrAssetDoesNotExist, + }, + // TODO(DEC-715): Add more test for non-USDC assets, after asset update + // is implemented. + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + ctx, keeper, pricesKeeper, perpetualsKeeper, accountKeeper, bankKeeper, assetsKeeper, _, _ := + keepertest.SubaccountsKeepers(t, true) + keepertest.CreateTestMarkets(t, ctx, pricesKeeper) + + keepertest.CreateTestLiquidityTiers(t, ctx, perpetualsKeeper) + + keepertest.CreateTestPerpetuals(t, ctx, perpetualsKeeper) + + // Set up Subaccounts module account. + auth_testutil.CreateTestModuleAccount(ctx, accountKeeper, types.ModuleName, []string{}) + + // Set up test account address. + addressStr := sample_testutil.AccAddress() + testAccAddress, err := sdk.AccAddressFromBech32(addressStr) + require.NoError(t, err) + + testAcc := authtypes.NewBaseAccount(testAccAddress, nil, accountKeeper.NextAccountNumber(ctx), 0) + accountKeeper.SetAccount(ctx, testAcc) + + if !tc.skipSetUpUsdc { + // Always create USDC as the first asset unless specificed to skip. + err := keepertest.CreateUsdcAsset(ctx, assetsKeeper) + require.NoError(t, err) + } + + if tc.asset.Denom != constants.Usdc.Denom { + _, err := assetsKeeper.CreateAsset( + ctx, + tc.asset.Id, + tc.asset.Symbol, + tc.asset.Denom, + tc.asset.DenomExponent, + tc.asset.HasMarket, + tc.asset.MarketId, + tc.asset.AtomicResolution, + ) + require.NoError(t, err) + } + + subaccounts := createNSubaccount(keeper, ctx, 2, big.NewInt(1_000)) + senderSubaccount := subaccounts[0] + recipientSubaccount := subaccounts[1] + senderSubaccount.AssetPositions = tc.senderAssetPositions + senderSubaccount.PerpetualPositions = tc.senderPerpetualPositions + recipientSubaccount.AssetPositions = tc.recipientAssetPositions + recipientSubaccount.PerpetualPositions = tc.recipientPerpetualPositions + + keeper.SetSubaccount(ctx, senderSubaccount) + keeper.SetSubaccount(ctx, recipientSubaccount) + + if tc.senderCollateralPoolBalance.Sign() > 0 { + err := bank_testutil.FundAccount( + ctx, + tc.senderCollateralPoolAddr, + sdk.Coins{ + sdk.NewCoin(tc.asset.Denom, sdkmath.NewIntFromBigInt(tc.senderCollateralPoolBalance)), + }, + *bankKeeper, + ) + require.NoError(t, err) + } + if tc.recipientCollateralPoolBalance.Sign() > 0 && + !tc.recipientCollateralPoolAddr.Equals(tc.senderCollateralPoolAddr) { + err := bank_testutil.FundAccount( + ctx, + tc.recipientCollateralPoolAddr, + sdk.Coins{ + sdk.NewCoin(tc.asset.Denom, sdkmath.NewIntFromBigInt(tc.recipientCollateralPoolBalance)), + }, + *bankKeeper, + ) + require.NoError(t, err) + } + + err = keeper.TransferFundsFromSubaccountToSubaccount( + ctx, + *senderSubaccount.Id, + *recipientSubaccount.Id, + tc.asset.Id, + tc.quantums, + ) + + require.ErrorIs(t, + err, + tc.expectedErr, + ) + + // Check the subaccount has been updated as expected. + updatedSenderSubaccount := keeper.GetSubaccount(ctx, *senderSubaccount.Id) + require.Equal(t, + tc.senderAssetPositions, + updatedSenderSubaccount.AssetPositions, + ) + + updatedRecipientSubaccount := keeper.GetSubaccount(ctx, *recipientSubaccount.Id) + require.Equal(t, + tc.recipientAssetPositions, + updatedRecipientSubaccount.AssetPositions, + ) + + // Check the subaccount module balance. + senderCollateralPoolAddrBalance := bankKeeper.GetBalance(ctx, tc.senderCollateralPoolAddr, tc.asset.Denom) + require.Equal(t, + sdk.NewCoin(tc.asset.Denom, sdkmath.NewIntFromBigInt(tc.senderCollateralPoolBalance)), + senderCollateralPoolAddrBalance, + ) + recipientCollateralPoolAddrBalance := bankKeeper.GetBalance(ctx, tc.recipientCollateralPoolAddr, tc.asset.Denom) + require.Equal(t, + sdk.NewCoin(tc.asset.Denom, sdkmath.NewIntFromBigInt(tc.recipientCollateralPoolBalance)), + recipientCollateralPoolAddrBalance, + ) + }) + } +} + func TestTransferFeesToFeeCollectorModule(t *testing.T) { tests := map[string]struct { skipSetUpUsdc bool diff --git a/protocol/x/subaccounts/types/types.go b/protocol/x/subaccounts/types/types.go index d4d68aa860..bb8a98ac62 100644 --- a/protocol/x/subaccounts/types/types.go +++ b/protocol/x/subaccounts/types/types.go @@ -51,6 +51,13 @@ type SubaccountsKeeper interface { assetId uint32, amount *big.Int, ) (err error) + TransferFundsFromSubaccountToSubaccount( + ctx sdk.Context, + senderSubaccountId SubaccountId, + recipientSubaccountId SubaccountId, + assetId uint32, + quantums *big.Int, + ) (err error) SetSubaccount(ctx sdk.Context, subaccount Subaccount) GetSubaccount( ctx sdk.Context, diff --git a/protocol/x/subaccounts/types/update.go b/protocol/x/subaccounts/types/update.go index d700bb1541..684b618b28 100644 --- a/protocol/x/subaccounts/types/update.go +++ b/protocol/x/subaccounts/types/update.go @@ -52,11 +52,12 @@ func GetErrorFromUpdateResults( } var updateResultStringMap = map[UpdateResult]string{ - Success: "Success", - NewlyUndercollateralized: "NewlyUndercollateralized", - StillUndercollateralized: "StillUndercollateralized", - WithdrawalsAndTransfersBlocked: "WithdrawalsAndTransfersBlocked", - UpdateCausedError: "UpdateCausedError", + Success: "Success", + NewlyUndercollateralized: "NewlyUndercollateralized", + StillUndercollateralized: "StillUndercollateralized", + WithdrawalsAndTransfersBlocked: "WithdrawalsAndTransfersBlocked", + UpdateCausedError: "UpdateCausedError", + ViolatesIsolatedSubaccountConstraints: "ViolatesIsolatedSubaccountConstraints", } const ( @@ -65,6 +66,7 @@ const ( StillUndercollateralized WithdrawalsAndTransfersBlocked UpdateCausedError + ViolatesIsolatedSubaccountConstraints ) // Update is used by the subaccounts keeper to allow other modules diff --git a/protocol/x/subaccounts/types/update_test.go b/protocol/x/subaccounts/types/update_test.go index 153241de08..c4cd9c5e74 100644 --- a/protocol/x/subaccounts/types/update_test.go +++ b/protocol/x/subaccounts/types/update_test.go @@ -88,8 +88,12 @@ func TestUpdateResultString(t *testing.T) { value: types.UpdateCausedError, expectedResult: "UpdateCausedError", }, + "ViolatesIsolatedSubaccountConstraints": { + value: types.ViolatesIsolatedSubaccountConstraints, + expectedResult: "ViolatesIsolatedSubaccountConstraints", + }, "UnexpectedError": { - value: types.UpdateResult(5), + value: types.UpdateResult(6), expectedResult: "UnexpectedError", }, } diff --git a/protocol/x/vault/abci.go b/protocol/x/vault/abci.go new file mode 100644 index 0000000000..7b74f6b859 --- /dev/null +++ b/protocol/x/vault/abci.go @@ -0,0 +1,13 @@ +package vault + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dydxprotocol/v4-chain/protocol/x/vault/keeper" +) + +func EndBlocker( + ctx sdk.Context, + keeper *keeper.Keeper, +) { + keeper.RefreshAllVaultOrders(ctx) +} diff --git a/protocol/x/vault/keeper/keeper.go b/protocol/x/vault/keeper/keeper.go index 319158f723..9fe75f116f 100644 --- a/protocol/x/vault/keeper/keeper.go +++ b/protocol/x/vault/keeper/keeper.go @@ -13,21 +13,33 @@ import ( type ( Keeper struct { - cdc codec.BinaryCodec - storeKey storetypes.StoreKey - authorities map[string]struct{} + cdc codec.BinaryCodec + storeKey storetypes.StoreKey + clobKeeper types.ClobKeeper + perpetualsKeeper types.PerpetualsKeeper + pricesKeeper types.PricesKeeper + subaccountsKeeper types.SubaccountsKeeper + authorities map[string]struct{} } ) func NewKeeper( cdc codec.BinaryCodec, storeKey storetypes.StoreKey, + clobKeeper types.ClobKeeper, + perpetualsKeeper types.PerpetualsKeeper, + pricesKeeper types.PricesKeeper, + subaccountsKeeper types.SubaccountsKeeper, authorities []string, ) *Keeper { return &Keeper{ - cdc: cdc, - storeKey: storeKey, - authorities: lib.UniqueSliceToSet(authorities), + cdc: cdc, + storeKey: storeKey, + clobKeeper: clobKeeper, + perpetualsKeeper: perpetualsKeeper, + pricesKeeper: pricesKeeper, + subaccountsKeeper: subaccountsKeeper, + authorities: lib.UniqueSliceToSet(authorities), } } diff --git a/protocol/x/vault/keeper/msg_server_deposit_to_vault.go b/protocol/x/vault/keeper/msg_server_deposit_to_vault.go new file mode 100644 index 0000000000..f462f19f39 --- /dev/null +++ b/protocol/x/vault/keeper/msg_server_deposit_to_vault.go @@ -0,0 +1,15 @@ +package keeper + +import ( + "context" + + "github.com/dydxprotocol/v4-chain/protocol/x/vault/types" +) + +// DepositToVault deposits from a subaccount to a vault. +func (k msgServer) DepositToVault( + goCtx context.Context, + msg *types.MsgDepositToVault, +) (*types.MsgDepositToVaultResponse, error) { + return &types.MsgDepositToVaultResponse{}, nil +} diff --git a/protocol/x/vault/keeper/orders.go b/protocol/x/vault/keeper/orders.go new file mode 100644 index 0000000000..3c58940d7d --- /dev/null +++ b/protocol/x/vault/keeper/orders.go @@ -0,0 +1,178 @@ +package keeper + +import ( + "fmt" + "math/big" + + errorsmod "cosmossdk.io/errors" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dydxprotocol/v4-chain/protocol/lib" + clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" + "github.com/dydxprotocol/v4-chain/protocol/x/vault/types" +) + +// TODO (TRA-118): store vault strategy constants in x/vault state. +const ( + // Determines how many layers of orders a vault places. + // E.g. if num_levels=2, a vault places 2 asks and 2 bids. + NUM_LAYERS = uint8(2) + // Determines minimum base spread when a vault quotes around reservation price. + MIN_BASE_SPREAD_PPM = uint32(3_000) // 30bps + // Determines the amount to add to min_price_change_ppm to arrive at base spread. + BASE_SPREAD_MIN_PRICE_CHANGE_PREMIUM_PPM = uint32(1_500) // 15bps + // Determines how aggressive a vault skews its orders. + SKEW_FACTOR_PPM = uint32(500_000) // 0.5 + // Determines the percentage of vault equity that each order is sized at. + ORDER_SIZE_PCT_PPM = uint32(100_000) // 10% + // Determines how long a vault's orders are valid for. + ORDER_EXPIRATION_SECONDS = uint32(5) // 5 seconds +) + +// RefreshAllVaultOrders refreshes all orders for all vaults by +// TODO(TRA-134) +// 1. Cancelling all existing orders. +// 2. Placing new orders. +func (k Keeper) RefreshAllVaultOrders(ctx sdk.Context) { +} + +// GetVaultClobOrders returns a list of long term orders for a given vault, with its corresponding +// clob pair, perpetual, market parameter, and market price. +// Let n be number of layers, then the function returns orders at [a_1, b_1, a_2, b_2, ..., a_n, b_n] +// where a_i and b_i are the ask price and bid price at i-th layer. To compute a_i and b_i: +// - a_i = oraclePrice * (1 + spread)^i +// - b_i = oraclePrice * (1 - spread)^i +// TODO (TRA-144): Implement order size +// TODO (TRA-114): Implement skew +func (k Keeper) GetVaultClobOrders( + ctx sdk.Context, + vaultId types.VaultId, +) (orders []*clobtypes.Order, err error) { + // Get clob pair, perpetual, market parameter, and market price that correspond to this vault. + clobPair, exists := k.clobKeeper.GetClobPair(ctx, clobtypes.ClobPairId(vaultId.Number)) + if !exists { + return orders, errorsmod.Wrap( + types.ErrClobPairNotFound, + fmt.Sprintf("VaultId: %v", vaultId), + ) + } + perpId := clobPair.Metadata.(*clobtypes.ClobPair_PerpetualClobMetadata).PerpetualClobMetadata.PerpetualId + perpetual, err := k.perpetualsKeeper.GetPerpetual(ctx, perpId) + if err != nil { + return orders, errorsmod.Wrap( + err, + fmt.Sprintf("VaultId: %v", vaultId), + ) + } + marketParam, exists := k.pricesKeeper.GetMarketParam(ctx, perpetual.Params.MarketId) + if !exists { + return orders, errorsmod.Wrap( + types.ErrMarketParamNotFound, + fmt.Sprintf("VaultId: %v", vaultId), + ) + } + marketPrice, err := k.pricesKeeper.GetMarketPrice(ctx, perpetual.Params.MarketId) + if err != nil { + return orders, errorsmod.Wrap( + err, + fmt.Sprintf("VaultId: %v", vaultId), + ) + } + + // Get vault (subaccount 0 of corresponding module account). + vault := satypes.SubaccountId{ + Owner: vaultId.ToModuleAccountAddress(), + Number: 0, + } + // Calculate spread. + spreadPpm := lib.Max( + MIN_BASE_SPREAD_PPM, + BASE_SPREAD_MIN_PRICE_CHANGE_PREMIUM_PPM+marketParam.MinPriceChangePpm, + ) + // Get market price in subticks. + subticks := clobtypes.PriceToSubticks( + marketPrice, + clobPair, + perpetual.Params.AtomicResolution, + lib.QuoteCurrencyAtomicResolution, + ) + // Get order expiration time. + goodTilBlockTime := &clobtypes.Order_GoodTilBlockTime{ + GoodTilBlockTime: uint32(ctx.BlockTime().Unix()) + ORDER_EXPIRATION_SECONDS, + } + // Construct one ask and one bid for each layer. + orders = make([]*clobtypes.Order, 2*NUM_LAYERS) + askSubticks := new(big.Rat).Set(subticks) + bidSubticks := new(big.Rat).Set(subticks) + for i := uint8(0); i < NUM_LAYERS; i++ { + // Calculate ask and bid subticks for this layer. + askSubticks = lib.BigRatMulPpm(askSubticks, lib.OneMillion+spreadPpm) + bidSubticks = lib.BigRatMulPpm(bidSubticks, lib.OneMillion-spreadPpm) + + // Construct ask at this layer. + ask := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: vault, + ClientId: k.GetVaultClobOrderClientId(ctx, clobtypes.Order_SIDE_SELL, uint8(i+1)), + OrderFlags: clobtypes.OrderIdFlags_LongTerm, + ClobPairId: clobPair.Id, + }, + Side: clobtypes.Order_SIDE_SELL, + Quantums: clobPair.StepBaseQuantums, // TODO (TRA-144): Implement order size + Subticks: lib.BigRatRoundToNearestMultiple( + askSubticks, + clobPair.SubticksPerTick, + true, // round up for asks + ), + GoodTilOneof: goodTilBlockTime, + } + + // Construct bid at this layer. + bid := clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: vault, + ClientId: k.GetVaultClobOrderClientId(ctx, clobtypes.Order_SIDE_BUY, uint8(i+1)), + OrderFlags: clobtypes.OrderIdFlags_LongTerm, + ClobPairId: clobPair.Id, + }, + Side: clobtypes.Order_SIDE_BUY, + Quantums: clobPair.StepBaseQuantums, // TODO (TRA-144): Implement order size + Subticks: lib.BigRatRoundToNearestMultiple( + bidSubticks, + clobPair.SubticksPerTick, + false, // round down for bids + ), + GoodTilOneof: goodTilBlockTime, + } + + orders[2*i] = &ask + orders[2*i+1] = &bid + } + + return orders, nil +} + +// GetVaultClobOrderClientId returns the client ID for a CLOB order where +// - 1st bit is `side-1` (subtract 1 as buy_side = 1, sell_side = 2) +// +// - 2nd bit is `block height % 2` +// - block height bit alternates between 0 and 1 to ensure that client IDs +// are different in two consecutive blocks (otherwise, order placement would +// fail because the same order IDs are already marked for cancellation) +// +// - next 8 bits are `layer` +func (k Keeper) GetVaultClobOrderClientId( + ctx sdk.Context, + side clobtypes.Order_Side, + layer uint8, +) uint32 { + sideBit := uint32(side - 1) + sideBit <<= 31 + + blockHeightBit := uint32(ctx.BlockHeight() % 2) + blockHeightBit <<= 30 + + layerBits := uint32(layer) << 22 + + return sideBit | blockHeightBit | layerBits +} diff --git a/protocol/x/vault/keeper/orders_test.go b/protocol/x/vault/keeper/orders_test.go new file mode 100644 index 0000000000..f47a04c16d --- /dev/null +++ b/protocol/x/vault/keeper/orders_test.go @@ -0,0 +1,296 @@ +package keeper_test + +import ( + "math" + "testing" + + "github.com/cometbft/cometbft/types" + testapp "github.com/dydxprotocol/v4-chain/protocol/testutil/app" + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" + clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" + satypes "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" + vaulttypes "github.com/dydxprotocol/v4-chain/protocol/x/vault/types" + "github.com/stretchr/testify/require" +) + +// TODO (TRA-118): store vault strategy constants in x/vault state. +const ( + numLayers = uint8(2) + minBaseSpreadPpm = uint32(3_000) // 30 bps + baseSpreadMinPriceChangePremiumPpm = uint32(1_500) // 15 bps + orderExpirationSeconds = uint32(5) // 5 seconds +) + +func TestGetVaultClobOrders(t *testing.T) { + tests := map[string]struct { + /* --- Setup --- */ + // Vault ID. + vaultId vaulttypes.VaultId + // Clob pair. + clobPair clobtypes.ClobPair + // Market param. + marketParam pricestypes.MarketParam + // Market price. + marketPrice pricestypes.MarketPrice + // Perpetual. + perpetual perptypes.Perpetual + + /* --- Expectations --- */ + expectedOrderSubticks []uint64 + expectedOrderQuantums []uint64 + expectedErr error + }{ + "Success - Get orders from Vault for Clob Pair 0": { + vaultId: constants.Vault_Clob_0, + clobPair: constants.ClobPair_Btc, + marketParam: constants.TestMarketParams[0], + marketPrice: constants.TestMarketPrices[0], + perpetual: constants.BtcUsd_0DefaultFunding_10AtomicResolution, + // To calculate order subticks: + // 1. spreadPpm = max(minBaseSpreadPpm, baseSpreadMinPriceChangePremiumPpm + minPriceChangePpm) + // 2. priceSubticks = marketPrice.Price * 10^(marketPrice.Exponent - quantumConversionExponent + + // baseAtomicResolution - quoteAtomicResolution) + // 3. askSubticks at layer i = priceSubticks * (1 + spread)^i + // bidSubticks at layer i = priceSubticks * (1 - spread)^i + // 4. subticks needs to be a multiple of subtickPerTick (round up for asks, round down for bids) + expectedOrderSubticks: []uint64{ + // spreadPpm = max(3_000, 1_500 + 50) = 3_000 + // priceSubticks = 5_000_000_000 * 10^(-5 - (-8) + (-10) - (-6)) = 5 * 10^8 + // a_1 = 5 * 10^8 * (1 + 0.003)^1 = 501_500_000 + 501_500_000, + // b_1 = 5 * 10^8 * (1 - 0.003)^1 = 498_500_000 + 498_500_000, + // a_2 = 5 * 10^8 * (1 + 0.003)^2 = 503_004_500 + 503_004_500, + // b_2 = 5 * 10^8 * (1 - 0.003)^2 = 497_004_500 + 497_004_500, + }, + expectedOrderQuantums: []uint64{ // TODO (TRA-144): Implement order size + 5, + 5, + 5, + 5, + }, + }, + "Success - Get orders from Vault for Clob Pair 1": { + vaultId: constants.Vault_Clob_1, + clobPair: constants.ClobPair_Eth, + marketParam: pricestypes.MarketParam{ + Id: constants.TestMarketParams[1].Id, + Pair: constants.TestMarketParams[1].Pair, + Exponent: constants.TestMarketParams[1].Exponent, + MinExchanges: constants.TestMarketParams[1].MinExchanges, + MinPriceChangePpm: 4_200, // Set a high min price change to test spread calculation. + ExchangeConfigJson: constants.TestMarketParams[1].ExchangeConfigJson, + }, + marketPrice: constants.TestMarketPrices[1], + perpetual: constants.EthUsd_20PercentInitial_10PercentMaintenance, + expectedOrderSubticks: []uint64{ + // spreadPpm = max(3_000, 1_500 + 4_200) = 5_700 + // priceSubticks = 3_000_000_000 * 10^(-6 - (-9) + (-9) - (-6)) = 3 * 10^9 + // a_1 = 3 * 10^9 * (1 + 0.0057)^1 = 3_017_100_000 + 3_017_100_000, + // b_1 = 3 * 10^9 * (1 - 0.0057)^1 = 2_982_900_000 + 2_982_900_000, + // a_2 = 3 * 10^9 * (1 + 0.0057)^2 = 3_034_297_470 + // round up to nearest multiple of subticksPerTick=1000. + 3_034_298_000, + // b_2 = 3 * 10^9 * (1 - 0.0057)^2 = 2_965_897_470 + // round down to nearest multiple of subticksPerTick=1000. + 2_965_897_000, + }, + expectedOrderQuantums: []uint64{ // TODO (TRA-144): Implement order size + 1000, + 1000, + 1000, + 1000, + }, + }, + "Error - Clob Pair doesn't exist": { + vaultId: constants.Vault_Clob_0, + clobPair: constants.ClobPair_Eth, + marketParam: constants.TestMarketParams[1], + marketPrice: constants.TestMarketPrices[1], + perpetual: constants.EthUsd_NoMarginRequirement, + expectedErr: vaulttypes.ErrClobPairNotFound, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + // Initialize tApp and ctx. + tApp := testapp.NewTestAppBuilder(t).WithGenesisDocFn(func() (genesis types.GenesisDoc) { + genesis = testapp.DefaultGenesis() + // Initialize prices module with test market param and market price. + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *pricestypes.GenesisState) { + genesisState.MarketParams = []pricestypes.MarketParam{tc.marketParam} + genesisState.MarketPrices = []pricestypes.MarketPrice{tc.marketPrice} + }, + ) + // Initialize perpetuals module with test perpetual. + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *perptypes.GenesisState) { + genesisState.LiquidityTiers = constants.LiquidityTiers + genesisState.Perpetuals = []perptypes.Perpetual{tc.perpetual} + }, + ) + // Initialize clob module with test clob pair. + testapp.UpdateGenesisDocWithAppStateForModule( + &genesis, + func(genesisState *clobtypes.GenesisState) { + genesisState.ClobPairs = []clobtypes.ClobPair{tc.clobPair} + }, + ) + return genesis + }).Build() + ctx := tApp.InitChain() + + // Get vault orders. + orders, err := tApp.App.VaultKeeper.GetVaultClobOrders(ctx, tc.vaultId) + if tc.expectedErr != nil { + require.ErrorContains(t, err, tc.expectedErr.Error()) + return + } + require.NoError(t, err) + + // Get expected orders. + buildVaultClobOrder := func( + layer uint8, + side clobtypes.Order_Side, + quantums uint64, + subticks uint64, + ) *clobtypes.Order { + return &clobtypes.Order{ + OrderId: clobtypes.OrderId{ + SubaccountId: satypes.SubaccountId{ + Owner: tc.vaultId.ToModuleAccountAddress(), + Number: 0, + }, + ClientId: tApp.App.VaultKeeper.GetVaultClobOrderClientId(ctx, side, layer), + OrderFlags: clobtypes.OrderIdFlags_LongTerm, + ClobPairId: tc.vaultId.Number, + }, + Side: side, + Quantums: quantums, + Subticks: subticks, + GoodTilOneof: &clobtypes.Order_GoodTilBlockTime{ + GoodTilBlockTime: uint32(ctx.BlockTime().Unix()) + orderExpirationSeconds, + }, + } + } + expectedOrders := make([]*clobtypes.Order, 0) + for i := uint8(0); i < numLayers; i++ { + expectedOrders = append( + expectedOrders, + // ask. + buildVaultClobOrder( + i+1, + clobtypes.Order_SIDE_SELL, + tc.expectedOrderQuantums[2*i], + tc.expectedOrderSubticks[2*i], + ), + // bid. + buildVaultClobOrder( + i+1, + clobtypes.Order_SIDE_BUY, + tc.expectedOrderQuantums[2*i+1], + tc.expectedOrderSubticks[2*i+1], + ), + ) + } + + // Compare expected orders with actual orders. + require.Equal( + t, + expectedOrders, + orders, + ) + }) + } +} + +func TestGetVaultClobOrderClientId(t *testing.T) { + tests := map[string]struct { + /* --- Setup --- */ + // side. + side clobtypes.Order_Side + // block height. + blockHeight int64 + // layer. + layer uint8 + + /* --- Expectations --- */ + // Expected client ID. + expectedClientId uint32 + }{ + "Buy, Block Height Odd, Layer 1": { + side: clobtypes.Order_SIDE_BUY, // 0<<31 + blockHeight: 1, // 1<<30 + layer: 1, // 1<<22 + expectedClientId: 0<<31 | 1<<30 | 1<<22, + }, + "Buy, Block Height Even, Layer 1": { + side: clobtypes.Order_SIDE_BUY, // 0<<31 + blockHeight: 2, // 0<<30 + layer: 1, // 1<<22 + expectedClientId: 0<<31 | 0<<30 | 1<<22, + }, + "Sell, Block Height Odd, Layer 2": { + side: clobtypes.Order_SIDE_SELL, // 1<<31 + blockHeight: 1, // 1<<30 + layer: 2, // 2<<22 + expectedClientId: 1<<31 | 1<<30 | 2<<22, + }, + "Sell, Block Height Even, Layer 2": { + side: clobtypes.Order_SIDE_SELL, // 1<<31 + blockHeight: 2, // 0<<30 + layer: 2, // 2<<22 + expectedClientId: 1<<31 | 0<<30 | 2<<22, + }, + "Buy, Block Height Even, Layer Max Uint8": { + side: clobtypes.Order_SIDE_BUY, // 0<<31 + blockHeight: 123456, // 0<<30 + layer: math.MaxUint8, // 255<<22 + expectedClientId: 0<<31 | 0<<30 | 255<<22, + }, + "Sell, Block Height Odd, Layer 0": { + side: clobtypes.Order_SIDE_SELL, // 1<<31 + blockHeight: 12345654321, // 1<<30 + layer: 0, // 0<<22 + expectedClientId: 1<<31 | 1<<30 | 0<<22, + }, + "Sell, Block Height Odd (negative), Layer 202": { + side: clobtypes.Order_SIDE_SELL, // 1<<31 + // Negative block height shouldn't happen but blockHeight + // is represented as int64. + blockHeight: -678987, // 1<<30 + layer: 202, // 202<<22 + expectedClientId: 1<<31 | 1<<30 | 202<<22, + }, + "Buy, Block Height Even (zero), Layer 157": { + side: clobtypes.Order_SIDE_SELL, // 1<<31 + blockHeight: 0, // 0<<30 + layer: 157, // 157<<22 + expectedClientId: 1<<31 | 0<<30 | 157<<22, + }, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + ctx := tApp.InitChain() + + clientId := tApp.App.VaultKeeper.GetVaultClobOrderClientId( + ctx.WithBlockHeight(tc.blockHeight), + tc.side, + tc.layer, + ) + require.Equal(t, tc.expectedClientId, clientId) + }) + } +} diff --git a/protocol/x/vault/keeper/shares.go b/protocol/x/vault/keeper/shares.go new file mode 100644 index 0000000000..467af6cf24 --- /dev/null +++ b/protocol/x/vault/keeper/shares.go @@ -0,0 +1,41 @@ +package keeper + +import ( + "cosmossdk.io/store/prefix" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/dydxprotocol/v4-chain/protocol/dtypes" + "github.com/dydxprotocol/v4-chain/protocol/x/vault/types" +) + +// GetTotalShares gets TotalShares for a vault. +func (k Keeper) GetTotalShares( + ctx sdk.Context, + vaultId types.VaultId, +) (val types.NumShares, exists bool) { + store := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.TotalSharesKeyPrefix)) + + b := store.Get(vaultId.ToStateKey()) + if b == nil { + return val, false + } + + k.cdc.MustUnmarshal(b, &val) + return val, true +} + +// SetTotalShares sets TotalShares for a vault. Returns error if `totalShares` is negative. +func (k Keeper) SetTotalShares( + ctx sdk.Context, + vaultId types.VaultId, + totalShares types.NumShares, +) error { + if totalShares.NumShares.Cmp(dtypes.NewInt(0)) == -1 { + return types.ErrNegativeShares + } + + b := k.cdc.MustMarshal(&totalShares) + totalSharesStore := prefix.NewStore(ctx.KVStore(k.storeKey), []byte(types.TotalSharesKeyPrefix)) + totalSharesStore.Set(vaultId.ToStateKey(), b) + + return nil +} diff --git a/protocol/x/vault/keeper/shares_test.go b/protocol/x/vault/keeper/shares_test.go new file mode 100644 index 0000000000..06454b0b80 --- /dev/null +++ b/protocol/x/vault/keeper/shares_test.go @@ -0,0 +1,67 @@ +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" + "github.com/dydxprotocol/v4-chain/protocol/x/vault/types" + "github.com/stretchr/testify/require" +) + +func TestGetSetTotalShares(t *testing.T) { + tApp := testapp.NewTestAppBuilder(t).Build() + ctx := tApp.InitChain() + k := tApp.App.VaultKeeper + + // Get total shares for a non-existing vault. + _, exists := k.GetTotalShares(ctx, constants.Vault_Clob_0) + require.Equal(t, false, exists) + + // Set total shares for a vault and then get. + err := k.SetTotalShares(ctx, constants.Vault_Clob_0, types.NumShares{ + NumShares: dtypes.NewInt(7), + }) + require.NoError(t, err) + numShares, exists := k.GetTotalShares(ctx, constants.Vault_Clob_0) + require.Equal(t, true, exists) + require.Equal(t, dtypes.NewInt(7), numShares.NumShares) + + // Set total shares for another vault and then get. + err = k.SetTotalShares(ctx, constants.Vault_Clob_1, types.NumShares{ + NumShares: dtypes.NewInt(456), + }) + require.NoError(t, err) + numShares, exists = k.GetTotalShares(ctx, constants.Vault_Clob_1) + require.Equal(t, true, exists) + require.Equal(t, dtypes.NewInt(456), numShares.NumShares) + + // Set total shares for second vault to 0. + err = k.SetTotalShares(ctx, constants.Vault_Clob_1, types.NumShares{ + NumShares: dtypes.NewInt(0), + }) + require.NoError(t, err) + numShares, exists = k.GetTotalShares(ctx, constants.Vault_Clob_1) + require.Equal(t, true, exists) + require.Equal(t, dtypes.NewInt(0), numShares.NumShares) + + // Set total shares for the first vault again and then get. + err = k.SetTotalShares(ctx, constants.Vault_Clob_0, types.NumShares{ + NumShares: dtypes.NewInt(123), + }) + require.NoError(t, err) + numShares, exists = k.GetTotalShares(ctx, constants.Vault_Clob_0) + require.Equal(t, true, exists) + require.Equal(t, dtypes.NewInt(123), numShares.NumShares) + + // Set total shares for the first vault to a negative value. + // Should get error and total shares should remain unchanged. + err = k.SetTotalShares(ctx, constants.Vault_Clob_0, types.NumShares{ + NumShares: dtypes.NewInt(-1), + }) + require.Equal(t, types.ErrNegativeShares, err) + numShares, exists = k.GetTotalShares(ctx, constants.Vault_Clob_0) + require.Equal(t, true, exists) + require.Equal(t, dtypes.NewInt(123), numShares.NumShares) +} diff --git a/protocol/x/vault/module.go b/protocol/x/vault/module.go index bf1c9e1d1b..c77ad88043 100644 --- a/protocol/x/vault/module.go +++ b/protocol/x/vault/module.go @@ -1,8 +1,10 @@ package vault import ( + "context" "encoding/json" "fmt" + "time" "cosmossdk.io/core/appmodule" @@ -12,8 +14,10 @@ import ( "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/codec" cdctypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/module" + "github.com/dydxprotocol/v4-chain/protocol/lib" "github.com/dydxprotocol/v4-chain/protocol/x/vault/client/cli" "github.com/dydxprotocol/v4-chain/protocol/x/vault/keeper" "github.com/dydxprotocol/v4-chain/protocol/x/vault/types" @@ -24,6 +28,7 @@ var ( _ module.HasGenesisBasics = AppModuleBasic{} _ appmodule.AppModule = AppModule{} + _ appmodule.HasEndBlocker = AppModule{} _ module.HasConsensusVersion = AppModule{} _ module.HasGenesis = AppModule{} _ module.HasServices = AppModule{} @@ -141,3 +146,13 @@ func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.Raw // consensus-breaking change introduced by the module. To avoid wrong/empty versions, the initial version should // be set to 1. func (AppModule) ConsensusVersion() uint64 { return 1 } + +// EndBlock executes all ABCI EndBlock logic respective to the vault module. +func (am AppModule) EndBlock(ctx context.Context) error { + defer telemetry.ModuleMeasureSince(am.Name(), time.Now(), telemetry.MetricKeyEndBlocker) + EndBlocker( + lib.UnwrapSDKContext(ctx, types.ModuleName), + &am.keeper, + ) + return nil +} diff --git a/protocol/x/vault/types/errors.go b/protocol/x/vault/types/errors.go new file mode 100644 index 0000000000..fdd1c6fd4b --- /dev/null +++ b/protocol/x/vault/types/errors.go @@ -0,0 +1,23 @@ +package types + +// DONTCOVER + +import errorsmod "cosmossdk.io/errors" + +var ( + ErrNegativeShares = errorsmod.Register( + ModuleName, + 1, + "Shares are negative", + ) + ErrClobPairNotFound = errorsmod.Register( + ModuleName, + 2, + "ClobPair not found", + ) + ErrMarketParamNotFound = errorsmod.Register( + ModuleName, + 3, + "MarketParam not found", + ) +) diff --git a/protocol/x/vault/types/expected_keepers.go b/protocol/x/vault/types/expected_keepers.go index ab1254f4c2..af96e94a2c 100644 --- a/protocol/x/vault/types/expected_keepers.go +++ b/protocol/x/vault/types/expected_keepers.go @@ -1 +1,48 @@ package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + clobtypes "github.com/dydxprotocol/v4-chain/protocol/x/clob/types" + perptypes "github.com/dydxprotocol/v4-chain/protocol/x/perpetuals/types" + pricestypes "github.com/dydxprotocol/v4-chain/protocol/x/prices/types" +) + +type ClobKeeper interface { + // Clob Pair. + GetClobPair(ctx sdk.Context, id clobtypes.ClobPairId) (val clobtypes.ClobPair, found bool) + + // Order. + GetLongTermOrderPlacement( + ctx sdk.Context, + orderId clobtypes.OrderId, + ) (val clobtypes.LongTermOrderPlacement, found bool) + HandleMsgCancelOrder( + ctx sdk.Context, + msg *clobtypes.MsgCancelOrder, + ) (err error) + HandleMsgPlaceOrder( + ctx sdk.Context, + msg *clobtypes.MsgPlaceOrder, + ) (err error) +} + +type PerpetualsKeeper interface { + GetPerpetual( + ctx sdk.Context, + id uint32, + ) (val perptypes.Perpetual, err error) +} + +type PricesKeeper interface { + GetMarketParam( + ctx sdk.Context, + id uint32, + ) (market pricestypes.MarketParam, exists bool) + GetMarketPrice( + ctx sdk.Context, + id uint32, + ) (pricestypes.MarketPrice, error) +} + +type SubaccountsKeeper interface { +} diff --git a/protocol/x/vault/types/keys.go b/protocol/x/vault/types/keys.go index 7e130df0e9..3ff5c4c37e 100644 --- a/protocol/x/vault/types/keys.go +++ b/protocol/x/vault/types/keys.go @@ -1,10 +1,16 @@ package types -// Module name and store keys +// Module name and store keys. const ( - // ModuleName defines the module name + // ModuleName defines the module name. ModuleName = "vault" - // StoreKey defines the primary module store key + // StoreKey defines the primary module store key. StoreKey = ModuleName ) + +// State. +const ( + // TotalSharesKeyPrefix is the prefix to retrieve all TotalShares. + TotalSharesKeyPrefix = "TotalShares:" +) diff --git a/protocol/x/vault/types/keys_test.go b/protocol/x/vault/types/keys_test.go new file mode 100644 index 0000000000..6b70d1828c --- /dev/null +++ b/protocol/x/vault/types/keys_test.go @@ -0,0 +1,17 @@ +package types_test + +import ( + "testing" + + "github.com/dydxprotocol/v4-chain/protocol/x/vault/types" + "github.com/stretchr/testify/require" +) + +func TestModuleKeys(t *testing.T) { + require.Equal(t, "vault", types.ModuleName) + require.Equal(t, "vault", types.StoreKey) +} + +func TestStateKeys(t *testing.T) { + require.Equal(t, "TotalShares:", types.TotalSharesKeyPrefix) +} diff --git a/protocol/x/vault/types/msg_deposit_to_vault.go b/protocol/x/vault/types/msg_deposit_to_vault.go new file mode 100644 index 0000000000..eefaed1c28 --- /dev/null +++ b/protocol/x/vault/types/msg_deposit_to_vault.go @@ -0,0 +1,5 @@ +package types + +func (msg *MsgDepositToVault) ValidateBasic() error { + return nil +} diff --git a/protocol/x/vault/types/tx.pb.go b/protocol/x/vault/types/tx.pb.go index ce1c55128a..4289883ce0 100644 --- a/protocol/x/vault/types/tx.pb.go +++ b/protocol/x/vault/types/tx.pb.go @@ -6,10 +6,17 @@ package types import ( context "context" fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" grpc1 "github.com/cosmos/gogoproto/grpc" proto "github.com/cosmos/gogoproto/proto" + github_com_dydxprotocol_v4_chain_protocol_dtypes "github.com/dydxprotocol/v4-chain/protocol/dtypes" + types "github.com/dydxprotocol/v4-chain/protocol/x/subaccounts/types" grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" math "math" + math_bits "math/bits" ) // Reference imports to suppress errors if they are not otherwise used. @@ -23,18 +30,131 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// MsgDepositToVault is the Msg/DepositToVault request type. +type MsgDepositToVault struct { + // The vault to deposit into. + VaultId *VaultId `protobuf:"bytes,1,opt,name=vault_id,json=vaultId,proto3" json:"vault_id,omitempty"` + // The subaccount to deposit from. + SubaccountId *types.SubaccountId `protobuf:"bytes,2,opt,name=subaccount_id,json=subaccountId,proto3" json:"subaccount_id,omitempty"` + // Number of quote quantums to deposit. + QuoteQuantums github_com_dydxprotocol_v4_chain_protocol_dtypes.SerializableInt `protobuf:"bytes,3,opt,name=quote_quantums,json=quoteQuantums,proto3,customtype=github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt" json:"quote_quantums"` +} + +func (m *MsgDepositToVault) Reset() { *m = MsgDepositToVault{} } +func (m *MsgDepositToVault) String() string { return proto.CompactTextString(m) } +func (*MsgDepositToVault) ProtoMessage() {} +func (*MsgDepositToVault) Descriptor() ([]byte, []int) { + return fileDescriptor_ced574c6017ce006, []int{0} +} +func (m *MsgDepositToVault) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDepositToVault) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDepositToVault.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 *MsgDepositToVault) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDepositToVault.Merge(m, src) +} +func (m *MsgDepositToVault) XXX_Size() int { + return m.Size() +} +func (m *MsgDepositToVault) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDepositToVault.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDepositToVault proto.InternalMessageInfo + +func (m *MsgDepositToVault) GetVaultId() *VaultId { + if m != nil { + return m.VaultId + } + return nil +} + +func (m *MsgDepositToVault) GetSubaccountId() *types.SubaccountId { + if m != nil { + return m.SubaccountId + } + return nil +} + +// MsgDepositToVaultResponse is the Msg/DepositToVault response type. +type MsgDepositToVaultResponse struct { +} + +func (m *MsgDepositToVaultResponse) Reset() { *m = MsgDepositToVaultResponse{} } +func (m *MsgDepositToVaultResponse) String() string { return proto.CompactTextString(m) } +func (*MsgDepositToVaultResponse) ProtoMessage() {} +func (*MsgDepositToVaultResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_ced574c6017ce006, []int{1} +} +func (m *MsgDepositToVaultResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDepositToVaultResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDepositToVaultResponse.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 *MsgDepositToVaultResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDepositToVaultResponse.Merge(m, src) +} +func (m *MsgDepositToVaultResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgDepositToVaultResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDepositToVaultResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDepositToVaultResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgDepositToVault)(nil), "dydxprotocol.vault.MsgDepositToVault") + proto.RegisterType((*MsgDepositToVaultResponse)(nil), "dydxprotocol.vault.MsgDepositToVaultResponse") +} + func init() { proto.RegisterFile("dydxprotocol/vault/tx.proto", fileDescriptor_ced574c6017ce006) } var fileDescriptor_ced574c6017ce006 = []byte{ - // 128 bytes of a gzipped FileDescriptorProto + // 343 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4e, 0xa9, 0x4c, 0xa9, 0x28, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0xce, 0xcf, 0xd1, 0x2f, 0x4b, 0x2c, 0xcd, 0x29, 0xd1, 0x2f, - 0xa9, 0xd0, 0x03, 0x8b, 0x08, 0x09, 0x21, 0x4b, 0xea, 0x81, 0x25, 0x8d, 0x58, 0xb9, 0x98, 0x7d, - 0x8b, 0xd3, 0x9d, 0x02, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, 0x23, 0x39, - 0xc6, 0x09, 0x8f, 0xe5, 0x18, 0x2e, 0x3c, 0x96, 0x63, 0xb8, 0xf1, 0x58, 0x8e, 0x21, 0xca, 0x3c, - 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, 0xd5, 0x70, 0x13, 0xdd, 0xe4, - 0x8c, 0xc4, 0xcc, 0x3c, 0x7d, 0xb8, 0x48, 0x05, 0xcc, 0xc2, 0xca, 0x82, 0xd4, 0xe2, 0x24, 0x36, - 0xb0, 0xb8, 0x31, 0x20, 0x00, 0x00, 0xff, 0xff, 0x2f, 0x7f, 0xc8, 0xf1, 0x93, 0x00, 0x00, 0x00, + 0xa9, 0xd0, 0x03, 0x8b, 0x08, 0x09, 0x21, 0x4b, 0xea, 0x81, 0x25, 0xa5, 0x34, 0x51, 0x34, 0x14, + 0x97, 0x26, 0x25, 0x26, 0x27, 0xe7, 0x97, 0xe6, 0x95, 0x14, 0x23, 0xb1, 0x21, 0xda, 0xa5, 0xe4, + 0xb0, 0x98, 0x0d, 0x26, 0xa1, 0xf2, 0x22, 0xe9, 0xf9, 0xe9, 0xf9, 0x60, 0xa6, 0x3e, 0x88, 0x05, + 0x11, 0x55, 0xea, 0x64, 0xe2, 0x12, 0xf4, 0x2d, 0x4e, 0x77, 0x49, 0x2d, 0xc8, 0x2f, 0xce, 0x2c, + 0x09, 0xc9, 0x0f, 0x03, 0xe9, 0x10, 0x32, 0xe3, 0xe2, 0x00, 0x6b, 0x8d, 0xcf, 0x4c, 0x91, 0x60, + 0x54, 0x60, 0xd4, 0xe0, 0x36, 0x92, 0xd6, 0xc3, 0x74, 0x9d, 0x1e, 0x58, 0xb1, 0x67, 0x4a, 0x10, + 0x7b, 0x19, 0x84, 0x21, 0xe4, 0xcd, 0xc5, 0x8b, 0x70, 0x17, 0x48, 0x33, 0x13, 0x58, 0xb3, 0x1a, + 0xaa, 0x66, 0x24, 0x6f, 0xe8, 0x05, 0xc3, 0xd9, 0x9e, 0x29, 0x41, 0x3c, 0xc5, 0x48, 0x3c, 0xa1, + 0x7c, 0x2e, 0xbe, 0xc2, 0xd2, 0xfc, 0x92, 0xd4, 0xf8, 0xc2, 0xd2, 0xc4, 0xbc, 0x92, 0xd2, 0xdc, + 0x62, 0x09, 0x66, 0x05, 0x46, 0x0d, 0x1e, 0x27, 0x8f, 0x13, 0xf7, 0xe4, 0x19, 0x6e, 0xdd, 0x93, + 0x77, 0x48, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x47, 0xf5, 0xbb, 0x89, + 0x6e, 0x72, 0x46, 0x62, 0x66, 0x9e, 0x3e, 0x5c, 0x24, 0xa5, 0xa4, 0xb2, 0x20, 0xb5, 0x58, 0x2f, + 0x38, 0xb5, 0x28, 0x33, 0x31, 0x27, 0xb3, 0x2a, 0x31, 0x29, 0x27, 0xd5, 0x33, 0xaf, 0x24, 0x88, + 0x17, 0x6c, 0x7e, 0x20, 0xd4, 0x78, 0x25, 0x69, 0x2e, 0x49, 0x8c, 0xa0, 0x08, 0x4a, 0x2d, 0x2e, + 0xc8, 0xcf, 0x2b, 0x4e, 0x35, 0xca, 0xe5, 0x62, 0xf6, 0x2d, 0x4e, 0x17, 0x4a, 0xe3, 0xe2, 0x43, + 0x0b, 0x2b, 0x55, 0x6c, 0x21, 0x83, 0x61, 0x8e, 0x94, 0x2e, 0x51, 0xca, 0x60, 0xd6, 0x39, 0x05, + 0x9e, 0x78, 0x24, 0xc7, 0x78, 0xe1, 0x91, 0x1c, 0xe3, 0x83, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, + 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, 0x1c, 0x43, 0x94, 0x39, 0xf1, 0xde, 0xae, 0x80, + 0x25, 0x31, 0x90, 0xef, 0x93, 0xd8, 0xc0, 0xe2, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0xa5, + 0x6a, 0xc3, 0x6b, 0x85, 0x02, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -49,6 +169,8 @@ const _ = grpc.SupportPackageIsVersion4 // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type MsgClient interface { + // DepositToVault deposits funds into a vault. + DepositToVault(ctx context.Context, in *MsgDepositToVault, opts ...grpc.CallOption) (*MsgDepositToVaultResponse, error) } type msgClient struct { @@ -59,22 +181,475 @@ func NewMsgClient(cc grpc1.ClientConn) MsgClient { return &msgClient{cc} } +func (c *msgClient) DepositToVault(ctx context.Context, in *MsgDepositToVault, opts ...grpc.CallOption) (*MsgDepositToVaultResponse, error) { + out := new(MsgDepositToVaultResponse) + err := c.cc.Invoke(ctx, "/dydxprotocol.vault.Msg/DepositToVault", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // MsgServer is the server API for Msg service. type MsgServer interface { + // DepositToVault deposits funds into a vault. + DepositToVault(context.Context, *MsgDepositToVault) (*MsgDepositToVaultResponse, error) } // UnimplementedMsgServer can be embedded to have forward compatible implementations. type UnimplementedMsgServer struct { } +func (*UnimplementedMsgServer) DepositToVault(ctx context.Context, req *MsgDepositToVault) (*MsgDepositToVaultResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DepositToVault not implemented") +} + func RegisterMsgServer(s grpc1.Server, srv MsgServer) { s.RegisterService(&_Msg_serviceDesc, srv) } +func _Msg_DepositToVault_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgDepositToVault) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).DepositToVault(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/dydxprotocol.vault.Msg/DepositToVault", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).DepositToVault(ctx, req.(*MsgDepositToVault)) + } + return interceptor(ctx, in, info, handler) +} + var _Msg_serviceDesc = grpc.ServiceDesc{ ServiceName: "dydxprotocol.vault.Msg", HandlerType: (*MsgServer)(nil), - Methods: []grpc.MethodDesc{}, - Streams: []grpc.StreamDesc{}, - Metadata: "dydxprotocol/vault/tx.proto", + Methods: []grpc.MethodDesc{ + { + MethodName: "DepositToVault", + Handler: _Msg_DepositToVault_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "dydxprotocol/vault/tx.proto", +} + +func (m *MsgDepositToVault) 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 *MsgDepositToVault) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDepositToVault) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.QuoteQuantums.Size() + i -= size + if _, err := m.QuoteQuantums.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x1a + if m.SubaccountId != nil { + { + size, err := m.SubaccountId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + if m.VaultId != nil { + { + size, err := m.VaultId.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil } + +func (m *MsgDepositToVaultResponse) 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 *MsgDepositToVaultResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDepositToVaultResponse) 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 + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgDepositToVault) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.VaultId != nil { + l = m.VaultId.Size() + n += 1 + l + sovTx(uint64(l)) + } + if m.SubaccountId != nil { + l = m.SubaccountId.Size() + n += 1 + l + sovTx(uint64(l)) + } + l = m.QuoteQuantums.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgDepositToVaultResponse) 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 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgDepositToVault) 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: MsgDepositToVault: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDepositToVault: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field VaultId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.VaultId == nil { + m.VaultId = &VaultId{} + } + if err := m.VaultId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SubaccountId", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.SubaccountId == nil { + m.SubaccountId = &types.SubaccountId{} + } + if err := m.SubaccountId.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field QuoteQuantums", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.QuoteQuantums.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + 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 (m *MsgDepositToVaultResponse) 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: MsgDepositToVaultResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDepositToVaultResponse: 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 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/protocol/x/vault/types/vault.pb.go b/protocol/x/vault/types/vault.pb.go new file mode 100644 index 0000000000..f0797dd494 --- /dev/null +++ b/protocol/x/vault/types/vault.pb.go @@ -0,0 +1,543 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: dydxprotocol/vault/vault.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + github_com_dydxprotocol_v4_chain_protocol_dtypes "github.com/dydxprotocol/v4-chain/protocol/dtypes" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// VaultType represents different types of vaults. +type VaultType int32 + +const ( + // Default value, invalid and unused. + VaultType_VAULT_TYPE_UNSPECIFIED VaultType = 0 + // Vault is associated with a CLOB pair. + VaultType_VAULT_TYPE_CLOB VaultType = 1 +) + +var VaultType_name = map[int32]string{ + 0: "VAULT_TYPE_UNSPECIFIED", + 1: "VAULT_TYPE_CLOB", +} + +var VaultType_value = map[string]int32{ + "VAULT_TYPE_UNSPECIFIED": 0, + "VAULT_TYPE_CLOB": 1, +} + +func (x VaultType) String() string { + return proto.EnumName(VaultType_name, int32(x)) +} + +func (VaultType) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_32accb5830bb2860, []int{0} +} + +// VaultId uniquely identifies a vault by its type and number. +type VaultId struct { + // Type of the vault. + Type VaultType `protobuf:"varint,1,opt,name=type,proto3,enum=dydxprotocol.vault.VaultType" json:"type,omitempty"` + // Unique ID of the vault within above type. + Number uint32 `protobuf:"varint,2,opt,name=number,proto3" json:"number,omitempty"` +} + +func (m *VaultId) Reset() { *m = VaultId{} } +func (m *VaultId) String() string { return proto.CompactTextString(m) } +func (*VaultId) ProtoMessage() {} +func (*VaultId) Descriptor() ([]byte, []int) { + return fileDescriptor_32accb5830bb2860, []int{0} +} +func (m *VaultId) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *VaultId) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_VaultId.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 *VaultId) XXX_Merge(src proto.Message) { + xxx_messageInfo_VaultId.Merge(m, src) +} +func (m *VaultId) XXX_Size() int { + return m.Size() +} +func (m *VaultId) XXX_DiscardUnknown() { + xxx_messageInfo_VaultId.DiscardUnknown(m) +} + +var xxx_messageInfo_VaultId proto.InternalMessageInfo + +func (m *VaultId) GetType() VaultType { + if m != nil { + return m.Type + } + return VaultType_VAULT_TYPE_UNSPECIFIED +} + +func (m *VaultId) GetNumber() uint32 { + if m != nil { + return m.Number + } + return 0 +} + +// NumShares represents the number of shares in a vault. +type NumShares struct { + // Number of shares. + NumShares github_com_dydxprotocol_v4_chain_protocol_dtypes.SerializableInt `protobuf:"bytes,1,opt,name=num_shares,json=numShares,proto3,customtype=github.com/dydxprotocol/v4-chain/protocol/dtypes.SerializableInt" json:"num_shares"` +} + +func (m *NumShares) Reset() { *m = NumShares{} } +func (m *NumShares) String() string { return proto.CompactTextString(m) } +func (*NumShares) ProtoMessage() {} +func (*NumShares) Descriptor() ([]byte, []int) { + return fileDescriptor_32accb5830bb2860, []int{1} +} +func (m *NumShares) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *NumShares) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_NumShares.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 *NumShares) XXX_Merge(src proto.Message) { + xxx_messageInfo_NumShares.Merge(m, src) +} +func (m *NumShares) XXX_Size() int { + return m.Size() +} +func (m *NumShares) XXX_DiscardUnknown() { + xxx_messageInfo_NumShares.DiscardUnknown(m) +} + +var xxx_messageInfo_NumShares proto.InternalMessageInfo + +func init() { + proto.RegisterEnum("dydxprotocol.vault.VaultType", VaultType_name, VaultType_value) + proto.RegisterType((*VaultId)(nil), "dydxprotocol.vault.VaultId") + proto.RegisterType((*NumShares)(nil), "dydxprotocol.vault.NumShares") +} + +func init() { proto.RegisterFile("dydxprotocol/vault/vault.proto", fileDescriptor_32accb5830bb2860) } + +var fileDescriptor_32accb5830bb2860 = []byte{ + // 300 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xa9, 0x4c, 0xa9, + 0x28, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0xce, 0xcf, 0xd1, 0x2f, 0x4b, 0x2c, 0xcd, 0x29, 0x81, 0x90, + 0x7a, 0x60, 0x41, 0x21, 0x21, 0x64, 0x79, 0x3d, 0xb0, 0x8c, 0x94, 0x48, 0x7a, 0x7e, 0x7a, 0x3e, + 0x58, 0x4c, 0x1f, 0xc4, 0x82, 0xa8, 0x54, 0x0a, 0xe1, 0x62, 0x0f, 0x03, 0x49, 0x7b, 0xa6, 0x08, + 0x19, 0x72, 0xb1, 0x94, 0x54, 0x16, 0xa4, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x19, 0xc9, 0xea, + 0x61, 0x9a, 0xa1, 0x07, 0x56, 0x1a, 0x52, 0x59, 0x90, 0x1a, 0x04, 0x56, 0x2a, 0x24, 0xc6, 0xc5, + 0x96, 0x57, 0x9a, 0x9b, 0x94, 0x5a, 0x24, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, 0x1b, 0x04, 0xe5, 0x29, + 0x95, 0x70, 0x71, 0xfa, 0x95, 0xe6, 0x06, 0x67, 0x24, 0x16, 0xa5, 0x16, 0x0b, 0xa5, 0x73, 0x71, + 0xe5, 0x95, 0xe6, 0xc6, 0x17, 0x83, 0x79, 0x60, 0xd3, 0x79, 0x9c, 0x3c, 0x4e, 0xdc, 0x93, 0x67, + 0xb8, 0x75, 0x4f, 0xde, 0x21, 0x3d, 0xb3, 0x24, 0xa3, 0x34, 0x49, 0x2f, 0x39, 0x3f, 0x57, 0x1f, + 0xd5, 0x4f, 0x26, 0xba, 0xc9, 0x19, 0x89, 0x99, 0x79, 0xfa, 0x70, 0x91, 0x14, 0x90, 0x8d, 0xc5, + 0x7a, 0xc1, 0xa9, 0x45, 0x99, 0x89, 0x39, 0x99, 0x55, 0x89, 0x49, 0x39, 0xa9, 0x9e, 0x79, 0x25, + 0x41, 0x9c, 0x79, 0x30, 0x8b, 0xb4, 0x6c, 0xb8, 0x38, 0xe1, 0x0e, 0x14, 0x92, 0xe2, 0x12, 0x0b, + 0x73, 0x0c, 0xf5, 0x09, 0x89, 0x0f, 0x89, 0x0c, 0x70, 0x8d, 0x0f, 0xf5, 0x0b, 0x0e, 0x70, 0x75, + 0xf6, 0x74, 0xf3, 0x74, 0x75, 0x11, 0x60, 0x10, 0x12, 0xe6, 0xe2, 0x47, 0x92, 0x73, 0xf6, 0xf1, + 0x77, 0x12, 0x60, 0x74, 0x0a, 0x3c, 0xf1, 0x48, 0x8e, 0xf1, 0xc2, 0x23, 0x39, 0xc6, 0x07, 0x8f, + 0xe4, 0x18, 0x27, 0x3c, 0x96, 0x63, 0xb8, 0xf0, 0x58, 0x8e, 0xe1, 0xc6, 0x63, 0x39, 0x86, 0x28, + 0x73, 0xe2, 0x1d, 0x59, 0x01, 0x8d, 0x0c, 0xb0, 0x5b, 0x93, 0xd8, 0xc0, 0xe2, 0xc6, 0x80, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x22, 0x8c, 0x36, 0xdc, 0xaf, 0x01, 0x00, 0x00, +} + +func (m *VaultId) 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 *VaultId) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *VaultId) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if m.Number != 0 { + i = encodeVarintVault(dAtA, i, uint64(m.Number)) + i-- + dAtA[i] = 0x10 + } + if m.Type != 0 { + i = encodeVarintVault(dAtA, i, uint64(m.Type)) + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func (m *NumShares) 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 *NumShares) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *NumShares) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size := m.NumShares.Size() + i -= size + if _, err := m.NumShares.MarshalTo(dAtA[i:]); err != nil { + return 0, err + } + i = encodeVarintVault(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintVault(dAtA []byte, offset int, v uint64) int { + offset -= sovVault(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *VaultId) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.Type != 0 { + n += 1 + sovVault(uint64(m.Type)) + } + if m.Number != 0 { + n += 1 + sovVault(uint64(m.Number)) + } + return n +} + +func (m *NumShares) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.NumShares.Size() + n += 1 + l + sovVault(uint64(l)) + return n +} + +func sovVault(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozVault(x uint64) (n int) { + return sovVault(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *VaultId) 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 ErrIntOverflowVault + } + 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: VaultId: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: VaultId: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType) + } + m.Type = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVault + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Type |= VaultType(b&0x7F) << shift + if b < 0x80 { + break + } + } + case 2: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field Number", wireType) + } + m.Number = 0 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVault + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + m.Number |= uint32(b&0x7F) << shift + if b < 0x80 { + break + } + } + default: + iNdEx = preIndex + skippy, err := skipVault(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthVault + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *NumShares) 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 ErrIntOverflowVault + } + 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: NumShares: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: NumShares: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field NumShares", wireType) + } + var byteLen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowVault + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + byteLen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if byteLen < 0 { + return ErrInvalidLengthVault + } + postIndex := iNdEx + byteLen + if postIndex < 0 { + return ErrInvalidLengthVault + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.NumShares.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipVault(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthVault + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipVault(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowVault + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowVault + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowVault + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthVault + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupVault + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthVault + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthVault = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowVault = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupVault = fmt.Errorf("proto: unexpected end of group") +) diff --git a/protocol/x/vault/types/vault_id.go b/protocol/x/vault/types/vault_id.go new file mode 100644 index 0000000000..7db466810c --- /dev/null +++ b/protocol/x/vault/types/vault_id.go @@ -0,0 +1,23 @@ +package types + +import ( + fmt "fmt" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" +) + +func (id *VaultId) ToStateKey() []byte { + b, err := id.Marshal() + if err != nil { + panic(err) + } + return b +} + +// ToModuleAccountAddress returns the module account address for the vault +// (generated from string "vault--") +func (id *VaultId) ToModuleAccountAddress() string { + return authtypes.NewModuleAddress( + fmt.Sprintf("vault-%s-%d", id.Type, id.Number), + ).String() +} diff --git a/protocol/x/vault/types/vault_id_test.go b/protocol/x/vault/types/vault_id_test.go new file mode 100644 index 0000000000..d93aee6a83 --- /dev/null +++ b/protocol/x/vault/types/vault_id_test.go @@ -0,0 +1,30 @@ +package types_test + +import ( + "testing" + + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + "github.com/dydxprotocol/v4-chain/protocol/testutil/constants" + "github.com/stretchr/testify/require" +) + +func TestToStateKey(t *testing.T) { + b, _ := constants.Vault_Clob_0.Marshal() + require.Equal(t, b, constants.Vault_Clob_0.ToStateKey()) + + b, _ = constants.Vault_Clob_1.Marshal() + require.Equal(t, b, constants.Vault_Clob_1.ToStateKey()) +} + +func TestToModuleAccountAddress(t *testing.T) { + require.Equal( + t, + authtypes.NewModuleAddress("vault-VAULT_TYPE_CLOB-0").String(), + constants.Vault_Clob_0.ToModuleAccountAddress(), + ) + require.Equal( + t, + authtypes.NewModuleAddress("vault-VAULT_TYPE_CLOB-1").String(), + constants.Vault_Clob_1.ToModuleAccountAddress(), + ) +}