Skip to content

Commit

Permalink
basic whitelist logic
Browse files Browse the repository at this point in the history
  • Loading branch information
jcompagni10 committed Jun 19, 2024
1 parent a85d659 commit e855c8f
Show file tree
Hide file tree
Showing 19 changed files with 1,002 additions and 176 deletions.
39 changes: 39 additions & 0 deletions proto/osmosis/tokenfactory/params.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
syntax = "proto3";
package osmosis.tokenfactory;

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

option go_package = "github.com/neutron-org/neutron/v4/x/tokenfactory/types";

message HookWhitelist {
uint64 code_id = 1 [(gogoproto.customname) = "CodeID"];
string denom_creator = 2;
}

// Params defines the parameters for the tokenfactory module.
message Params {
// DenomCreationFee defines the fee to be charged on the creation of a new
// denom. The fee is drawn from the MsgCreateDenom's sender account, and
// transferred to the community pool.
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
];

// DenomCreationGasConsume defines the gas cost for creating a new denom.
// This is intended as a spam deterrence mechanism.
//
// See: https://github.com/CosmWasm/token-factory/issues/11
uint64 denom_creation_gas_consume = 2 [
(gogoproto.moretags) = "yaml:\"denom_creation_gas_consume\"",
(gogoproto.nullable) = true
];

// FeeCollectorAddress is the address where fees collected from denom creation
// are sent to
string fee_collector_address = 3;
repeated HookWhitelist whitelisted_hooks = 4;
}
2 changes: 1 addition & 1 deletion proto/osmosis/tokenfactory/v1beta1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ syntax = "proto3";
package osmosis.tokenfactory.v1beta1;

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

option go_package = "github.com/neutron-org/neutron/v4/x/tokenfactory/types";

Expand Down
2 changes: 1 addition & 1 deletion proto/osmosis/tokenfactory/v1beta1/params.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import "cosmos/base/v1beta1/coin.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";

option go_package = "github.com/neutron-org/neutron/v4/x/tokenfactory/types";
option go_package = "github.com/neutron-org/neutron/v4/x/tokenfactory/types/v1beta1";

// Params defines the parameters for the tokenfactory module.
message Params {
Expand Down
2 changes: 1 addition & 1 deletion proto/osmosis/tokenfactory/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package osmosis.tokenfactory.v1beta1;

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

option go_package = "github.com/neutron-org/neutron/v4/x/tokenfactory/types";

Expand Down
2 changes: 1 addition & 1 deletion proto/osmosis/tokenfactory/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import "cosmos/base/v1beta1/coin.proto";
import "cosmos/msg/v1/msg.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
import "osmosis/tokenfactory/v1beta1/params.proto";
import "osmosis/tokenfactory/params.proto";

option go_package = "github.com/neutron-org/neutron/v4/x/tokenfactory/types";

Expand Down
1 change: 1 addition & 0 deletions wasmbinding/test/custom_message_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ func (suite *CustomMessengerTestSuite) SetupTest() {
sdk.NewCoins(sdk.NewInt64Coin(params.DefaultDenom, 10_000_000)),
0,
FeeCollectorAddress,
tokenfactorytypes.DefaultWhitelistedHooks,
))
suite.Require().NoError(err)

Expand Down
1 change: 1 addition & 0 deletions wasmbinding/test/custom_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,7 @@ func (suite *CustomQuerierTestSuite) TestDenomAdmin() {
sdk.NewCoins(sdk.NewInt64Coin(params.DefaultDenom, 10_000_000)),
0,
FeeCollectorAddress,
tokenfactorytypes.DefaultWhitelistedHooks,
))
suite.Require().NoError(err)

Expand Down
5 changes: 5 additions & 0 deletions x/tokenfactory/keeper/before_send.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ func (k Keeper) callBeforeSendListener(ctx context.Context, from, to sdk.AccAddr
return err
}

// Do not invoke hook if denom is not whitelisted and `from` is a module
if !k.isHookWhitelisted(c, coin.Denom, cwAddr) {
return nil
}

var msgBz []byte

// get msgBz, either BlockBeforeSend or TrackBeforeSend
Expand Down
18 changes: 18 additions & 0 deletions x/tokenfactory/keeper/before_send_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,24 @@ func (suite *KeeperTestSuite) TestTrackBeforeSendWasm() {

queryResp, err := suite.GetNeutronZoneApp(suite.ChainA).WasmKeeper.QuerySmart(suite.ChainA.GetContext(), cosmwasmAddress, []byte(`{"total_supply_at":{}}`))
suite.Require().NoError(err)

// Contract has not been called because it is not whitelisted
suite.Require().Equal("\"0\"", string(queryResp))

// Whitelist the hook
params := types.DefaultParams()
params.WhitelistedHooks = []*types.HookWhitelist{{DenomCreator: senderAddress.String(), CodeID: codeID}}
err = suite.GetNeutronZoneApp(suite.ChainA).TokenFactoryKeeper.SetParams(suite.ChainA.GetContext(), params)
suite.Require().NoError(err)

// mint tokens again
_, err = suite.msgServer.Mint(suite.ChainA.GetContext(), types.NewMsgMint(suite.TestAccs[0].String(), tokenToSend))
suite.Require().NoError(err)

queryResp, err = suite.GetNeutronZoneApp(suite.ChainA).WasmKeeper.QuerySmart(suite.ChainA.GetContext(), cosmwasmAddress, []byte(`{"total_supply_at":{}}`))
suite.Require().NoError(err)

// Whitelisted contract has now been called
suite.Require().Equal("\"100\"", string(queryResp))
})
}
Expand Down
1 change: 1 addition & 0 deletions x/tokenfactory/keeper/keeper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ func (suite *KeeperTestSuite) Setup() {
sdktypes.NewCoins(sdktypes.NewInt64Coin(params.DefaultDenom, TopUpCoinsAmount)),
0,
FeeCollectorAddress,
types.DefaultWhitelistedHooks,
))
suite.Require().NoError(err)

Expand Down
21 changes: 21 additions & 0 deletions x/tokenfactory/keeper/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,24 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error {
store.Set(types.ParamsKey, bz)
return nil
}

func (k Keeper) isHookWhitelisted(ctx sdk.Context, denom string, contractAddress sdk.AccAddress) bool {
contractInfo := k.contractKeeper.GetContractInfo(ctx, contractAddress)
if contractInfo == nil {
return false
}
codeID := contractInfo.CodeID
whitelistedHooks := k.GetParams(ctx).WhitelistedHooks
denomCreator, _, err := types.DeconstructDenom(denom)
if err != nil {
return false
}

for _, hook := range whitelistedHooks {
if hook.CodeID == codeID && hook.DenomCreator == denomCreator {
return true
}
}

return false
}
2 changes: 2 additions & 0 deletions x/tokenfactory/types/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package types
import (
"context"

wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
sdk "github.com/cosmos/cosmos-sdk/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)
Expand Down Expand Up @@ -36,4 +37,5 @@ type BankHooks interface {

type ContractKeeper interface {
Sudo(ctx context.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error)
GetContractInfo(ctx context.Context, contractAddress sdk.AccAddress) *wasmtypes.ContractInfo
}
41 changes: 21 additions & 20 deletions x/tokenfactory/types/genesis.pb.go

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

33 changes: 31 additions & 2 deletions x/tokenfactory/types/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,28 +12,31 @@ var (
KeyDenomCreationFee = []byte("DenomCreationFee")
KeyDenomCreationGasConsume = []byte("DenomCreationGasConsume")
KeyFeeCollectorAddress = []byte("FeeCollectorAddress")
KeyWhitelistedHooks = []byte("WhitelistedHooks")
// We don't want to charge users for denom creation
DefaultDenomCreationFee sdk.Coins
DefaultDenomCreationGasConsume uint64
DefaultFeeCollectorAddress = ""
DefaultWhitelistedHooks = []*HookWhitelist{}
)

// ParamKeyTable the param key table for tokenfactory module.
func ParamKeyTable() paramtypes.KeyTable {
return paramtypes.NewKeyTable().RegisterParamSet(&Params{})
}

func NewParams(denomCreationFee sdk.Coins, denomCreationGasConsume uint64, feeCollectorAddress string) Params {
func NewParams(denomCreationFee sdk.Coins, denomCreationGasConsume uint64, feeCollectorAddress string, whitelistedHooks []*HookWhitelist) Params {
return Params{
DenomCreationFee: denomCreationFee,
DenomCreationGasConsume: denomCreationGasConsume,
FeeCollectorAddress: feeCollectorAddress,
WhitelistedHooks: whitelistedHooks,
}
}

// DefaultParams returns a default set of parameters
func DefaultParams() Params {
return NewParams(DefaultDenomCreationFee, DefaultDenomCreationGasConsume, DefaultFeeCollectorAddress)
return NewParams(DefaultDenomCreationFee, DefaultDenomCreationGasConsume, DefaultFeeCollectorAddress, DefaultWhitelistedHooks)
}

// Validate validates params
Expand All @@ -54,6 +57,10 @@ func (p Params) Validate() error {
return fmt.Errorf("failed to validate params: %w", err)
}

if err := validateWhitelistedHooks(p.WhitelistedHooks); err != nil {
return fmt.Errorf("failed to validate params: %w", err)
}

return nil
}

Expand All @@ -63,6 +70,7 @@ func (p *Params) ParamSetPairs() paramtypes.ParamSetPairs {
paramtypes.NewParamSetPair(KeyDenomCreationFee, &p.DenomCreationFee, validateDenomCreationFee),
paramtypes.NewParamSetPair(KeyDenomCreationGasConsume, &p.DenomCreationGasConsume, validateDenomCreationGasConsume),
paramtypes.NewParamSetPair(KeyFeeCollectorAddress, &p.FeeCollectorAddress, validateFeeCollectorAddress),
paramtypes.NewParamSetPair(KeyWhitelistedHooks, &p.WhitelistedHooks, validateWhitelistedHooks),
}
}

Expand Down Expand Up @@ -105,3 +113,24 @@ func validateFeeCollectorAddress(i interface{}) error {

return nil
}

func validateWhitelistedHooks(i interface{}) error {
hooks, ok := i.([]*HookWhitelist)
if !ok {
return fmt.Errorf("invalid parameter type: %T", i)
}

seenHooks := map[string]bool{}
for _, hook := range hooks {
hookStr := hook.String()
if seenHooks[hookStr] {
return fmt.Errorf("duplicate whitelisted hook: %#v", hook)
}
seenHooks[hookStr] = true
_, err := sdk.AccAddressFromBech32(hook.DenomCreator)
if err != nil {
return fmt.Errorf("invalid denom creator address: %w", err)
}
}
return nil
}
Loading

0 comments on commit e855c8f

Please sign in to comment.