Skip to content

Commit

Permalink
imp(dymns): patch x/dymns follow audit report (#1265)
Browse files Browse the repository at this point in the history
  • Loading branch information
VictorTrustyDev authored Oct 4, 2024
1 parent b738cff commit 3d1c96d
Show file tree
Hide file tree
Showing 66 changed files with 2,935 additions and 4,237 deletions.
1 change: 0 additions & 1 deletion app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,6 @@ func (a *AppKeepers) SetupHooks() {
a.IncentivesKeeper.Hooks(),
a.TxFeesKeeper.Hooks(),
a.DelayedAckKeeper.GetEpochHooks(),
a.DymNSKeeper.GetEpochHooks(),
a.RollappKeeper.GetEpochHooks(),
),
)
Expand Down
19 changes: 2 additions & 17 deletions proto/dymensionxyz/dymension/dymns/market.proto
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ option go_package = "github.com/dymensionxyz/dymension/v3/x/dymns/types";
// SellOrder defines a sell order, placed by owner, to sell a Dym-Name/Alias.
// Sell-Order has an expiry date.
// After expiry date, if no one has placed a bid, this Sell-Order will be closed, no change.
// If there is a bid, the highest bid will win, and the Dym-Name/Alias ownership will be transferred to the winner.
// If the bid matches the sell price, the Dym-Name/Alias ownership will be transferred to the bidder immediately.
// - If there is a bid, the highest bid will win, and the Dym-Name/Alias ownership will be transferred to the winner.
// - If the bid matches the sell price, the Dym-Name/Alias ownership will be transferred to the bidder immediately.
message SellOrder {
// asset_id is the Dym-Name/Alias being opened to be sold.
string asset_id = 1;
Expand All @@ -33,21 +33,6 @@ message SellOrder {
SellOrderBid highest_bid = 6;
}

// ActiveSellOrdersExpiration contains list of active SOs, store expiration date mapped by asset identity.
// Used by hook to find out expired SO instead of iterating through all records.
message ActiveSellOrdersExpiration {
repeated ActiveSellOrdersExpirationRecord records = 1 [(gogoproto.nullable) = false];
}

// ActiveSellOrdersExpirationRecord contains the expiration date of an active Sell-Order.
message ActiveSellOrdersExpirationRecord {
// asset_id is the Dym-Name/Alias being opened to be sold.
string asset_id = 1;

// expire_at is the last effective date of this Sell-Order.
int64 expire_at = 2;
}

// SellOrderBid defines a bid placed by an account on a Sell-Order.
message SellOrderBid {
// bidder is the account address of the account which placed the bid.
Expand Down
9 changes: 7 additions & 2 deletions proto/dymensionxyz/dymension/dymns/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ message PriceParams {
(gogoproto.moretags) = "yaml:\"min_offer_price\"",
(gogoproto.nullable) = false
];

// min_bid_increment_percent is the minimum percent raised compare to previous bid of a Sell-Order.
// The valid range from 0% to 100%, but capped at 10%.
uint32 min_bid_increment_percent = 6 [
(gogoproto.moretags) = "yaml:\"min_bid_increment_percent\""
];
}

// ChainsParams defines setting for prioritized aliases mapping.
Expand Down Expand Up @@ -127,7 +133,6 @@ message MiscParams {
bool enable_trading_name = 4;

// enable_trading_alias is the flag to enable trading of Alias.
// To be used in the future when Alias trading implementation is ready
// or disable trading of Alias when needed.
// To be used to stop trading of Alias when needed.
bool enable_trading_alias = 5;
}
22 changes: 22 additions & 0 deletions proto/dymensionxyz/dymension/dymns/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ service Msg {
// This will stop the advertisement and remove the Dym-Name/Alias sale from the market.
// Can only be performed if no one has placed a bid on the asset.
rpc CancelSellOrder(MsgCancelSellOrder) returns (MsgCancelSellOrderResponse) {}
// CompleteSellOrder is message handler,
// handles Sell-Order completion action, can be performed by either asset owner or the person who placed the highest bid.
// Can only be performed when Sell-Order expired and has a bid placed.
// If the asset was expired or prohibited trading, bid placed will be force to return to the bidder, ownership will not be transferred.
rpc CompleteSellOrder(MsgCompleteSellOrder) returns (MsgCompleteSellOrderResponse) {}
// PurchaseOrder is message handler,
// handles purchasing a Dym-Name/Alias from a Sell-Order, performed by the buyer.
rpc PurchaseOrder(MsgPurchaseOrder) returns (MsgPurchaseOrderResponse) {}
Expand Down Expand Up @@ -219,6 +224,23 @@ message MsgCancelSellOrder {
// MsgCancelSellOrderResponse defines the response for the Sell-Order cancellation.
message MsgCancelSellOrderResponse {}

// MsgCompleteSellOrder defines the message used for user to complete a Sell-Order.
message MsgCompleteSellOrder {
option (cosmos.msg.v1.signer) = "participant";

// asset_id is the Dym-Name/Alias about to perform Sell Order completion action.
string asset_id = 1;

// asset_type is the type of the asset of the order, is Dym-Name/Alias.
AssetType asset_type = 2;

// participant is the bech32-encoded address of either asset owner or highest bidder account.
string participant = 3;
}

// MsgCompleteSellOrderResponse defines the response for the Sell-Order completion.
message MsgCompleteSellOrderResponse {}

// MsgPurchaseOrder defines the message used for user to bid/purchase a Sell-Order.
message MsgPurchaseOrder {
option (cosmos.msg.v1.signer) = "buyer";
Expand Down
2 changes: 2 additions & 0 deletions x/dymns/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ func GetTxCmd() *cobra.Command {
NewUpdateDetailsTxCmd(),
NewPlaceDymNameSellOrderTxCmd(),
NewPlaceAliasSellOrderTxCmd(),
NewCancelSellOrderTxCmd(),
NewCompleteSellOrderTxCmd(),
NewPlaceBidOnDymNameOrderTxCmd(),
NewPlaceBidOnAliasOrderTxCmd(),
NewOfferBuyDymNameTxCmd(),
Expand Down
71 changes: 71 additions & 0 deletions x/dymns/client/cli/tx_cancel_sell_order.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package cli

import (
"fmt"
"strings"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/version"
dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types"
dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils"
"github.com/spf13/cobra"
)

// NewCancelSellOrderTxCmd is the CLI command for close a Sell-Order.
func NewCancelSellOrderTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "cancel-sell-order [Name/Alias/Handle] [myname/x]",
Aliases: []string{"cancel-so"},
Short: "Cancel a sell-order (only when no bid placed)",
Example: fmt.Sprintf(
`$ %s tx %s cancel-sell-order name myname --%s owner
$ %s tx %s cancel-sell-order alias x --%s owner`,
version.AppName, dymnstypes.ModuleName, flags.FlagFrom,
version.AppName, dymnstypes.ModuleName, flags.FlagFrom,
),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

target := args[1]
var assetType dymnstypes.AssetType

switch strings.ToLower(args[0]) {
case "name", "dym-name", "dymname", "n":
assetType = dymnstypes.TypeName
if !dymnsutils.IsValidDymName(target) {
return fmt.Errorf("input is not a valid Dym-Name: %s", target)
}
case "alias", "handle", "handles", "a":
assetType = dymnstypes.TypeAlias
if !dymnsutils.IsValidAlias(target) {
return fmt.Errorf("input is not a valid Alias: %s", target)
}
default:
return fmt.Errorf("invalid asset type: %s, must be 'Name' or 'Alias'/'Handle'", args[0])
}

owner := clientCtx.GetFromAddress().String()
if owner == "" {
return fmt.Errorf("flag --%s is required", flags.FlagFrom)
}

msg := &dymnstypes.MsgCancelSellOrder{
AssetId: target,
AssetType: assetType,
Owner: owner,
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
72 changes: 72 additions & 0 deletions x/dymns/client/cli/tx_complete_sell_order.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package cli

import (
"fmt"
"strings"

"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/version"
dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types"
dymnsutils "github.com/dymensionxyz/dymension/v3/x/dymns/utils"
"github.com/spf13/cobra"
)

// NewCompleteSellOrderTxCmd is the CLI command for completing a Sell-Order.
func NewCompleteSellOrderTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "complete-sell-order [Name/Alias/Handle] [myname/x]",
Aliases: []string{"complete-so"},
Short: "Complete a sell-order (must be expired and has at least one bid)",
Long: "Request to complete a sell-order (must be expired and has at least one bid). Can be submitted by either the owner or the highest bidder.",
Example: fmt.Sprintf(
`$ %s tx %s complete-sell-order name myname --%s owner/bidder
$ %s tx %s complete-sell-order alias x --%s owner/bidder`,
version.AppName, dymnstypes.ModuleName, flags.FlagFrom,
version.AppName, dymnstypes.ModuleName, flags.FlagFrom,
),
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) (err error) {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

target := args[1]
var assetType dymnstypes.AssetType

switch strings.ToLower(args[0]) {
case "name", "dym-name", "dymname", "n":
assetType = dymnstypes.TypeName
if !dymnsutils.IsValidDymName(target) {
return fmt.Errorf("input is not a valid Dym-Name: %s", target)
}
case "alias", "handle", "handles", "a":
assetType = dymnstypes.TypeAlias
if !dymnsutils.IsValidAlias(target) {
return fmt.Errorf("input is not a valid Alias: %s", target)
}
default:
return fmt.Errorf("invalid asset type: %s, must be 'Name' or 'Alias'/'Handle'", args[0])
}

participant := clientCtx.GetFromAddress().String()
if participant == "" {
return fmt.Errorf("flag --%s is required", flags.FlagFrom)
}

msg := &dymnstypes.MsgCompleteSellOrder{
AssetId: target,
AssetType: assetType,
Participant: participant,
}

return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}

flags.AddTxFlagsToCmd(cmd)

return cmd
}
4 changes: 2 additions & 2 deletions x/dymns/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ func TestExportThenInitGenesis(t *testing.T) {
// Init genesis state

genState.Params.Misc.EndEpochHookIdentifier = "week" // Change the epoch identifier to test if it is imported correctly
genState.Params.Misc.SellOrderDuration = 9999 * time.Hour
genState.Params.Misc.SellOrderDuration = 6 * 24 * time.Hour

newDymNsKeeper, newBankKeeper, newRollAppKeeper, newCtx := testkeeper.DymNSKeeper(t)
newCtx = newCtx.WithBlockTime(now)
Expand All @@ -290,7 +290,7 @@ func TestExportThenInitGenesis(t *testing.T) {
importedParams := newDymNsKeeper.GetParams(newCtx)
require.Equal(t, genState.Params, importedParams)
require.Equal(t, "week", importedParams.Misc.EndEpochHookIdentifier)
require.Equal(t, 9999*time.Hour, importedParams.Misc.SellOrderDuration)
require.Equal(t, 6*24*time.Hour, importedParams.Misc.SellOrderDuration)
})

t.Run("Dym-Names should be imported correctly", func(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion x/dymns/keeper/alias.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ func (k Keeper) GetEffectiveAliasesByChainId(ctx sdk.Context, chainId string) []
aliasesOfRollApp := k.GetAliasesOfRollAppId(ctx, chainId)

// If the chain-id is a RollApp, must exclude the aliases which being reserved in params.
// Please read the `processActiveAliasSellOrders` method (hooks.go) for more information.
// Please read the `processCompleteSellOrderWithAssetTypeAlias` method (msg_server_complete_sell_order.go) for more information.
reservedAliases := k.GetAllAliasAndChainIdInParams(ctx)
aliasesOfRollApp = slices.DeleteFunc(aliasesOfRollApp, func(a string) bool {
_, found := reservedAliases[a]
Expand Down
11 changes: 9 additions & 2 deletions x/dymns/keeper/dym_name.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,8 +213,15 @@ func (k Keeper) GetAllDymNames(ctx sdk.Context) (list []dymnstypes.DymName) {

// PruneDymName removes a Dym-Name from the KVStore, as well as all related records.
func (k Keeper) PruneDymName(ctx sdk.Context, name string) error {
// remove SO (force, ignore active SO)
k.DeleteSellOrder(ctx, name, dymnstypes.TypeName)
// Force removing any existing SO.
if so := k.GetSellOrder(ctx, name, dymnstypes.TypeName); so != nil {
if so.HighestBid != nil {
if err := k.RefundBid(ctx, *so.HighestBid, dymnstypes.TypeName); err != nil {
return err
}
}
k.DeleteSellOrder(ctx, name, dymnstypes.TypeName)
}

dymName := k.GetDymName(ctx, name)
if dymName == nil {
Expand Down
35 changes: 21 additions & 14 deletions x/dymns/keeper/generic_reverse_lookup.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package keeper

import (
"slices"
"strings"

sdk "github.com/cosmos/cosmos-sdk/types"
dymnstypes "github.com/dymensionxyz/dymension/v3/x/dymns/types"
)

// GenericAddReverseLookupRecord is a utility method that help to add a reverse lookup record.
Expand All @@ -12,26 +14,22 @@ func (k Keeper) GenericAddReverseLookupRecord(
marshaller func([]string) []byte,
unMarshaller func([]byte) []string,
) error {
modifiedRecord := dymnstypes.StringList{
newElement,
}
var modifiedRecord []string

store := ctx.KVStore(k.storeKey)
bz := store.Get(key)
if bz != nil {
existingRecord := unMarshaller(bz)

modifiedRecord = dymnstypes.StringList(existingRecord).Combine(
modifiedRecord,
)

if len(modifiedRecord) == len(existingRecord) {
// no new mapping to add
if slices.Contains(existingRecord, newElement) {
// already exist
return nil
}
}

modifiedRecord = modifiedRecord.Sort()
modifiedRecord = append(existingRecord, newElement)
} else {
modifiedRecord = []string{newElement}
}

bz = marshaller(modifiedRecord)
store.Set(key, bz)
Expand Down Expand Up @@ -69,16 +67,25 @@ func (k Keeper) GenericRemoveReverseLookupRecord(
}

existingRecord := unMarshaller(bz)
modifiedRecord := slices.DeleteFunc(existingRecord, func(r string) bool {
return r == elementToRemove
})

modifiedRecord := dymnstypes.StringList(existingRecord).Exclude([]string{elementToRemove})
if len(existingRecord) == len(modifiedRecord) {
// not found
return nil
}

if len(modifiedRecord) == 0 {
// no more, remove record
store.Delete(key)
return nil
}

modifiedRecord = modifiedRecord.Sort()
// just for safety, sort the records
slices.SortFunc(modifiedRecord, func(a, b string) int {
return strings.Compare(a, b)
})

bz = marshaller(modifiedRecord)
store.Set(key, bz)
Expand Down
Loading

0 comments on commit 3d1c96d

Please sign in to comment.