Skip to content

Commit

Permalink
x/tokenfactory create denom fee (#1435)
Browse files Browse the repository at this point in the history
* params

* tests

* Update x/tokenfactory/keeper/keeper_test.go

Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>

Co-authored-by: Dev Ojha <ValarDragon@users.noreply.github.com>
  • Loading branch information
sunnya97 and ValarDragon committed May 6, 2022
1 parent ddaa866 commit 28761d0
Show file tree
Hide file tree
Showing 22 changed files with 1,136 additions and 122 deletions.
2 changes: 2 additions & 0 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -313,8 +313,10 @@ func (appKeepers *AppKeepers) InitNormalKeepers(
tokenFactoryKeeper := tokenfactorykeeper.NewKeeper(
appCodec,
appKeepers.keys[tokenfactorytypes.StoreKey],
appKeepers.GetSubspace(tokenfactorytypes.ModuleName),
appKeepers.AccountKeeper,
appKeepers.BankKeeper.WithMintCoinsRestriction(tokenfactorytypes.NewTokenFactoryDenomMintCoinsRestriction()),
appKeepers.DistrKeeper,
)
appKeepers.TokenFactoryKeeper = &tokenFactoryKeeper

Expand Down
63 changes: 63 additions & 0 deletions docs/core/proto-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,9 @@
- [osmosis/tokenfactory/v1beta1/authorityMetadata.proto](#osmosis/tokenfactory/v1beta1/authorityMetadata.proto)
- [DenomAuthorityMetadata](#osmosis.tokenfactory.v1beta1.DenomAuthorityMetadata)

- [osmosis/tokenfactory/v1beta1/params.proto](#osmosis/tokenfactory/v1beta1/params.proto)
- [Params](#osmosis.tokenfactory.v1beta1.Params)

- [osmosis/tokenfactory/v1beta1/genesis.proto](#osmosis/tokenfactory/v1beta1/genesis.proto)
- [GenesisDenom](#osmosis.tokenfactory.v1beta1.GenesisDenom)
- [GenesisState](#osmosis.tokenfactory.v1beta1.GenesisState)
Expand All @@ -291,6 +294,8 @@
- [QueryDenomAuthorityMetadataResponse](#osmosis.tokenfactory.v1beta1.QueryDenomAuthorityMetadataResponse)
- [QueryDenomsFromCreatorRequest](#osmosis.tokenfactory.v1beta1.QueryDenomsFromCreatorRequest)
- [QueryDenomsFromCreatorResponse](#osmosis.tokenfactory.v1beta1.QueryDenomsFromCreatorResponse)
- [QueryParamsRequest](#osmosis.tokenfactory.v1beta1.QueryParamsRequest)
- [QueryParamsResponse](#osmosis.tokenfactory.v1beta1.QueryParamsResponse)

- [Query](#osmosis.tokenfactory.v1beta1.Query)

Expand Down Expand Up @@ -3978,6 +3983,37 @@ permission, but is planned to be extended to the future.



<!-- end messages -->

<!-- end enums -->

<!-- end HasExtensions -->

<!-- end services -->



<a name="osmosis/tokenfactory/v1beta1/params.proto"></a>
<p align="right"><a href="#top">Top</a></p>

## osmosis/tokenfactory/v1beta1/params.proto



<a name="osmosis.tokenfactory.v1beta1.Params"></a>

### Params
Params holds parameters for the tokenfactory module


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `denom_creation_fee` | [cosmos.base.v1beta1.Coin](#cosmos.base.v1beta1.Coin) | repeated | |





<!-- end messages -->

<!-- end enums -->
Expand Down Expand Up @@ -4019,6 +4055,7 @@ GenesisState defines the tokenfactory module's genesis state.

| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `params` | [Params](#osmosis.tokenfactory.v1beta1.Params) | | params defines the paramaters of the module. |
| `factory_denoms` | [GenesisDenom](#osmosis.tokenfactory.v1beta1.GenesisDenom) | repeated | |


Expand Down Expand Up @@ -4101,6 +4138,31 @@ GenesisState defines the tokenfactory module's genesis state.




<a name="osmosis.tokenfactory.v1beta1.QueryParamsRequest"></a>

### QueryParamsRequest
QueryParamsRequest is the request type for the Query/Params RPC method.






<a name="osmosis.tokenfactory.v1beta1.QueryParamsResponse"></a>

### QueryParamsResponse
QueryParamsResponse is the response type for the Query/Params RPC method.


| Field | Type | Label | Description |
| ----- | ---- | ----- | ----------- |
| `params` | [Params](#osmosis.tokenfactory.v1beta1.Params) | | params defines the parameters of the module. |





<!-- end messages -->

<!-- end enums -->
Expand All @@ -4115,6 +4177,7 @@ Query defines the gRPC querier service.

| Method Name | Request Type | Response Type | Description | HTTP Verb | Endpoint |
| ----------- | ------------ | ------------- | ------------| ------- | -------- |
| `Params` | [QueryParamsRequest](#osmosis.tokenfactory.v1beta1.QueryParamsRequest) | [QueryParamsResponse](#osmosis.tokenfactory.v1beta1.QueryParamsResponse) | Params returns the total set of minting parameters. | GET|/osmosis/tokenfactory/v1beta1/params|
| `DenomAuthorityMetadata` | [QueryDenomAuthorityMetadataRequest](#osmosis.tokenfactory.v1beta1.QueryDenomAuthorityMetadataRequest) | [QueryDenomAuthorityMetadataResponse](#osmosis.tokenfactory.v1beta1.QueryDenomAuthorityMetadataResponse) | | GET|/osmosis/tokenfactory/v1beta1/denoms/{denom}/authority_metadata|
| `DenomsFromCreator` | [QueryDenomsFromCreatorRequest](#osmosis.tokenfactory.v1beta1.QueryDenomsFromCreatorRequest) | [QueryDenomsFromCreatorResponse](#osmosis.tokenfactory.v1beta1.QueryDenomsFromCreatorResponse) | | GET|/osmosis/tokenfactory/v1beta1/denoms_from_creator/{creator}|

Expand Down
5 changes: 3 additions & 2 deletions proto/osmosis/lockup/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,13 @@ message MsgBeginUnlocking {
message MsgBeginUnlockingResponse { bool success = 1; }

// MsgExtendLockup extends the existing lockup's duration.
// The new duration is longer than the original.
// The new duration is longer than the original.
message MsgExtendLockup {
string owner = 1 [ (gogoproto.moretags) = "yaml:\"owner\"" ];
uint64 ID = 2;

// duration to be set. fails if lower than the current duration, or is unlocking
// duration to be set. fails if lower than the current duration, or is
// unlocking
google.protobuf.Duration duration = 3 [
(gogoproto.nullable) = false,
(gogoproto.stdduration) = true,
Expand Down
6 changes: 4 additions & 2 deletions proto/osmosis/tokenfactory/v1beta1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ package osmosis.tokenfactory.v1beta1;

import "gogoproto/gogo.proto";
import "osmosis/tokenfactory/v1beta1/authorityMetadata.proto";
import "osmosis/tokenfactory/v1beta1/params.proto";

option go_package = "github.com/osmosis-labs/osmosis/v7/x/tokenfactory/types";

// GenesisState defines the tokenfactory module's genesis state.
message GenesisState {
option (gogoproto.equal) = true;
// params defines the paramaters of the module.
Params params = 1 [ (gogoproto.nullable) = false ];

repeated GenesisDenom factory_denoms = 1 [
repeated GenesisDenom factory_denoms = 2 [
(gogoproto.moretags) = "yaml:\"factory_denoms\"",
(gogoproto.nullable) = false
];
Expand Down
18 changes: 18 additions & 0 deletions proto/osmosis/tokenfactory/v1beta1/params.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
syntax = "proto3";
package osmosis.tokenfactory.v1beta1;

import "gogoproto/gogo.proto";
import "osmosis/tokenfactory/v1beta1/authorityMetadata.proto";
import "cosmos_proto/cosmos.proto";
import "cosmos/base/v1beta1/coin.proto";

option go_package = "github.com/osmosis-labs/osmosis/v7/x/tokenfactory/types";

// Params holds parameters for the tokenfactory module
message Params {
repeated cosmos.base.v1beta1.Coin denom_creation_fee = 1 [
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins",
(gogoproto.moretags) = "yaml:\"denom_creation_fee\"",
(gogoproto.nullable) = false
];
}
15 changes: 15 additions & 0 deletions proto/osmosis/tokenfactory/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "cosmos/base/query/v1beta1/pagination.proto";
import "osmosis/tokenfactory/v1beta1/authorityMetadata.proto";
import "osmosis/tokenfactory/v1beta1/params.proto";

option go_package = "github.com/osmosis-labs/osmosis/v7/x/tokenfactory/types";

// Query defines the gRPC querier service.
service Query {
// Params returns the total set of minting parameters.
rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
option (google.api.http).get = "/osmosis/tokenfactory/v1beta1/params";
}

rpc DenomAuthorityMetadata(QueryDenomAuthorityMetadataRequest)
returns (QueryDenomAuthorityMetadataResponse) {
option (google.api.http).get =
Expand All @@ -23,6 +29,15 @@ service Query {
}
}

// QueryParamsRequest is the request type for the Query/Params RPC method.
message QueryParamsRequest {}

// QueryParamsResponse is the response type for the Query/Params RPC method.
message QueryParamsResponse {
// params defines the parameters of the module.
Params params = 1 [ (gogoproto.nullable) = false ];
}

message QueryDenomAuthorityMetadataRequest {
string denom = 1 [ (gogoproto.moretags) = "yaml:\"denom\"" ];
}
Expand Down
3 changes: 2 additions & 1 deletion x/lockup/types/tx.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions x/tokenfactory/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) {
k.CreateModuleAccount(ctx)

k.SetParams(ctx, genState.Params)

for _, genDenom := range genState.GetFactoryDenoms() {
creator, nonce, err := types.DeconstructDenom(genDenom.GetDenom())
if err != nil {
Expand Down Expand Up @@ -49,5 +51,6 @@ func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState {

return &types.GenesisState{
FactoryDenoms: genDenoms,
Params: k.GetParams(ctx),
}
}
40 changes: 19 additions & 21 deletions x/tokenfactory/keeper/admins_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ import (
func (suite *KeeperTestSuite) TestAdminMsgs() {
suite.SetupTest()

addr1 := sdk.AccAddress([]byte("addr1---------------"))
addr2 := sdk.AccAddress([]byte("addr2---------------"))
addr0bal := int64(0)
addr1bal := int64(0)
addr2bal := int64(0)

msgServer := keeper.NewMsgServerImpl(*suite.App.TokenFactoryKeeper)

// Create a denom
res, err := msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(addr1.String(), "bitcoin"))
res, err := msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), "bitcoin"))
suite.Require().NoError(err)
denom := res.GetNewTokenDenom()

Expand All @@ -27,46 +25,46 @@ func (suite *KeeperTestSuite) TestAdminMsgs() {
Denom: res.GetNewTokenDenom(),
})
suite.Require().NoError(err)
suite.Require().Equal(addr1.String(), queryRes.AuthorityMetadata.Admin)
suite.Require().Equal(suite.TestAccs[0].String(), queryRes.AuthorityMetadata.Admin)

// Test minting to admins own account
_, err = msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), types.NewMsgMint(addr1.String(), sdk.NewInt64Coin(denom, 10)))
addr1bal += 10
_, err = msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), types.NewMsgMint(suite.TestAccs[0].String(), sdk.NewInt64Coin(denom, 10)))
addr0bal += 10
suite.Require().NoError(err)
suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, addr1, denom).Amount.Int64() == addr1bal, suite.App.BankKeeper.GetBalance(suite.Ctx, addr1, denom))
suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[0], denom).Amount.Int64() == addr0bal, suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[0], denom))

// // Test force transferring
// _, err = msgServer.ForceTransfer(sdk.WrapSDKContext(suite.Ctx), types.NewMsgForceTransfer(addr1.String(), sdk.NewInt64Coin(denom, 5), addr2.String(), addr1.String()))
// _, err = msgServer.ForceTransfer(sdk.WrapSDKContext(suite.Ctx), types.NewMsgForceTransfer(suite.TestAccs[0].String(), sdk.NewInt64Coin(denom, 5), suite.TestAccs[1].String(), suite.TestAccs[0].String()))
// suite.Require().NoError(err)
// suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, addr1, denom).IsEqual(sdk.NewInt64Coin(denom, 15)))
// suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, addr2, denom).IsEqual(sdk.NewInt64Coin(denom, 5)))
// suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[0], denom).IsEqual(sdk.NewInt64Coin(denom, 15)))
// suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[1], denom).IsEqual(sdk.NewInt64Coin(denom, 5)))

// Test burning from own account
_, err = msgServer.Burn(sdk.WrapSDKContext(suite.Ctx), types.NewMsgBurn(addr1.String(), sdk.NewInt64Coin(denom, 5)))
addr1bal -= 5
_, err = msgServer.Burn(sdk.WrapSDKContext(suite.Ctx), types.NewMsgBurn(suite.TestAccs[0].String(), sdk.NewInt64Coin(denom, 5)))
addr0bal -= 5
suite.Require().NoError(err)
suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, addr2, denom).Amount.Int64() == addr2bal)
suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[1], denom).Amount.Int64() == addr1bal)

// Test Change Admin
_, err = msgServer.ChangeAdmin(sdk.WrapSDKContext(suite.Ctx), types.NewMsgChangeAdmin(addr1.String(), denom, addr2.String()))
_, err = msgServer.ChangeAdmin(sdk.WrapSDKContext(suite.Ctx), types.NewMsgChangeAdmin(suite.TestAccs[0].String(), denom, suite.TestAccs[1].String()))
queryRes, err = suite.queryClient.DenomAuthorityMetadata(suite.Ctx.Context(), &types.QueryDenomAuthorityMetadataRequest{
Denom: res.GetNewTokenDenom(),
})
suite.Require().NoError(err)
suite.Require().Equal(addr2.String(), queryRes.AuthorityMetadata.Admin)
suite.Require().Equal(suite.TestAccs[1].String(), queryRes.AuthorityMetadata.Admin)

// Make sure old admin can no longer do actions
_, err = msgServer.Burn(sdk.WrapSDKContext(suite.Ctx), types.NewMsgBurn(addr1.String(), sdk.NewInt64Coin(denom, 5)))
_, err = msgServer.Burn(sdk.WrapSDKContext(suite.Ctx), types.NewMsgBurn(suite.TestAccs[0].String(), sdk.NewInt64Coin(denom, 5)))
suite.Require().Error(err)

// Make sure the new admin works
_, err = msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), types.NewMsgMint(addr2.String(), sdk.NewInt64Coin(denom, 5)))
addr2bal += 5
_, err = msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), types.NewMsgMint(suite.TestAccs[1].String(), sdk.NewInt64Coin(denom, 5)))
addr1bal += 5
suite.Require().NoError(err)
suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, addr2, denom).Amount.Int64() == addr2bal)
suite.Require().True(suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[1], denom).Amount.Int64() == addr1bal)

// Try setting admin to empty
_, err = msgServer.ChangeAdmin(sdk.WrapSDKContext(suite.Ctx), types.NewMsgChangeAdmin(addr2.String(), denom, ""))
_, err = msgServer.ChangeAdmin(sdk.WrapSDKContext(suite.Ctx), types.NewMsgChangeAdmin(suite.TestAccs[1].String(), denom, ""))
suite.Require().NoError(err)
queryRes, err = suite.queryClient.DenomAuthorityMetadata(suite.Ctx.Context(), &types.QueryDenomAuthorityMetadataRequest{
Denom: res.GetNewTokenDenom(),
Expand Down
10 changes: 10 additions & 0 deletions x/tokenfactory/keeper/createdenom.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ import (

// ConvertToBaseToken converts a fee amount in a whitelisted fee token to the base fee token amount
func (k Keeper) CreateDenom(ctx sdk.Context, creatorAddr string, denomNonce string) (newTokenDenom string, err error) {
// Send creation fee to community pool
creationFee := k.GetParams(ctx).DenomCreationFee
accAddr, err := sdk.AccAddressFromBech32(creatorAddr)
if err != nil {
return "", err
}
if err := k.distrKeeper.FundCommunityPool(ctx, creationFee, accAddr); err != nil {
return "", err
}

denom, err := types.GetTokenDenom(creatorAddr, denomNonce)
if err != nil {
return "", err
Expand Down
26 changes: 16 additions & 10 deletions x/tokenfactory/keeper/createdenom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ import (
func (suite *KeeperTestSuite) TestMsgCreateDenom() {
suite.SetupTest()

addr1 := sdk.AccAddress([]byte("addr1---------------"))

msgServer := keeper.NewMsgServerImpl(*suite.App.TokenFactoryKeeper)

denomCreationFee := suite.App.TokenFactoryKeeper.GetParams(suite.Ctx).DenomCreationFee

// Get balance of acc 0 before creating a denom
preCreateBalance := suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[0], denomCreationFee[0].Denom)

// Creating a denom should work
res, err := msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(addr1.String(), "bitcoin"))
res, err := msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), "bitcoin"))
suite.Require().NoError(err)
suite.Require().NotEmpty(res.GetNewTokenDenom())

Expand All @@ -24,27 +27,30 @@ func (suite *KeeperTestSuite) TestMsgCreateDenom() {
Denom: res.GetNewTokenDenom(),
})
suite.Require().NoError(err)
suite.Require().Equal(addr1.String(), queryRes.AuthorityMetadata.Admin)
suite.Require().Equal(suite.TestAccs[0].String(), queryRes.AuthorityMetadata.Admin)

// Make sure that creation fee was deducted
postCreateBalance := suite.App.BankKeeper.GetBalance(suite.Ctx, suite.TestAccs[0], suite.App.TokenFactoryKeeper.GetParams(suite.Ctx).DenomCreationFee[0].Denom)
suite.Require().True(preCreateBalance.Sub(postCreateBalance).IsEqual(denomCreationFee[0]))

// Make sure that a second version of the same denom can't be recreated
res, err = msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(addr1.String(), "bitcoin"))
res, err = msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), "bitcoin"))
suite.Require().Error(err)

// Creating a second denom should work
res, err = msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(addr1.String(), "litecoin"))
res, err = msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[0].String(), "litecoin"))
suite.Require().NoError(err)
suite.Require().NotEmpty(res.GetNewTokenDenom())

// Try querying all the denoms created by addr1
// Try querying all the denoms created by suite.TestAccs[0]
queryRes2, err := suite.queryClient.DenomsFromCreator(suite.Ctx.Context(), &types.QueryDenomsFromCreatorRequest{
Creator: addr1.String(),
Creator: suite.TestAccs[0].String(),
})
suite.Require().NoError(err)
suite.Require().Len(queryRes2.Denoms, 2)

// Make sure that a second account can create a denom with the same nonce
addr2 := sdk.AccAddress([]byte("addr2---------------"))
res, err = msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(addr2.String(), "bitcoin"))
res, err = msgServer.CreateDenom(sdk.WrapSDKContext(suite.Ctx), types.NewMsgCreateDenom(suite.TestAccs[1].String(), "bitcoin"))
suite.Require().NoError(err)
suite.Require().NotEmpty(res.GetNewTokenDenom())

Expand Down
7 changes: 7 additions & 0 deletions x/tokenfactory/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ import (

var _ types.QueryServer = Keeper{}

func (k Keeper) Params(ctx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)
params := k.GetParams(sdkCtx)

return &types.QueryParamsResponse{Params: params}, nil
}

func (k Keeper) DenomAuthorityMetadata(ctx context.Context, req *types.QueryDenomAuthorityMetadataRequest) (*types.QueryDenomAuthorityMetadataResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(ctx)

Expand Down
Loading

0 comments on commit 28761d0

Please sign in to comment.