Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ import (
"github.com/bitbadges/bitbadgeschain/x/tokenization/types"

gammkeeper "github.com/bitbadges/bitbadgeschain/x/gamm/keeper"
potkeeper "github.com/bitbadges/bitbadgeschain/x/pot/keeper"

ibchooks "github.com/bitbadges/bitbadgeschain/x/ibc-hooks"
ibcratelimitkeeper "github.com/bitbadges/bitbadgeschain/x/ibc-rate-limit/keeper"
Expand Down Expand Up @@ -173,6 +174,7 @@ type App struct {
MapsKeeper mapsmodulekeeper.Keeper
ManagerSplitterKeeper managersplittermodulekeeper.Keeper

PotKeeper potkeeper.Keeper
GammKeeper gammkeeper.Keeper
PoolManagerKeeper poolmanager.Keeper
SendmanagerKeeper sendmanagermodulekeeper.Keeper
Expand Down Expand Up @@ -350,6 +352,11 @@ func New(
return nil, err
}

// Register x/pot module (Proof of Token — credential-gated validator power)
if err := app.registerPotModule(appOpts); err != nil {
return nil, fmt.Errorf("failed to register pot module: %w", err)
}

// Register EVM modules first (PreciseBank depends on EVM keeper)
if err := app.registerEVMModules(appOpts); err != nil {
return nil, err
Expand Down Expand Up @@ -379,8 +386,9 @@ func New(
})

// Register custom global transfer checkers (optional)
// Note: x/pot no longer uses a transfer hook. It iterates all validators in
// its EndBlocker to catch both transfer-based and time-dependent credential changes.
app.TokenizationKeeper.RegisterCustomGlobalTransferChecker(func(ctx sdk.Context, from string, to string, initiatedBy string, collection *types.TokenCollection, transferBalances []*types.Balance, memo string) []tokenizationmodulekeeper.GlobalTransferChecker {
// Add custom logic as needed here
return nil
})

Expand Down
4 changes: 4 additions & 0 deletions app/app_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
anchormoduletypes "github.com/bitbadges/bitbadgeschain/x/anchor/types"
gammtypes "github.com/bitbadges/bitbadgeschain/x/gamm/types"
poolmanagertypes "github.com/bitbadges/bitbadgeschain/x/poolmanager/types"
pottypes "github.com/bitbadges/bitbadgeschain/x/pot/types"
_ "github.com/bitbadges/bitbadgeschain/x/sendmanager/module"
sendmanagermoduletypes "github.com/bitbadges/bitbadgeschain/x/sendmanager/types"

Expand Down Expand Up @@ -115,6 +116,7 @@ var (
// chain modules
anchormoduletypes.ModuleName,
tokenizationmoduletypes.ModuleName,
pottypes.ModuleName, // x/pot after tokenization
mapsmoduletypes.ModuleName,
managersplittermoduletypes.ModuleName,
packetforwardtypes.ModuleName,
Expand Down Expand Up @@ -149,6 +151,7 @@ var (
// chain modules
anchormoduletypes.ModuleName,
tokenizationmoduletypes.ModuleName,
pottypes.ModuleName, // x/pot no-op BeginBlock but needed for module manager
mapsmoduletypes.ModuleName,
managersplittermoduletypes.ModuleName,
packetforwardtypes.ModuleName,
Expand All @@ -166,6 +169,7 @@ var (
// cosmos sdk modules
crisistypes.ModuleName,
govtypes.ModuleName,
pottypes.ModuleName, // x/pot MUST be BEFORE staking: it jails/unjails, then staking emits ValidatorUpdates
stakingtypes.ModuleName,
feegrant.ModuleName,
group.ModuleName,
Expand Down
68 changes: 68 additions & 0 deletions app/pot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package app

import (
"cosmossdk.io/core/appmodule"
storetypes "cosmossdk.io/store/types"
cdctypes "github.com/cosmos/cosmos-sdk/codec/types"
servertypes "github.com/cosmos/cosmos-sdk/server/types"
"github.com/cosmos/cosmos-sdk/types/module"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
govtypes "github.com/cosmos/cosmos-sdk/x/gov/types"

potkeeper "github.com/bitbadges/bitbadgeschain/x/pot/keeper"
pot "github.com/bitbadges/bitbadgeschain/x/pot/module"
pottypes "github.com/bitbadges/bitbadgeschain/x/pot/types"
)

// registerPotModule registers the x/pot module stores, keeper, and AppModule.
// This follows the same manual registration pattern as registerGammModules.
func (app *App) registerPotModule(_ servertypes.AppOptions) error {
// Register store keys (no transient store needed — x/pot uses persistent KV only).
storeKey := storetypes.NewKVStoreKey(pottypes.StoreKey)
if err := app.RegisterStores(storeKey); err != nil {
return err
}

// Create the tokenization adapter (wraps the concrete tokenization keeper).
tokenizationAdapter := potkeeper.NewTokenizationKeeperAdapter(app.TokenizationKeeper)

// Governance authority for MsgUpdateParams.
authority := authtypes.NewModuleAddress(govtypes.ModuleName)

// Create the staking adapter (for chains using x/staking).
// For PoA chains, replace this with potkeeper.NewPoAAdapter(poaKeeper, potKeeper).
validatorSet := potkeeper.NewStakingAdapter(app.StakingKeeper, app.SlashingKeeper)

// Create the pot keeper.
app.PotKeeper = potkeeper.NewKeeper(
app.appCodec,
storeKey,
app.Logger(),
authority.String(),
tokenizationAdapter,
validatorSet,
)

// Create and register the AppModule.
potModule := pot.NewAppModule(app.appCodec, app.PotKeeper)
if err := app.RegisterModules(potModule); err != nil {
return err
}

return nil
}

// RegisterPot registers the x/pot module on the client side so that
// its CLI commands (tx pot, query pot) and interface types are available
// in the binary. This mirrors the RegisterGamm / RegisterIBC pattern.
func RegisterPot(registry cdctypes.InterfaceRegistry) map[string]appmodule.AppModule {
modules := map[string]appmodule.AppModule{
pottypes.ModuleName: pot.AppModule{},
}

for name, m := range modules {
module.CoreAppModuleBasicAdaptor(name, m).RegisterInterfaces(registry)
}

return modules
}
6 changes: 6 additions & 0 deletions cmd/bitbadgeschaind/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,12 @@ func NewRootCmd() *cobra.Command {
autoCliOpts.Modules[name] = mod
}

potModules := app.RegisterPot(clientCtx.InterfaceRegistry)
for name, mod := range potModules {
moduleBasicManager[name] = module.CoreAppModuleBasicAdaptor(name, mod)
autoCliOpts.Modules[name] = mod
}

initRootCmd(rootCmd, clientCtx.TxConfig, moduleBasicManager)

overwriteFlagDefaults(rootCmd, map[string]string{
Expand Down
3 changes: 2 additions & 1 deletion config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ accounts:
address: bb1zecxs0n0kjqr0vjc5ug3kk594zg4s7jn6lj5dj
- name: charlie
coins:
- 1ubadge
- 99999999999999996ubadge
- 500000000000000ustake
- name: keplr
address: bb1w63npeee74ewuudzf8cgvy6at4jn4mjr0a9r5p
coins:
Expand Down
12 changes: 12 additions & 0 deletions proto/pot/genesis.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
syntax = "proto3";
package pot;

import "gogoproto/gogo.proto";
import "pot/params.proto";

option go_package = "github.com/bitbadges/bitbadgeschain/x/pot/types";

message GenesisState {
Params params = 1 [(gogoproto.nullable) = false];
repeated bytes compliance_jailed_validators = 2;
}
13 changes: 13 additions & 0 deletions proto/pot/params.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
syntax = "proto3";
package pot;

import "gogoproto/gogo.proto";

option go_package = "github.com/bitbadges/bitbadgeschain/x/pot/types";

message Params {
uint64 credential_collection_id = 1;
uint64 credential_token_id = 2;
uint64 min_credential_balance = 3;
string mode = 4;
}
20 changes: 20 additions & 0 deletions proto/pot/query.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
syntax = "proto3";
package pot;

import "gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "pot/params.proto";

option go_package = "github.com/bitbadges/bitbadgeschain/x/pot/types";

service Query {
rpc Params(QueryParamsRequest) returns (QueryParamsResponse) {
option (google.api.http).get = "/pot/params";
}
}

message QueryParamsRequest {}

message QueryParamsResponse {
Params params = 1 [(gogoproto.nullable) = false];
}
25 changes: 25 additions & 0 deletions proto/pot/tx.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
syntax = "proto3";
package pot;

import "amino/amino.proto";
import "cosmos/msg/v1/msg.proto";
import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";
import "pot/params.proto";

option go_package = "github.com/bitbadges/bitbadgeschain/x/pot/types";

service Msg {
option (cosmos.msg.v1.service) = true;
rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse);
}

message MsgUpdateParams {
option (cosmos.msg.v1.signer) = "authority";
option (amino.name) = "pot/MsgUpdateParams";

string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
Params params = 2 [(gogoproto.nullable) = false];
}

message MsgUpdateParamsResponse {}
123 changes: 123 additions & 0 deletions x/pot/client/cli/tx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package cli

import (
"encoding/json"
"fmt"
"strconv"

"github.com/bitbadges/bitbadgeschain/x/pot/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/gogoproto/proto"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/spf13/cobra"
)

func GetTxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: types.ModuleName,
Short: "pot transactions subcommands",
DisableFlagParsing: false,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
cmd.AddCommand(CmdUpdateParams())
return cmd
}

func CmdUpdateParams() *cobra.Command {
cmd := &cobra.Command{
Use: "update-params [collection-id] [token-id] [min-balance] [mode]",
Short: "Update x/pot params (must be sent from governance authority)",
Long: "Update x/pot params. Mode: staked_multiplier, equal, or credential_weighted. Set collection-id to 0 to disable.",
Args: cobra.ExactArgs(4),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}

collectionId, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
return fmt.Errorf("invalid collection-id: %w", err)
}
tokenId, err := strconv.ParseUint(args[1], 10, 64)
if err != nil {
return fmt.Errorf("invalid token-id: %w", err)
}
minBalance, err := strconv.ParseUint(args[2], 10, 64)
if err != nil {
return fmt.Errorf("invalid min-balance: %w", err)
}
mode := args[3]

params := types.Params{
CredentialCollectionId: collectionId,
CredentialTokenId: tokenId,
MinCredentialBalance: minBalance,
Mode: mode,
}
if err := params.Validate(); err != nil {
return err
}

msg := &types.MsgUpdateParams{
Authority: clientCtx.GetFromAddress().String(),
Params: params,
}

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

func GetQueryCmd() *cobra.Command {
cmd := &cobra.Command{
Use: types.ModuleName,
Short: "Querying commands for the pot module",
DisableFlagParsing: false,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
cmd.AddCommand(CmdQueryParams())
return cmd
}

func CmdQueryParams() *cobra.Command {
cmd := &cobra.Command{
Use: "params",
Short: "Query x/pot params",
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}

// Use ABCI store query since we have JSON-encoded params
// (no protobuf gRPC gateway wired yet).
resp, err := clientCtx.Client.ABCIQuery(cmd.Context(), fmt.Sprintf("/store/%s/key", types.StoreKey), types.ParamsKey)
if err != nil {
return fmt.Errorf("failed to query params: %w", err)
}
if resp.Response.Value == nil {
fmt.Println("x/pot params: not set (module disabled)")
return nil
}
var params types.Params
if err := proto.Unmarshal(resp.Response.Value, &params); err != nil {
// Try JSON fallback
if err2 := json.Unmarshal(resp.Response.Value, &params); err2 != nil {
return fmt.Errorf("failed to unmarshal params: proto: %w, json: %v", err, err2)
}
}
out, _ := json.MarshalIndent(params, "", " ")
fmt.Println(string(out))
return nil
},
}
flags.AddQueryFlagsToCmd(cmd)
return cmd
}
Loading
Loading