Skip to content

Commit

Permalink
feat: ADR-010: Mutual DTag exchange between users. (#656)
Browse files Browse the repository at this point in the history
## Description

This PR implements the changes approved in ADR 010.
Closes: #643

<!-- Add a description of the changes that this PR introduces and the files that
are the most critical to review. -->

---

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR Targeting](https://github.com/desmos-labs/desmos/blob/master/CONTRIBUTING.md#pr-targeting))
- [x] provided a link to the relevant issue or specification
- [x] followed the guidelines for [building modules](https://docs.cosmos.network/v0.44/building-modules/intro.html)
- [x] included the necessary unit and integration [tests](https://github.com/desmos-labs/desmos/blob/master/CONTRIBUTING.md#testing)
- [x] added a changelog entry to `CHANGELOG.md`
- [x] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [ ] updated the relevant documentation or specification
- [ ] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
  • Loading branch information
leobragaz authored Oct 20, 2021
1 parent 6ffb714 commit 71960c8
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type: feat
module: x/profiles
pull_request: 646
description: Added the possibility to mutually exchange DTags between users
backward_compatible: true
date: 2021-10-19T15:25:15.721604Z
4 changes: 1 addition & 3 deletions docs/architecture/adr-010-mutual-dtags-exchange.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,7 @@ There are no backwards compatibility issues related to these changes.
## Test Cases [optional]
The following tests cases needs to be added:
1. Accepting a DTag transfer request specifying the receiver DTag works properly;
2. Swapping two profiles DTags works properly;
3. Replacing DTag with valid DTag works properly;
4. Replacing DTag with invalid DTag returns an error.
2. Swapping two profiles DTags works properly.
## References
Expand Down
8 changes: 5 additions & 3 deletions x/profiles/client/cli/cli_dtag_requests.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@ func GetCmdCancelDTagTransfer() *cobra.Command {
// GetCmdAcceptDTagTransfer returns the command to accept a DTag transfer request
func GetCmdAcceptDTagTransfer() *cobra.Command {
cmd := &cobra.Command{
Use: "accept-dtag-transfer-request [newDTag] [address]",
Short: "Accept a DTag transfer request made by the user with the given address",
Args: cobra.ExactArgs(2),
Use: "accept-dtag-transfer-request [DTag] [address]",
Short: `Accept a DTag transfer request made by the user with the given address.
When accepting the request, you can specify the request recipient DTag as your new DTag.
If this happens, your DTag and the other user's one will be effectively swapped.`,
Args: cobra.ExactArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
Expand Down
24 changes: 15 additions & 9 deletions x/profiles/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,10 @@ func (k Keeper) Logger(ctx sdk.Context) log.Logger {
return ctx.Logger().With("module", "x/"+types.ModuleName)
}

// StoreProfile stores the given profile inside the current context.
// storeProfileWithoutDTagCheck stores the given profile inside the current context
// without checking if another profile with the same DTag already exists.
// It assumes that the given profile has already been validated.
// It returns an error if a profile with the same DTag from a different creator already exists
func (k Keeper) StoreProfile(ctx sdk.Context, profile *types.Profile) error {
addr := k.GetAddressFromDTag(ctx, profile.DTag)
if addr != "" && addr != profile.GetAddress().String() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest,
"a profile with DTag %s has already been created", profile.DTag)
}

func (k Keeper) storeProfileWithoutDTagCheck(ctx sdk.Context, profile *types.Profile) error {
store := ctx.KVStore(k.storeKey)

oldProfile, found, err := k.GetProfile(ctx, profile.GetAddress().String())
Expand All @@ -99,6 +93,18 @@ func (k Keeper) StoreProfile(ctx sdk.Context, profile *types.Profile) error {
return nil
}

// StoreProfile stores the given profile inside the current context.
// It assumes that the given profile has already been validated.
// It returns an error if a profile with the same DTag from a different creator already exists
func (k Keeper) StoreProfile(ctx sdk.Context, profile *types.Profile) error {
addr := k.GetAddressFromDTag(ctx, profile.DTag)
if addr != "" && addr != profile.GetAddress().String() {
return sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest,
"a profile with DTag %s has already been created", profile.DTag)
}
return k.storeProfileWithoutDTagCheck(ctx, profile)
}

// GetProfile returns the profile corresponding to the given address inside the current context.
func (k Keeper) GetProfile(ctx sdk.Context, address string) (profile *types.Profile, found bool, err error) {
sdkAcc, err := sdk.AccAddressFromBech32(address)
Expand Down
18 changes: 12 additions & 6 deletions x/profiles/keeper/msg_server_dtag_transfers.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,18 +105,24 @@ func (k msgServer) AcceptDTagTransferRequest(goCtx context.Context, msg *types.M
return nil, sdkerrors.Wrap(sdkerrors.ErrInvalidRequest, err.Error())
}

// Store the profile
err = k.StoreProfile(ctx, currentOwnerProfile)
if err != nil {
return nil, err
}

// Check for an existent profile of the receiving user
receiverProfile, exist, err := k.GetProfile(ctx, msg.Sender)
if err != nil {
return nil, err
}

if exist && msg.NewDTag == receiverProfile.DTag {
err = k.storeProfileWithoutDTagCheck(ctx, currentOwnerProfile)
if err != nil {
return nil, err
}
} else {
err = k.StoreProfile(ctx, currentOwnerProfile)
if err != nil {
return nil, err
}
}

if !exist {
add, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
Expand Down
42 changes: 42 additions & 0 deletions x/profiles/keeper/msg_server_dtag_transfers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ func (suite *KeeperTestSuite) TestMsgServer_AcceptDTagTransfer() {
testCases := []struct {
name string
store func(ctx sdk.Context)
check func(ctx sdk.Context)
msg *types.MsgAcceptDTagTransferRequest
shouldErr bool
expEvents sdk.Events
Expand Down Expand Up @@ -313,6 +314,44 @@ func (suite *KeeperTestSuite) TestMsgServer_AcceptDTagTransfer() {
),
},
},
{
name: "DTag swapped correctly",
store: func(ctx sdk.Context) {
request := types.NewDTagTransferRequest(
"receiverDTag",
"cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
"cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns",
)
receiverProfile := testutil.ProfileFromAddr(request.Receiver)
receiverProfile.DTag = "receiverDTag"
senderProfile := testutil.ProfileFromAddr(request.Sender)
senderProfile.DTag = "senderDTag"
suite.Require().NoError(suite.k.StoreProfile(ctx, senderProfile))
suite.Require().NoError(suite.k.StoreProfile(ctx, receiverProfile))
suite.Require().NoError(suite.k.SaveDTagTransferRequest(ctx, request))
},
msg: types.NewMsgAcceptDTagTransferRequest(
"senderDTag",
"cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47",
"cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns",
),
expEvents: sdk.Events{
sdk.NewEvent(
types.EventTypeDTagTransferAccept,
sdk.NewAttribute(types.AttributeDTagToTrade, "receiverDTag"),
sdk.NewAttribute(types.AttributeNewDTag, "senderDTag"),
sdk.NewAttribute(types.AttributeRequestSender, "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47"),
sdk.NewAttribute(types.AttributeRequestReceiver, "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns"),
),
},
check: func(ctx sdk.Context) {
senderProfile, _, _ := suite.k.GetProfile(ctx, "cosmos1y54exmx84cqtasvjnskf9f63djuuj68p7hqf47")
receiverProfile, _, _ := suite.k.GetProfile(ctx, "cosmos1cjf97gpzwmaf30pzvaargfgr884mpp5ak8f7ns")

suite.Require().Equal(senderProfile.DTag, "receiverDTag")
suite.Require().Equal(receiverProfile.DTag, "senderDTag")
},
},
}

for _, tc := range testCases {
Expand All @@ -332,6 +371,9 @@ func (suite *KeeperTestSuite) TestMsgServer_AcceptDTagTransfer() {
} else {
suite.Require().NoError(err)
suite.Require().Equal(tc.expEvents, ctx.EventManager().Events())
if tc.check != nil {
tc.check(ctx)
}
}
})
}
Expand Down

0 comments on commit 71960c8

Please sign in to comment.