diff --git a/app/app.go b/app/app.go index a61a75a39..15888daad 100644 --- a/app/app.go +++ b/app/app.go @@ -59,8 +59,9 @@ import ( feegrantmodule "github.com/cosmos/cosmos-sdk/x/feegrant/module" "github.com/cosmos/cosmos-sdk/x/genutil" genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types" - cosmosgov "github.com/cosmos/cosmos-sdk/x/gov" + "github.com/cosmos/cosmos-sdk/x/gov" govclient "github.com/cosmos/cosmos-sdk/x/gov/client" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/cosmos/cosmos-sdk/x/mint" mintkeeper "github.com/cosmos/cosmos-sdk/x/mint/keeper" @@ -98,8 +99,6 @@ import ( ibchost "github.com/cosmos/ibc-go/v3/modules/core/24-host" ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper" ibctesting "github.com/cosmos/ibc-go/v3/testing" - "github.com/neutron-org/neutron/x/gov" - govkeeper "github.com/neutron-org/neutron/x/gov/keeper" "github.com/spf13/cast" abci "github.com/tendermint/tendermint/abci/types" tmjson "github.com/tendermint/tendermint/libs/json" @@ -194,7 +193,7 @@ var ( transfer.AppModuleBasic{}, vesting.AppModuleBasic{}, wasm.AppModuleBasic{}, - cosmosgov.NewAppModuleBasic(getGovProposalHandlers()...), + gov.NewAppModuleBasic(getGovProposalHandlers()...), interchainqueries.AppModuleBasic{}, interchaintxs.AppModuleBasic{}, ) @@ -510,7 +509,7 @@ func New( app.GovKeeper = govkeeper.NewKeeper( appCodec, keys[govtypes.StoreKey], app.GetSubspace(govtypes.ModuleName), app.AccountKeeper, app.BankKeeper, - app.WasmKeeper, + app.StakingKeeper, govRouter, ) diff --git a/cmd/neutrond/genwasm.go b/cmd/neutrond/genwasm.go deleted file mode 100644 index 50e48986b..000000000 --- a/cmd/neutrond/genwasm.go +++ /dev/null @@ -1,30 +0,0 @@ -package main - -import ( - "github.com/cosmos/cosmos-sdk/client" - "github.com/spf13/cobra" - - wasmcli "github.com/CosmWasm/wasmd/x/wasm/client/cli" -) - -func addGenesisWasmMsgCmd(defaultNodeHome string) *cobra.Command { - txCmd := &cobra.Command{ - Use: "add-wasm-message", - Short: "Wasm genesis subcommands", - DisableFlagParsing: true, - SuggestionsMinimumDistance: 2, - RunE: client.ValidateCmd, - } - - genesisIO := wasmcli.NewDefaultGenesisIO() - - txCmd.AddCommand( - wasmcli.GenesisStoreCodeCmd(defaultNodeHome, genesisIO), - wasmcli.GenesisInstantiateContractCmd(defaultNodeHome, genesisIO), - wasmcli.GenesisExecuteContractCmd(defaultNodeHome, genesisIO), - wasmcli.GenesisListContractsCmd(defaultNodeHome, genesisIO), - wasmcli.GenesisListCodesCmd(defaultNodeHome, genesisIO), - ) - - return txCmd -} diff --git a/cmd/neutrond/root.go b/cmd/neutrond/root.go index 6b3e0b9a0..77b7663a6 100644 --- a/cmd/neutrond/root.go +++ b/cmd/neutrond/root.go @@ -92,7 +92,6 @@ func initRootCmd(rootCmd *cobra.Command, encodingConfig params.EncodingConfig) { genutilcli.GenTxCmd(app.ModuleBasics, encodingConfig.TxConfig, banktypes.GenesisBalancesIterator{}, app.DefaultNodeHome), genutilcli.ValidateGenesisCmd(app.ModuleBasics), AddGenesisAccountCmd(app.DefaultNodeHome), - addGenesisWasmMsgCmd(app.DefaultNodeHome), tmcli.NewCompletionCmd(rootCmd, true), // testnetCmd(app.ModuleBasics, banktypes.GenesisBalancesIterator{}), debug.Cmd(), diff --git a/contracts/neutron_dao.wasm b/contracts/neutron_dao.wasm deleted file mode 100644 index 05f676e9c..000000000 Binary files a/contracts/neutron_dao.wasm and /dev/null differ diff --git a/network/init.sh b/network/init.sh index 7e9c269cc..4c03328f9 100755 --- a/network/init.sh +++ b/network/init.sh @@ -23,7 +23,7 @@ RESTPORT_2=1317 ROSETTA_1=8080 ROSETTA_2=8081 -# Stop if it is already running +# Stop if it is already running if pgrep -x "$BINARY" >/dev/null; then echo "Terminating $BINARY..." killall $BINARY @@ -55,8 +55,8 @@ echo $VAL_MNEMONIC_2 | $BINARY keys add val2 --home $CHAIN_DIR/$CHAINID_2 --reco echo $DEMO_MNEMONIC_1 | $BINARY keys add demowallet1 --home $CHAIN_DIR/$CHAINID_1 --recover --keyring-backend=test echo $DEMO_MNEMONIC_2 | $BINARY keys add demowallet2 --home $CHAIN_DIR/$CHAINID_2 --recover --keyring-backend=test echo $DEMO_MNEMONIC_3 | $BINARY keys add demowallet3 --home $CHAIN_DIR/$CHAINID_1 --recover --keyring-backend=test -echo $RLY_MNEMONIC_1 | $BINARY keys add rly1 --home $CHAIN_DIR/$CHAINID_1 --recover --keyring-backend=test -echo $RLY_MNEMONIC_2 | $BINARY keys add rly2 --home $CHAIN_DIR/$CHAINID_2 --recover --keyring-backend=test +echo $RLY_MNEMONIC_1 | $BINARY keys add rly1 --home $CHAIN_DIR/$CHAINID_1 --recover --keyring-backend=test +echo $RLY_MNEMONIC_2 | $BINARY keys add rly2 --home $CHAIN_DIR/$CHAINID_2 --recover --keyring-backend=test $BINARY add-genesis-account $($BINARY --home $CHAIN_DIR/$CHAINID_1 keys show val1 --keyring-backend test -a) 100000000000stake --home $CHAIN_DIR/$CHAINID_1 $BINARY add-genesis-account $($BINARY --home $CHAIN_DIR/$CHAINID_2 keys show val2 --keyring-backend test -a) 100000000000stake --home $CHAIN_DIR/$CHAINID_2 @@ -72,14 +72,6 @@ $BINARY gentx val2 7000000000stake --home $CHAIN_DIR/$CHAINID_2 --chain-id $CHAI $BINARY collect-gentxs --home $CHAIN_DIR/$CHAINID_1 $BINARY collect-gentxs --home $CHAIN_DIR/$CHAINID_2 -echo "Initializing dao contract in genesis..." -# Upload the dao contract -$BINARY add-wasm-message store ${DAO_CONTRACT} --output json --run-as neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u --home $CHAIN_DIR/$CHAINID_1 -# Instantiate the contract -INIT_CONTRACT="$(printf '{"owner":"%s"}' "${ADMIN_ADDRESS}")" -#echo "Instantiate" -$BINARY add-wasm-message instantiate-contract 1 "$INIT_CONTRACT" --run-as neutron1mjk79fjjgpplak5wq838w0yd982gzkyf8fxu8u --admin ${ADMIN_ADDRESS} --label "DAO" --home $CHAIN_DIR/$CHAINID_1 - echo "Changing defaults and ports in app.toml and config.toml files..." sed -i -e 's#"tcp://0.0.0.0:26656"#"tcp://0.0.0.0:'"$P2PPORT_1"'"#g' $CHAIN_DIR/$CHAINID_1/config/config.toml sed -i -e 's#"tcp://127.0.0.1:26657"#"tcp://0.0.0.0:'"$RPCPORT_1"'"#g' $CHAIN_DIR/$CHAINID_1/config/config.toml diff --git a/x/gov/README.md b/x/gov/README.md deleted file mode 100644 index d6eadf2f9..000000000 --- a/x/gov/README.md +++ /dev/null @@ -1,42 +0,0 @@ -# x/gov -The Neutron's governance is a wrapper over original cosmos sdk `gov` module with a some key difference. For the modules are very similar, overview section describes only the differences. Learn more about the gov module (and therefore about the Neutron's one) by the link to the cosmos documentation site: https://docs.cosmos.network/master/modules/gov/ - -What makes this module different from the original one: -- Staking no more affects user's voting power -- Voting power is now calculates and stores in smart-contract -- Tally logic is modified - -Read about these features below to make a better understanding. - -## Staking no more affects user's voting power - -The original gov module computes voting power on proposals using each user's delegations. Since the Neutron's plan is to not use standard staking & validators, it's necessary to remove rudimentary usage of staking module. Instead of this, however, we are introduced an alternative -## Voting power is now calculated and stored in smart-contract - -We use cosm-wasm contract which implements several methods: -```rust -pub fn query_voting_power(deps: Deps, user_addr: Addr) -> StdResult {...} -``` - -```rust -pub fn query_voting_powers(deps: Deps) -> StdResult> {...} -``` -where ```VotingPowerResponse``` is - -```rust -pub struct VotingPowerResponse { - /// Address of the user - pub user: String, - /// The user's current voting power, i.e. the amount of NTRN tokens locked in voting contract - pub voting_power: Uint128, -} -``` -currently neutron-core uses only `query_voting_powers`, but `query_voting_power` seems to be useful in future - -## Tally logic is modified -Tally interface hasn't changed, but instead of calculating voting results by staked tokens, it uses an above contract's query -```golang -// GetTokensInDao queries the voting contract for an array of users who have tokens locked in the -// contract and their respective amount, as well as computing the total amount of locked tokens. -func GetTokensInDao(ctx sdk.Context, k wasmtypes.ViewKeeper, contractAddr sdk.AccAddress) (map[string]sdk.Int, sdk.Int, error) {...} -``` diff --git a/x/gov/abci.go b/x/gov/abci.go deleted file mode 100644 index 911a300c1..000000000 --- a/x/gov/abci.go +++ /dev/null @@ -1,117 +0,0 @@ -package gov - -import ( - "fmt" - "time" - - "github.com/cosmos/cosmos-sdk/telemetry" - sdk "github.com/cosmos/cosmos-sdk/types" - - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - - "github.com/neutron-org/neutron/x/gov/keeper" -) - -// EndBlocker called at the end of every block, processing proposals -// same as the vanilla gov EndBlocker, except for we replace the `Tally` -// function with our own implementation -func EndBlocker(ctx sdk.Context, keeper keeper.Keeper) { - defer telemetry.ModuleMeasureSince(govtypes.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) - - logger := keeper.Logger(ctx) - - // delete inactive proposal from store and its deposits - keeper.IterateInactiveProposalsQueue(ctx, ctx.BlockHeader().Time, func(proposal govtypes.Proposal) bool { - keeper.DeleteProposal(ctx, proposal.ProposalId) - keeper.DeleteDeposits(ctx, proposal.ProposalId) - - // called when proposal become inactive - keeper.AfterProposalFailedMinDeposit(ctx, proposal.ProposalId) - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - govtypes.EventTypeInactiveProposal, - sdk.NewAttribute(govtypes.AttributeKeyProposalID, fmt.Sprintf("%d", proposal.ProposalId)), - sdk.NewAttribute(govtypes.AttributeKeyProposalResult, govtypes.AttributeValueProposalDropped), - ), - ) - - logger.Info( - "proposal did not meet minimum deposit; deleted", - "proposal", proposal.ProposalId, - "title", proposal.GetTitle(), - "min_deposit", keeper.GetDepositParams(ctx).MinDeposit.String(), - "total_deposit", proposal.TotalDeposit.String(), - ) - - return false - }) - - // fetch active proposals whose voting periods have ended (are passed the block time) - keeper.IterateActiveProposalsQueue(ctx, ctx.BlockHeader().Time, func(proposal govtypes.Proposal) bool { - var tagValue, logMsg string - - passes, burnDeposits, tallyResults := keeper.Tally(ctx, proposal) // our custom implementation of tally logics - - if burnDeposits { - keeper.DeleteDeposits(ctx, proposal.ProposalId) - } else { - keeper.RefundDeposits(ctx, proposal.ProposalId) - } - - if passes { - handler := keeper.Router().GetRoute(proposal.ProposalRoute()) - cacheCtx, writeCache := ctx.CacheContext() - - // The proposal handler may execute state mutating logic depending on the proposal content. - // If the handler fails, no state mutation is written and the error message is logged. - err := handler(cacheCtx, proposal.GetContent()) - if err == nil { - proposal.Status = govtypes.StatusPassed - tagValue = govtypes.AttributeValueProposalPassed - logMsg = "passed" - - // The cached context is created with a new EventManager. However, since the proposal - // handler execution was successful, we want to track/keep any events emitted, so we - // re-emit to "merge" the events into the original Context's EventManager. - ctx.EventManager().EmitEvents(cacheCtx.EventManager().Events()) - - // write state to the underlying multi-store - writeCache() - } else { - proposal.Status = govtypes.StatusFailed - tagValue = govtypes.AttributeValueProposalFailed - logMsg = fmt.Sprintf("passed, but failed on execution: %s", err) - } - } else { - proposal.Status = govtypes.StatusRejected - tagValue = govtypes.AttributeValueProposalRejected - logMsg = "rejected" - } - - proposal.FinalTallyResult = tallyResults - - keeper.SetProposal(ctx, proposal) - keeper.RemoveFromActiveProposalQueue(ctx, proposal.ProposalId, proposal.VotingEndTime) - - // when proposal become active - keeper.AfterProposalVotingPeriodEnded(ctx, proposal.ProposalId) - - logger.Info( - "proposal tallied", - "proposal", proposal.ProposalId, - "title", proposal.GetTitle(), - "result", logMsg, - ) - - ctx.EventManager().EmitEvent( - sdk.NewEvent( - govtypes.EventTypeActiveProposal, - sdk.NewAttribute(govtypes.AttributeKeyProposalID, fmt.Sprintf("%d", proposal.ProposalId)), - sdk.NewAttribute(govtypes.AttributeKeyProposalResult, tagValue), - ), - ) - - return false - }) -} diff --git a/x/gov/keeper/keeper.go b/x/gov/keeper/keeper.go deleted file mode 100644 index bac596e38..000000000 --- a/x/gov/keeper/keeper.go +++ /dev/null @@ -1,48 +0,0 @@ -package keeper - -import ( - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - - govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" -) - -// Keeper defines the custom governance module Keeper -// -// NOTE: Keeper wraps the vanilla gov keeper to inherit most of its functions. However, we include an -// additional dependency, the wasm keeper, which is needed for our custom vote tallying logic -type Keeper struct { - govkeeper.Keeper - - storeKey sdk.StoreKey - wasmKeeper wasmtypes.ViewKeeper -} - -// NewKeeper returns a custom gov keeper -// -// NOTE: compared to the vanilla gov keeper's constructor function here we: -// 1. require an additional wasm keeper, which is needed for our custom vote tallying logic -// 2. remove staking module -func NewKeeper( - cdc codec.BinaryCodec, key sdk.StoreKey, paramSpace govtypes.ParamSubspace, - authKeeper govtypes.AccountKeeper, bankKeeper govtypes.BankKeeper, - wasmKeeper wasmtypes.ViewKeeper, rtr govtypes.Router, -) Keeper { - return Keeper{ - Keeper: govkeeper.NewKeeper(cdc, key, paramSpace, authKeeper, bankKeeper, nil, rtr), - storeKey: key, - wasmKeeper: wasmKeeper, - } -} - -// deleteVote deletes a vote from a given proposalID and voter from the store -// -// NOTE: the vanilla gov module does not make the `deleteVote` function public, so in order to delete -// votes, we need to redefine the function here -func (k Keeper) deleteVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) { - store := ctx.KVStore(k.storeKey) - store.Delete(govtypes.VoteKey(proposalID, voterAddr)) -} diff --git a/x/gov/keeper/query_server.go b/x/gov/keeper/query_server.go deleted file mode 100644 index 1e8877629..000000000 --- a/x/gov/keeper/query_server.go +++ /dev/null @@ -1,79 +0,0 @@ -package keeper - -import ( - "context" - - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - sdk "github.com/cosmos/cosmos-sdk/types" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" -) - -type queryServer struct{ k Keeper } - -// NewQueryServerImpl creates an implementation of the QueryServer interface for the given keeper -func NewQueryServerImpl(k Keeper) govtypes.QueryServer { - return &queryServer{k} -} - -func (qs queryServer) TallyResult(goCtx context.Context, req *govtypes.QueryTallyResultRequest) (*govtypes.QueryTallyResultResponse, error) { - if req == nil { - return nil, status.Error(codes.InvalidArgument, "invalid request") - } - - if req.ProposalId == 0 { - return nil, status.Error(codes.InvalidArgument, "proposal id can not be 0") - } - - ctx := sdk.UnwrapSDKContext(goCtx) - - proposal, ok := qs.k.GetProposal(ctx, req.ProposalId) - if !ok { - return nil, status.Errorf(codes.NotFound, "proposal %d doesn't exist", req.ProposalId) - } - - var tallyResult govtypes.TallyResult - - switch { - case proposal.Status == govtypes.StatusDepositPeriod: - tallyResult = govtypes.EmptyTallyResult() - - case proposal.Status == govtypes.StatusPassed || proposal.Status == govtypes.StatusRejected: - tallyResult = proposal.FinalTallyResult - - default: - // proposal is in voting period - _, _, tallyResult = qs.k.Tally(ctx, proposal) // replace with our custom Tally function - } - - return &govtypes.QueryTallyResultResponse{Tally: tallyResult}, nil -} - -func (qs queryServer) Proposal(goCtx context.Context, req *govtypes.QueryProposalRequest) (*govtypes.QueryProposalResponse, error) { - return qs.k.Proposal(goCtx, req) -} - -func (qs queryServer) Proposals(goCtx context.Context, req *govtypes.QueryProposalsRequest) (*govtypes.QueryProposalsResponse, error) { - return qs.k.Proposals(goCtx, req) -} - -func (qs queryServer) Vote(goCtx context.Context, req *govtypes.QueryVoteRequest) (*govtypes.QueryVoteResponse, error) { - return qs.k.Vote(goCtx, req) -} - -func (qs queryServer) Votes(goCtx context.Context, req *govtypes.QueryVotesRequest) (*govtypes.QueryVotesResponse, error) { - return qs.k.Votes(goCtx, req) -} - -func (qs queryServer) Params(goCtx context.Context, req *govtypes.QueryParamsRequest) (*govtypes.QueryParamsResponse, error) { - return qs.k.Params(goCtx, req) -} - -func (qs queryServer) Deposit(goCtx context.Context, req *govtypes.QueryDepositRequest) (*govtypes.QueryDepositResponse, error) { - return qs.k.Deposit(goCtx, req) -} - -func (qs queryServer) Deposits(goCtx context.Context, req *govtypes.QueryDepositsRequest) (*govtypes.QueryDepositsResponse, error) { - return qs.k.Deposits(goCtx, req) -} diff --git a/x/gov/keeper/tally.go b/x/gov/keeper/tally.go deleted file mode 100644 index 7cfc907c4..000000000 --- a/x/gov/keeper/tally.go +++ /dev/null @@ -1,94 +0,0 @@ -package keeper - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - - wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" -) - -// DefaultContractAddr is the wasm contract address generated by code ID 1 and instance ID 1. -// -// In other words, the first ever contract to be deployed on this chain will necessarily have this address. -var DefaultContractAddr = wasmkeeper.BuildContractAddress(1, 1) - -// Tally iterates over the votes and updates the tally of a proposal based on the voting power of the voters -func (k Keeper) Tally(ctx sdk.Context, proposal govtypes.Proposal) (passes bool, burnDeposits bool, tallyResults govtypes.TallyResult) { - results := make(map[govtypes.VoteOption]sdk.Dec) - results[govtypes.OptionYes] = sdk.ZeroDec() - results[govtypes.OptionAbstain] = sdk.ZeroDec() - results[govtypes.OptionNo] = sdk.ZeroDec() - results[govtypes.OptionNoWithVeto] = sdk.ZeroDec() - - // fetch all tokens locked in the DAO contract - // - // NOTE: for now we simply use the default contract address. later it may be a better idea to use - // a configurable parameter - tokensLocked, totalTokensLocked := MustGetTokensInVoting(ctx, k.wasmKeeper, DefaultContractAddr) - - // total amount of tokens that have voted in this poll; used to determine whether the poll reaches - // quorum and the pass threshold - totalTokensVoted := sdk.ZeroDec() - - // iterate through votes - k.IterateVotes(ctx, proposal.ProposalId, func(vote govtypes.Vote) bool { - voterAddr := sdk.MustAccAddressFromBech32(vote.Voter) - - votingPower := sdk.ZeroDec() - - // if the voter has tokens locked in DAO contract, add that to the voting power - if votingPowerInDao, ok := tokensLocked[vote.Voter]; ok { - votingPower = votingPower.Add(votingPowerInDao.ToDec()) - } - - incrementTallyResult(votingPower, vote.Options, results, &totalTokensVoted) - k.deleteVote(ctx, vote.ProposalId, voterAddr) - - return false - }) - - tallyParams := k.GetTallyParams(ctx) - tallyResults = govtypes.NewTallyResultFromMap(results) - - // if there is no staked coins, the proposal fails - if totalTokensLocked.IsZero() { - return false, false, tallyResults - } - - // if there is not enough quorum of votes, the proposal fails, and deposit burned - // - // NOTE: should the deposit really be burned here? - if totalTokensVoted.Quo(totalTokensLocked.ToDec()).LT(tallyParams.Quorum) { - return false, true, tallyResults - } - - // if everyone abstains, proposal fails - if totalTokensVoted.Sub(results[govtypes.OptionAbstain]).IsZero() { - return false, false, tallyResults - } - - // if more than 1/3 of voters veto, proposal fails, and deposit burned - // - // NOTE: here 1/3 is defined as 1/3 *of all votes*, including abstaining votes. could it make more - // sense to instead define it as 1/3 *of all non-abstaining votes*? - if results[govtypes.OptionNoWithVeto].Quo(totalTokensVoted).GT(tallyParams.VetoThreshold) { - return false, true, tallyResults - } - - // if no less than 1/2 of non-abstaining voters vote No, proposal fails - if results[govtypes.OptionNo].Quo(totalTokensVoted.Sub(results[govtypes.OptionAbstain])).GTE(tallyParams.Threshold) { - return false, false, tallyResults - } - - // otherwise, meaning more than 1/2 of non-abstaining voters vote Yes, proposal passes - return true, false, tallyResults -} - -func incrementTallyResult(votingPower sdk.Dec, options []govtypes.WeightedVoteOption, results map[govtypes.VoteOption]sdk.Dec, totalTokensVoted *sdk.Dec) { - for _, option := range options { - subPower := votingPower.Mul(option.Weight) - results[option.Option] = results[option.Option].Add(subPower) - } - - *totalTokensVoted = totalTokensVoted.Add(votingPower) -} diff --git a/x/gov/keeper/voting.go b/x/gov/keeper/voting.go deleted file mode 100644 index 42a5444e7..000000000 --- a/x/gov/keeper/voting.go +++ /dev/null @@ -1,79 +0,0 @@ -package keeper - -import ( - "encoding/json" - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - - "github.com/neutron-org/neutron/x/gov/types" -) - -// queryVotingPowers queries the dao contract of user voting powers based on the given query msg -func queryVotingPowers(ctx sdk.Context, k wasmtypes.ViewKeeper, contractAddr sdk.AccAddress) (types.VotingPowersResponse, error) { - var votingPowersResponse types.VotingPowersResponse - - req, err := json.Marshal(&types.QueryMsg{VotingPowers: &types.VotingPowersQuery{}}) - if err != nil { - return nil, sdkerrors.Wrapf(types.ErrFailedToQueryVoting, "failed to marshal query request: %s", err) - } - - res, err := k.QuerySmart(ctx, contractAddr, req) - if err != nil { - return nil, sdkerrors.Wrapf(types.ErrFailedToQueryVoting, "query returned error: %s", err) - } - - err = json.Unmarshal(res, &votingPowersResponse) - if err != nil { - return nil, sdkerrors.Wrapf(types.ErrFailedToQueryVoting, "failed to unmarshal query response: %s", err) - } - - return votingPowersResponse, nil -} - -// incrementVotingPowers increments the voting power counter based on the contract query response -// -// NOTE: This function modifies the `tokensLocked` and `totalTokensAmount` variables in place. -func incrementVotingPowers(votingPowersResponse types.VotingPowersResponse, tokensLocked map[string]sdk.Int, totalTockensLocked *sdk.Int) error { - for _, item := range votingPowersResponse { - if _, ok := tokensLocked[item.User]; ok { - return sdkerrors.Wrapf(types.ErrFailedToQueryVoting, "query response contains duplicate address: %s", item.User) - } - - tokensLocked[item.User] = sdk.Int(item.VotingPower) - *totalTockensLocked = totalTockensLocked.Add(sdk.Int(item.VotingPower)) - } - - return nil -} - -// GetTokensInDao queries the voting contract for an array of users who have tokens locked in the -// contract and their respective amount, as well as computing the total amount of locked tokens. -func GetTokensInDao(ctx sdk.Context, k wasmtypes.ViewKeeper, contractAddr sdk.AccAddress) (map[string]sdk.Int, sdk.Int, error) { - tokensLocked := make(map[string]sdk.Int) - totalTokenAmount := sdk.ZeroInt() - - votingPowersResponse, err := queryVotingPowers(ctx, k, contractAddr) - if err != nil { - return nil, sdk.ZeroInt(), err - } - - if err = incrementVotingPowers(votingPowersResponse, tokensLocked, &totalTokenAmount); err != nil { - return nil, sdk.ZeroInt(), err - } - - return tokensLocked, totalTokenAmount, nil -} - -// MustGetTokensInVoting is the same with `GetTokensInDao`, but panics on error -func MustGetTokensInVoting(ctx sdk.Context, k wasmtypes.ViewKeeper, contractAddr sdk.AccAddress) (map[string]sdk.Int, sdk.Int) { - tokensLocked, totalTokensLocked, err := GetTokensInDao(ctx, k, contractAddr) - if err != nil { - panic(fmt.Sprintf("failed to tally vote: %s", err)) - } - - return tokensLocked, totalTokensLocked -} diff --git a/x/gov/module.go b/x/gov/module.go deleted file mode 100644 index 7eb539cc6..000000000 --- a/x/gov/module.go +++ /dev/null @@ -1,54 +0,0 @@ -package gov - -import ( - abci "github.com/tendermint/tendermint/abci/types" - - "github.com/cosmos/cosmos-sdk/codec" - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/cosmos/cosmos-sdk/types/module" - - "github.com/cosmos/cosmos-sdk/x/gov" - govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" - - "github.com/neutron-org/neutron/x/gov/keeper" -) - -// AppModule must implement the `module.AppModule` interface -var _ module.AppModule = AppModule{} - -// AppModule implements an application module for the custom gov module -// -// NOTE: our custom AppModule wraps the vanilla `gov.AppModule` to inherit most of its functions. -// However, we overwrite the `EndBlock` function to replace it with our custom vote tallying logic -type AppModule struct { - gov.AppModule - - keeper keeper.Keeper -} - -// NewAppModule creates a new AppModule object -func NewAppModule(cdc codec.Codec, keeper keeper.Keeper, ak govtypes.AccountKeeper, bk govtypes.BankKeeper) AppModule { - return AppModule{ - AppModule: gov.NewAppModule(cdc, keeper.Keeper, ak, bk), - keeper: keeper, - } -} - -// EndBlock returns the end blocker for the gov module. It returns no validator updates. -// -// NOTE: this overwrites the vanilla gov module EndBlocker with our custom vote tallying logic -func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { - EndBlocker(ctx, am.keeper) - return []abci.ValidatorUpdate{} -} - -// RegisterServices registers module services. -// -// NOTE: this overwrites the vanilla gov module RegisterServices function -func (am AppModule) RegisterServices(cfg module.Configurator) { - // msg server - use the vanilla implementation - govtypes.RegisterMsgServer(cfg.MsgServer(), govkeeper.NewMsgServerImpl(am.keeper.Keeper)) - // query server - use our custom implementation - govtypes.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServerImpl(am.keeper)) -} diff --git a/x/gov/types/errors.go b/x/gov/types/errors.go deleted file mode 100644 index e547b427f..000000000 --- a/x/gov/types/errors.go +++ /dev/null @@ -1,12 +0,0 @@ -package types - -import ( - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" -) - -// ErrFailedToQueryVoting represents an error where the gov module fails to query the vesting contract -// -// NOTE: latest version (v0.46.0) of the vanilla gov module already registered 2-15, so we start from 16 -var ErrFailedToQueryVoting = sdkerrors.Register(govtypes.ModuleName, 16, "failed to query vesting contract") diff --git a/x/gov/types/keys.go b/x/gov/types/keys.go deleted file mode 100644 index a4577c29d..000000000 --- a/x/gov/types/keys.go +++ /dev/null @@ -1,15 +0,0 @@ -package types - -const ( - // ModuleName defines the module name - ModuleName = "gov" - - // StoreKey defines the primary module store key - StoreKey = ModuleName - - // RouterKey is the message route for slashing - RouterKey = ModuleName - - // MemStoreKey defines the in-memory store key - MemStoreKey = "mem_gov" -) diff --git a/x/gov/types/voting.go b/x/gov/types/voting.go deleted file mode 100644 index 619eeaee9..000000000 --- a/x/gov/types/voting.go +++ /dev/null @@ -1,33 +0,0 @@ -package types - -import sdk "github.com/cosmos/cosmos-sdk/types" - -// InstantiateMsg corresponding to the Rust type `neutron-voting::msg::InstantiateMsg` -type InstantiateMsg struct { - Owner string `json:"owner"` -} - -// QueryMsg corresponding to the Rust enum `neutron-voting::msg::QueryMsg` -// -// NOTE: For covenience, we don't include other enum variants, as they are not needed here -type QueryMsg struct { - VotingPower *VotingPowerQuery `json:"voting_power,omitempty"` - VotingPowers *VotingPowersQuery `json:"voting_powers,omitempty"` -} - -// VotingPowerQuery corresponding to the Rust enum variant `neutron-voting::msg::QueryMsg::VotingPower` -type VotingPowerQuery struct { - User string `json:"user,omitempty"` -} - -// VotingPowersQuery corresponding to the Rust enum variant `neutron-voting::msg::QueryMsg::VotingPowers` -type VotingPowersQuery struct{} - -// VotingPowerResponse corresponding to the `voting_powers` query's respons type's repeating element -type VotingPowerResponse struct { - User string `json:"user"` - VotingPower sdk.Uint `json:"voting_power"` -} - -// VotingPowersResponse corresponding to the response type of the `voting_powers` query -type VotingPowersResponse []VotingPowerResponse