Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TRA-512] Add MsgCreateMarketPermissionless msg #1991

Merged
merged 10 commits into from
Jul 31, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,18 +1,22 @@
import { Rpc } from "../../helpers";
import * as _m0 from "protobufjs/minimal";
import { MsgSetMarketsHardCap, MsgSetMarketsHardCapResponse } from "./tx";
import { MsgSetMarketsHardCap, MsgSetMarketsHardCapResponse, MsgCreateMarketPermissionless, MsgCreateMarketPermissionlessResponse } from "./tx";
/** Msg defines the Msg service. */

export interface Msg {
/** SetMarketsHardCap sets a hard cap on the number of markets listed */
setMarketsHardCap(request: MsgSetMarketsHardCap): Promise<MsgSetMarketsHardCapResponse>;
/** CreateMarketPermissionless creates a new market without going through x/gov */

createMarketPermissionless(request: MsgCreateMarketPermissionless): Promise<MsgCreateMarketPermissionlessResponse>;
}
export class MsgClientImpl implements Msg {
private readonly rpc: Rpc;

constructor(rpc: Rpc) {
this.rpc = rpc;
this.setMarketsHardCap = this.setMarketsHardCap.bind(this);
this.createMarketPermissionless = this.createMarketPermissionless.bind(this);
}

setMarketsHardCap(request: MsgSetMarketsHardCap): Promise<MsgSetMarketsHardCapResponse> {
Expand All @@ -21,4 +25,10 @@ export class MsgClientImpl implements Msg {
return promise.then(data => MsgSetMarketsHardCapResponse.decode(new _m0.Reader(data)));
}

createMarketPermissionless(request: MsgCreateMarketPermissionless): Promise<MsgCreateMarketPermissionlessResponse> {
const data = MsgCreateMarketPermissionless.encode(request).finish();
const promise = this.rpc.request("dydxprotocol.listing.Msg", "CreateMarketPermissionless", data);
return promise.then(data => MsgCreateMarketPermissionlessResponse.decode(new _m0.Reader(data)));
}

}
142 changes: 142 additions & 0 deletions indexer/packages/v4-protos/src/codegen/dydxprotocol/listing/tx.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { SubaccountId, SubaccountIdSDKType } from "../subaccounts/subaccount";
import * as _m0 from "protobufjs/minimal";
import { DeepPartial } from "../../helpers";
/**
Expand Down Expand Up @@ -28,6 +29,48 @@ export interface MsgSetMarketsHardCapResponse {}
/** MsgSetMarketsHardCapResponse defines the MsgSetMarketsHardCap response */

export interface MsgSetMarketsHardCapResponseSDKType {}
/**
* MsgCreateMarketPermissionless is a message used to create new markets without
* // going through x/gov
*/

export interface MsgCreateMarketPermissionless {
/** The name of the `Perpetual` (e.g. `BTC-USD`). */
ticker: string;
/** The subaccount to deposit from. */

subaccountId?: SubaccountId;
/** Number of quote quantums to deposit. */

quoteQuantums: Uint8Array;
}
/**
* MsgCreateMarketPermissionless is a message used to create new markets without
* // going through x/gov
*/

export interface MsgCreateMarketPermissionlessSDKType {
/** The name of the `Perpetual` (e.g. `BTC-USD`). */
ticker: string;
/** The subaccount to deposit from. */

subaccount_id?: SubaccountIdSDKType;
/** Number of quote quantums to deposit. */

quote_quantums: Uint8Array;
}
/**
* MsgCreateMarketPermissionlessResponse defines the
* MsgCreateMarketPermissionless response
*/

export interface MsgCreateMarketPermissionlessResponse {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a type alias for the empty interface.

An empty interface is equivalent to {}. Use a type alias instead.

- export interface MsgCreateMarketPermissionlessResponse {}
+ export type MsgCreateMarketPermissionlessResponse = {};
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface MsgCreateMarketPermissionlessResponse {}
export type MsgCreateMarketPermissionlessResponse = {};
Tools
Biome

[error] 67-67: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

/**
* MsgCreateMarketPermissionlessResponse defines the
* MsgCreateMarketPermissionless response
*/

export interface MsgCreateMarketPermissionlessResponseSDKType {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using a type alias for the empty interface.

An empty interface is equivalent to {}. Use a type alias instead.

- export interface MsgCreateMarketPermissionlessResponseSDKType {}
+ export type MsgCreateMarketPermissionlessResponseSDKType = {};
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export interface MsgCreateMarketPermissionlessResponseSDKType {}
export type MsgCreateMarketPermissionlessResponseSDKType = {};
Tools
Biome

[error] 73-73: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)


function createBaseMsgSetMarketsHardCap(): MsgSetMarketsHardCap {
return {
Expand Down Expand Up @@ -116,4 +159,103 @@ export const MsgSetMarketsHardCapResponse = {
return message;
}

};

function createBaseMsgCreateMarketPermissionless(): MsgCreateMarketPermissionless {
return {
ticker: "",
subaccountId: undefined,
quoteQuantums: new Uint8Array()
};
}

export const MsgCreateMarketPermissionless = {
encode(message: MsgCreateMarketPermissionless, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
if (message.ticker !== "") {
writer.uint32(10).string(message.ticker);
}

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): MsgCreateMarketPermissionless {
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseMsgCreateMarketPermissionless();

while (reader.pos < end) {
const tag = reader.uint32();

switch (tag >>> 3) {
case 1:
message.ticker = reader.string();
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<MsgCreateMarketPermissionless>): MsgCreateMarketPermissionless {
const message = createBaseMsgCreateMarketPermissionless();
message.ticker = object.ticker ?? "";
message.subaccountId = object.subaccountId !== undefined && object.subaccountId !== null ? SubaccountId.fromPartial(object.subaccountId) : undefined;
message.quoteQuantums = object.quoteQuantums ?? new Uint8Array();
return message;
}

};

function createBaseMsgCreateMarketPermissionlessResponse(): MsgCreateMarketPermissionlessResponse {
return {};
}

export const MsgCreateMarketPermissionlessResponse = {
encode(_: MsgCreateMarketPermissionlessResponse, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
return writer;
},

decode(input: _m0.Reader | Uint8Array, length?: number): MsgCreateMarketPermissionlessResponse {
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
let end = length === undefined ? reader.len : reader.pos + length;
const message = createBaseMsgCreateMarketPermissionlessResponse();

while (reader.pos < end) {
const tag = reader.uint32();

switch (tag >>> 3) {
default:
reader.skipType(tag & 7);
break;
}
}

return message;
},

fromPartial(_: DeepPartial<MsgCreateMarketPermissionlessResponse>): MsgCreateMarketPermissionlessResponse {
const message = createBaseMsgCreateMarketPermissionlessResponse();
return message;
}

shrenujb marked this conversation as resolved.
Show resolved Hide resolved
};
29 changes: 29 additions & 0 deletions proto/dydxprotocol/listing/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package dydxprotocol.listing;

import "cosmos_proto/cosmos.proto";
import "cosmos/msg/v1/msg.proto";
import "gogoproto/gogo.proto";
import "dydxprotocol/subaccounts/subaccount.proto";

option go_package = "github.com/dydxprotocol/v4-chain/protocol/x/listing/types";

Expand All @@ -11,6 +13,10 @@ service Msg {
// SetMarketsHardCap sets a hard cap on the number of markets listed
rpc SetMarketsHardCap(MsgSetMarketsHardCap)
returns (MsgSetMarketsHardCapResponse);

// CreateMarketPermissionless creates a new market without going through x/gov
rpc CreateMarketPermissionless(MsgCreateMarketPermissionless)
returns (MsgCreateMarketPermissionlessResponse);
}

// MsgSetMarketsHardCap is used to set a hard cap on the number of markets
Expand All @@ -27,3 +33,26 @@ message MsgSetMarketsHardCap {

// MsgSetMarketsHardCapResponse defines the MsgSetMarketsHardCap response
message MsgSetMarketsHardCapResponse {}

// MsgCreateMarketPermissionless is a message used to create new markets without
// // going through x/gov
message MsgCreateMarketPermissionless {
option (cosmos.msg.v1.signer) = "subaccount_id";

// The name of the `Perpetual` (e.g. `BTC-USD`).
string ticker = 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
];
}

// MsgCreateMarketPermissionlessResponse defines the
// MsgCreateMarketPermissionless response
message MsgCreateMarketPermissionlessResponse {}
3 changes: 3 additions & 0 deletions protocol/app/module/interface_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ func NewInterfaceRegistry(addrPrefix string, valAddrPrefix string) (types.Interf
"dydxprotocol.vault.MsgDepositToVault": getLegacyMsgSignerFn(
[]string{"subaccount_id", "owner"},
),
"dydxprotocol.listing.MsgCreateMarketPermissionless": getLegacyMsgSignerFn(
[]string{"subaccount_id", "owner"},
),

// App injected messages have no signers.
"dydxprotocol.bridge.MsgAcknowledgeBridges": noSigners,
Expand Down
6 changes: 4 additions & 2 deletions protocol/app/msgs/all_msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,10 @@ var (
"/dydxprotocol.govplus.MsgSlashValidatorResponse": {},

// listing
"/dydxprotocol.listing.MsgSetMarketsHardCap": {},
"/dydxprotocol.listing.MsgSetMarketsHardCapResponse": {},
"/dydxprotocol.listing.MsgSetMarketsHardCap": {},
"/dydxprotocol.listing.MsgSetMarketsHardCapResponse": {},
"/dydxprotocol.listing.MsgCreateMarketPermissionless": {},
"/dydxprotocol.listing.MsgCreateMarketPermissionlessResponse": {},

// perpetuals
"/dydxprotocol.perpetuals.MsgAddPremiumVotes": {},
Expand Down
5 changes: 5 additions & 0 deletions protocol/app/msgs/normal_msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
ibccore "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types"
"github.com/dydxprotocol/v4-chain/protocol/lib"
clob "github.com/dydxprotocol/v4-chain/protocol/x/clob/types"
listing "github.com/dydxprotocol/v4-chain/protocol/x/listing/types"
sending "github.com/dydxprotocol/v4-chain/protocol/x/sending/types"
vault "github.com/dydxprotocol/v4-chain/protocol/x/vault/types"
marketmapmoduletypes "github.com/skip-mev/slinky/x/marketmap/types"
Expand Down Expand Up @@ -224,6 +225,10 @@ var (
"/dydxprotocol.clob.MsgPlaceOrder": &clob.MsgPlaceOrder{},
"/dydxprotocol.clob.MsgPlaceOrderResponse": nil,

// listing
"/dydxprotocol.listing.MsgCreateMarketPermissionless": &listing.MsgCreateMarketPermissionless{},
"/dydxprotocol.listing.MsgCreateMarketPermissionlessResponse": nil,

// perpetuals

// prices
Expand Down
4 changes: 4 additions & 0 deletions protocol/app/msgs/normal_msgs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ func TestNormalMsgs_Key(t *testing.T) {
"/dydxprotocol.clob.MsgPlaceOrder",
"/dydxprotocol.clob.MsgPlaceOrderResponse",

// listing
"/dydxprotocol.listing.MsgCreateMarketPermissionless",
"/dydxprotocol.listing.MsgCreateMarketPermissionlessResponse",

// perpetuals

// prices
Expand Down
40 changes: 40 additions & 0 deletions protocol/x/listing/keeper/msg_create_market_permissionless.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package keeper

import (
"context"

"github.com/dydxprotocol/v4-chain/protocol/lib"
"github.com/dydxprotocol/v4-chain/protocol/x/listing/types"
)

func (k msgServer) CreateMarketPermissionless(
goCtx context.Context,
msg *types.MsgCreateMarketPermissionless,
) (*types.MsgCreateMarketPermissionlessResponse, error) {
ctx := lib.UnwrapSDKContext(goCtx, types.ModuleName)

// Check if the number of listed markets is above the hard cap
numPerpetuals := len(k.PerpetualsKeeper.GetAllPerpetuals(ctx))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why check num perpetuals instead of num markets?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No reason. Shouldn't really make a difference

if uint32(numPerpetuals) > k.Keeper.GetMarketsHardCap(ctx) {
return nil, types.ErrMarketsHardCapReached
}

marketId, err := k.Keeper.CreateMarket(ctx, msg.Ticker)
if err != nil {
return nil, err
}

perpetualId, err := k.Keeper.CreatePerpetual(ctx, marketId, msg.Ticker)
if err != nil {
return nil, err
}

_, err = k.Keeper.CreateClobPair(ctx, perpetualId)
if err != nil {
return nil, err
}

// TODO: vault deposit for PML
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reminder: Implement the vault deposit logic.

The TODO comment indicates that the vault deposit logic for PML is missing.

Do you want me to generate the implementation code or open a GitHub issue to track this task?


return &types.MsgCreateMarketPermissionlessResponse{}, nil
}
6 changes: 6 additions & 0 deletions protocol/x/listing/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,10 @@ var (
1,
"reference price is zero",
)

ErrMarketsHardCapReached = errorsmod.Register(
ModuleName,
2,
"listed markets hard cap reached",
)
)
1 change: 1 addition & 0 deletions protocol/x/listing/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ type PerpetualsKeeper interface {
marketType perpetualtypes.PerpetualMarketType,
) (perpetualtypes.Perpetual, error)
AcquireNextPerpetualID(ctx sdk.Context) uint32
GetAllPerpetuals(ctx sdk.Context) (list []perpetualtypes.Perpetual)
}
Loading
Loading