Skip to content

Commit

Permalink
feat(tokenfactory): tx msgs for mint and burn (#1620)
Browse files Browse the repository at this point in the history
  • Loading branch information
Unique-Divine authored Oct 6, 2023
1 parent 8f9c4ca commit 54ad5f5
Show file tree
Hide file tree
Showing 26 changed files with 3,704 additions and 313 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
State transitions, collections, genesis import and export, and app wiring
* [#1607](https://github.com/NibiruChain/nibiru/pull/1607) - Token factory
transaction messages for CreateDenom, ChangeAdmin, and UpdateModuleParams
* [#1620](https://github.com/NibiruChain/nibiru/pull/1620) - Token factory
transaction messages for Mint and Burn

### State Machine Breaking

Expand Down
6 changes: 3 additions & 3 deletions app/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ func (app *NibiruApp) InitKeepers(
appCodec,
keys[banktypes.StoreKey],
app.AccountKeeper,
blockedAddresses(),
BlockedAddresses(),
authtypes.NewModuleAddress(govtypes.ModuleName).String(),
)
app.stakingKeeper = stakingkeeper.NewKeeper(
Expand Down Expand Up @@ -679,8 +679,8 @@ func orderedModuleNames() []string {
}
}

// blockedAddresses returns all the app's blocked account addresses.
func blockedAddresses() map[string]bool {
// BlockedAddresses returns all the app's blocked account addresses.
func BlockedAddresses() map[string]bool {
modAccAddrs := make(map[string]bool)
for acc := range maccPerms {
modAccAddrs[authtypes.NewModuleAddress(acc).String()] = true
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
cosmossdk.io/math v1.1.2
github.com/CosmWasm/wasmd v0.40.2
github.com/CosmWasm/wasmvm v1.4.0
github.com/MakeNowJust/heredoc/v2 v2.0.1
github.com/NibiruChain/collections v0.3.0
github.com/armon/go-metrics v0.4.1
github.com/cometbft/cometbft v0.37.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,8 @@ github.com/DATA-DOG/go-sqlmock v1.3.3/go.mod h1:f/Ixk793poVmq4qj/V1dPUg2JEAKC73Q
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/DataDog/zstd v1.5.0/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/MakeNowJust/heredoc/v2 v2.0.1 h1:rlCHh70XXXv7toz95ajQWOWQnN4WNLt0TdpZYIR/J6A=
github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM=
github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=
github.com/NibiruChain/collections v0.3.0 h1:DB2RPzzgcHk35lRXuJ1cPOezweAZ6/c/Guq3bAo4W6w=
github.com/NibiruChain/collections v0.3.0/go.mod h1:tKTlBL+Cs1oJnS4tT9MIaFWr7BWsUXrc7KPzP1LxRBo=
Expand Down
28 changes: 27 additions & 1 deletion proto/nibiru/tokenfactory/v1/event.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ syntax = "proto3";

package nibiru.tokenfactory.v1;

import "cosmos/bank/v1beta1/bank.proto";
import "cosmos/base/v1beta1/coin.proto";
import "gogoproto/gogo.proto";

option go_package = "github.com/NibiruChain/nibiru/x/tokenfactory/types";

message EventCreateDenom {
Expand All @@ -11,6 +15,28 @@ message EventCreateDenom {

message EventChangeAdmin {
string denom = 1;
string old_admin = 3;
string new_admin = 2;
string old_admin = 3;
}

message EventMint {
cosmos.base.v1beta1.Coin coin = 1
[ (gogoproto.moretags) = "yaml:\"coin\"", (gogoproto.nullable) = false ];
string to_addr = 2;
string caller = 3;
}

message EventBurn {
cosmos.base.v1beta1.Coin coin = 1
[ (gogoproto.moretags) = "yaml:\"coin\"", (gogoproto.nullable) = false ];
string from_addr = 2;
string caller = 3;
}

message EventSetDenomMetadata {
string denom = 1;
// Metadata: Official x/bank metadata for the denom. All token factory denoms
// are standard, native assets. The "metadata.base" is the denom.
cosmos.bank.v1beta1.Metadata metadata = 2 [ (gogoproto.nullable) = false ];
string caller = 3;
}
53 changes: 49 additions & 4 deletions proto/nibiru/tokenfactory/v1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "gogoproto/gogo.proto";
import "cosmos/base/v1beta1/coin.proto";
import "cosmos/msg/v1/msg.proto";
import "cosmos_proto/cosmos.proto";
import "cosmos/bank/v1beta1/bank.proto";
import "nibiru/tokenfactory/v1/state.proto";

option go_package = "github.com/NibiruChain/nibiru/x/tokenfactory/types";
Expand All @@ -19,10 +20,10 @@ service Msg {
// module parameters.
rpc UpdateModuleParams(MsgUpdateModuleParams)
returns (MsgUpdateModuleParamsResponse);
// TODO MsgMint
// TODO MsgBurn
// TODO MsgSetDenomMetadata
// TODO MsgForceTransfer
rpc Mint(MsgMint) returns (MsgMintResponse);
rpc Burn(MsgBurn) returns (MsgBurnResponse);
rpc SetDenomMetadata(MsgSetDenomMetadata)
returns (MsgSetDenomMetadataResponse);
}

// MsgCreateDenom: sdk.Msg that registers an a token factory denom.
Expand Down Expand Up @@ -69,3 +70,47 @@ message MsgUpdateModuleParams {
// MsgUpdateModuleParamsResponse is the gRPC response for the
// MsgUpdateModuleParams TxMsg.
message MsgUpdateModuleParamsResponse {}

// MsgMint: sdk.Msg (TxMsg) where an denom admin mints more of the token.
message MsgMint {
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
// coin: The denom identifier and amount to mint.
cosmos.base.v1beta1.Coin coin = 2
[ (gogoproto.moretags) = "yaml:\"coin\"", (gogoproto.nullable) = false ];
// mint_to_addr: An address to which tokens will be minted. If blank,
// tokens are minted to the "sender".
string mint_to = 3 [
(gogoproto.moretags) = "yaml:\"mint_to\"",
(gogoproto.nullable) = true
];
}

message MsgMintResponse { string mint_to = 1; }

// MsgBurn: sdk.Msg (TxMsg) where a denom admin burns some of the token.
// The reason that the sender isn't automatically the "burn_from" address
// is to support smart contracts (primary use case). In this situation, the
// contract is the message signer and sender, while "burn_from" is based on the
// contract logic.
message MsgBurn {
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
// coin: The denom identifier and amount to burn.
cosmos.base.v1beta1.Coin coin = 2
[ (gogoproto.moretags) = "yaml:\"coin\"", (gogoproto.nullable) = false ];
// burn_from: The address from which tokens will be burned.
string burn_from = 3 [ (gogoproto.moretags) = "yaml:\"burn_from\"" ];
}

message MsgBurnResponse {}

// MsgSetDenomMetadata: sdk.Msg (TxMsg) enabling the denom admin to change its
// bank metadata.
message MsgSetDenomMetadata {
string sender = 1 [ (gogoproto.moretags) = "yaml:\"sender\"" ];

// Metadata: Official x/bank metadata for the denom. All token factory denoms
// are standard, native assets. The "metadata.base" is the denom.
cosmos.bank.v1beta1.Metadata metadata = 2 [ (gogoproto.nullable) = false ];
}

message MsgSetDenomMetadataResponse {}
70 changes: 66 additions & 4 deletions x/tokenfactory/cli/cli_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cli_test

import (
"fmt"
"testing"

"github.com/stretchr/testify/suite"
Expand All @@ -12,6 +13,8 @@ import (
"github.com/NibiruChain/nibiru/x/common/testutil/testapp"
"github.com/NibiruChain/nibiru/x/tokenfactory/cli"
"github.com/NibiruChain/nibiru/x/tokenfactory/types"

sdk "github.com/cosmos/cosmos-sdk/types"
)

var _ suite.SetupAllSuite = (*IntegrationTestSuite)(nil)
Expand All @@ -28,9 +31,11 @@ func TestIntegrationTestSuite(t *testing.T) {
suite.Run(t, new(IntegrationTestSuite))
}

// TestTokenFactory: Runs the test suite with a deterministic order.
func (s *IntegrationTestSuite) TestTokenFactory() {
s.T().Run("CreateDenomTest", s.CreateDenomTest)
s.T().Run("ChangeAdminTest", s.ChangeAdminTest)
s.Run("CreateDenomTest", s.CreateDenomTest)
s.Run("MintBurnTest", s.MintBurnTest)
s.Run("ChangeAdminTest", s.ChangeAdminTest)
}

func (s *IntegrationTestSuite) SetupSuite() {
Expand Down Expand Up @@ -59,7 +64,7 @@ func (s *IntegrationTestSuite) SetupSuite() {
s.NoError(s.network.WaitForNextBlock())
}

func (s *IntegrationTestSuite) CreateDenomTest(t *testing.T) {
func (s *IntegrationTestSuite) CreateDenomTest() {
creator := s.val.Address
createDenom := func(subdenom string, wantErr bool) {
_, err := s.network.ExecTxCmd(
Expand Down Expand Up @@ -93,7 +98,64 @@ func (s *IntegrationTestSuite) CreateDenomTest(t *testing.T) {
s.ElementsMatch(denoms, wantDenoms)
}

func (s *IntegrationTestSuite) ChangeAdminTest(t *testing.T) {
func (s *IntegrationTestSuite) MintBurnTest() {
creator := s.val.Address
mint := func(coin string, mintTo string, wantErr bool) {
mintToArg := fmt.Sprintf("--mint-to=%s", mintTo)
_, err := s.network.ExecTxCmd(
cli.NewTxCmd(), creator, []string{"mint", coin, mintToArg})
if wantErr {
s.Require().Error(err)
return
}
s.Require().NoError(err)
s.NoError(s.network.WaitForNextBlock())
}

burn := func(coin string, burnFrom string, wantErr bool) {
burnFromArg := fmt.Sprintf("--burn-from=%s", burnFrom)
_, err := s.network.ExecTxCmd(
cli.NewTxCmd(), creator, []string{"burn", coin, burnFromArg})
if wantErr {
s.Require().Error(err)
return
}
s.Require().NoError(err)
s.NoError(s.network.WaitForNextBlock())
}

t := s.T()
t.Log("mint successfully")
denom := types.TFDenom{
Creator: creator.String(),
Subdenom: "nusd",
}
coin := sdk.NewInt64Coin(denom.String(), 420)
wantErr := false
mint(coin.String(), creator.String(), wantErr) // happy

t.Log("want error: unregistered denom")
coin.Denom = "notadenom"
wantErr = true
mint(coin.String(), creator.String(), wantErr)
burn(coin.String(), creator.String(), wantErr)

t.Log("want error: invalid coin")
mint("notacoin_231,,", creator.String(), wantErr)
burn("notacoin_231,,", creator.String(), wantErr)

t.Log(`want error: unable to parse "mint-to" or "burn-from"`)
coin.Denom = denom.String()
mint(coin.String(), "invalidAddr", wantErr)
burn(coin.String(), "invalidAddr", wantErr)

t.Log("burn successfully")
coin.Denom = denom.String()
wantErr = false
burn(coin.String(), creator.String(), wantErr) // happy
}

func (s *IntegrationTestSuite) ChangeAdminTest() {
creator := s.val.Address
admin := creator
newAdmin := testutil.AccAddress()
Expand Down
Loading

0 comments on commit 54ad5f5

Please sign in to comment.