diff --git a/.dockerignore b/.dockerignore index d16386367..64952591a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,4 @@ -build/ \ No newline at end of file +build/ +.github/ +interchaintest/ +*.md \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 183f707d9..ea79b0be6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -11,6 +11,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + GO_VERSION: 1.20.7 + jobs: build: runs-on: ubuntu-latest @@ -18,9 +21,9 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: - go-version: 1.20.0 + go-version: ${{ env.GO_VERSION }} - run: go build ./... test: @@ -28,9 +31,9 @@ jobs: name: test steps: - name: Install Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: - go-version: 1.20.0 + go-version: ${{ env.GO_VERSION }} - name: Checkout code uses: actions/checkout@v3 - name: Test @@ -42,9 +45,9 @@ jobs: steps: - uses: actions/checkout@v3 - name: Setup go - uses: actions/setup-go@v3 + uses: actions/setup-go@v4 with: - go-version: 1.20.0 + go-version: ${{ env.GO_VERSION }} - run: | go mod tidy CHANGES_IN_REPO=$(git status --porcelain) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 9cfb1ea78..9b1a3a8cd 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -17,6 +17,9 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + GO_VERSION: 1.20.7 + jobs: analyze: name: Analyze @@ -29,9 +32,9 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 with: - go-version: 1.20.0 + go-version: ${{ env.GO_VERSION }} # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v2 diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index b98d8c4e6..6662d1d2f 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -16,14 +16,17 @@ concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true +env: + GO_VERSION: 1.20.7 + jobs: golangci: name: lint runs-on: ubuntu-latest steps: - - uses: actions/setup-go@v3 + - uses: actions/setup-go@v4 with: - go-version: 1.20.0 + go-version: ${{ env.GO_VERSION }} - uses: actions/checkout@v3 - name: golangci-lint-junod diff --git a/.github/workflows/interchaintest-E2E.yml b/.github/workflows/interchaintest-E2E.yml index 8e1a839b6..5d7b642ff 100644 --- a/.github/workflows/interchaintest-E2E.yml +++ b/.github/workflows/interchaintest-E2E.yml @@ -4,152 +4,93 @@ on: pull_request: push: tags: - - '**' + - "**" branches: - - 'main' - - 'master' + - "main" + - "master" permissions: contents: read packages: write env: - REGISTRY: ghcr.io - IMAGE_NAME: ${{ github.repository }}-e2e - GO_VERSION: 1.19 + GO_VERSION: 1.20.7 + TAR_PATH: /tmp/juno-docker-image.tar + IMAGE_NAME: juno-docker-image concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: - build-and-push-image: + build-docker: runs-on: ubuntu-latest - outputs: - branchTag: ${{ steps.meta.outputs.version }} - permissions: - contents: read - packages: write - steps: - - name: Checkout repository + - name: Checkout uses: actions/checkout@v3 - # We setup go & cache dependencies here. This way each child job - # does not have to reinstall all dependencies individually. - # Should ID be unique to this job only? - - name: Setup Golang with cache - uses: magnetikonline/action-golang-cache@v4 + - name: Setup Go ${{ env.GO_VERSION }} + uses: actions/setup-go@v4 with: go-version: ${{ env.GO_VERSION }} - id: go - - - name: Download dependencies - run: | - go mod download - cd interchaintest && go mod download - - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + cache-dependency-path: interchaintest/go.sum - name: Set up Docker Buildx uses: docker/setup-buildx-action@v2 - - name: Log in to the Container registry - uses: docker/login-action@v2 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract metadata (tags, labels) for Docker - id: meta - uses: docker/metadata-action@v4 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image - id: push + - name: Build and export uses: docker/build-push-action@v4 with: context: . - platforms: linux/amd64 - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max - - # make juno:branchname here for all needs.build-and-push-image.outputs.branchTag - # then upload to github. Then download for each as a cache. This way its only built once - - test-juno-basic: - needs: build-and-push-image - uses: ./.github/workflows/make-cmd-runner.yml - with: - make-command: ictest-basic - branch-tag: ${{needs.build-and-push-image.outputs.branchTag}} - - test-juno-ibc: - needs: build-and-push-image - uses: ./.github/workflows/make-cmd-runner.yml - with: - make-command: ictest-ibc - branch-tag: ${{needs.build-and-push-image.outputs.branchTag}} + tags: juno:local + outputs: type=docker,dest=${{ env.TAR_PATH }} - test-juno-upgrade: - needs: build-and-push-image - uses: ./.github/workflows/make-cmd-runner.yml - with: - make-command: ictest-upgrade - branch-tag: ${{needs.build-and-push-image.outputs.branchTag}} - - test-juno-tokenfactory: - needs: build-and-push-image - uses: ./.github/workflows/make-cmd-runner.yml - with: - make-command: ictest-tokenfactory - branch-tag: ${{needs.build-and-push-image.outputs.branchTag}} + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: ${{ env.IMAGE_NAME }} + path: ${{ env.TAR_PATH }} - test-juno-feeshare: - needs: build-and-push-image - uses: ./.github/workflows/make-cmd-runner.yml - with: - make-command: ictest-feeshare - branch-tag: ${{needs.build-and-push-image.outputs.branchTag}} + e2e-tests: + needs: build-docker + runs-on: ubuntu-latest + strategy: + matrix: + # names of `make` commands to run tests + test: + - "ictest-basic" + - "ictest-ibchooks" + - "ictest-tokenfactory" + - "ictest-feeshare" + - "ictest-pfm" + - "ictest-upgrade" + - "ictest-ibc" + - "ictest-unity-deploy" + - "ictest-unity-gov" + - "ictest-pob" + - "ictest-drip" + fail-fast: false - test-juno-ibchooks: - needs: build-and-push-image - uses: ./.github/workflows/make-cmd-runner.yml - with: - make-command: ictest-ibchooks - branch-tag: ${{needs.build-and-push-image.outputs.branchTag}} + steps: + - name: Set up Go ${{ env.GO_VERSION }} + uses: actions/setup-go@v4 + with: + go-version: ${{ env.GO_VERSION }} + cache-dependency-path: interchaintest/go.sum - # === UNITY CONTRACT === - test-juno-unity-deploy: - needs: build-and-push-image - uses: ./.github/workflows/make-cmd-runner.yml - with: - make-command: ictest-unity-deploy - branch-tag: ${{needs.build-and-push-image.outputs.branchTag}} + - name: checkout chain + uses: actions/checkout@v3 - test-juno-unity-gov: - needs: build-and-push-image - uses: ./.github/workflows/make-cmd-runner.yml - with: - make-command: ictest-unity-gov - branch-tag: ${{needs.build-and-push-image.outputs.branchTag}} + - name: Download Tarball Artifact + uses: actions/download-artifact@v3 + with: + name: ${{ env.IMAGE_NAME }} + path: /tmp - test-juno-pfm: - needs: build-and-push-image - uses: ./.github/workflows/make-cmd-runner.yml - with: - make-command: ictest-pfm - branch-tag: ${{needs.build-and-push-image.outputs.branchTag}} + - name: Load Docker Image + run: | + docker image load -i ${{ env.TAR_PATH }} + docker image ls -a - test-pob: - needs: build-and-push-image - uses: ./.github/workflows/make-cmd-runner.yml - with: - make-command: ictest-pob - branch-tag: ${{needs.build-and-push-image.outputs.branchTag}} + - name: Run Test + run: make ${{ matrix.test }} diff --git a/.github/workflows/make-cmd-runner.yml b/.github/workflows/make-cmd-runner.yml deleted file mode 100644 index 66f999b7d..000000000 --- a/.github/workflows/make-cmd-runner.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Make runner - -on: - workflow_call: - inputs: - make-command: - type: string - required: true - description: 'The make command to run' - branch-tag: - type: string - required: true - description: 'The branch tag of the docker image' - - -jobs: - run_make: - runs-on: ubuntu-latest - steps: - - name: checkout chain - uses: actions/checkout@v2 - - - name: Setup Golang with cache - uses: magnetikonline/action-golang-cache@v4 - with: - go-version: 1.19 - id: go - - - run: make ${{ inputs.make-command }} - env: - BRANCH_CI: ${{ inputs.branch-tag }} \ No newline at end of file diff --git a/Makefile b/Makefile index 926b50a43..1595cffd7 100644 --- a/Makefile +++ b/Makefile @@ -157,6 +157,9 @@ ictest-unity-gov: rm-testcache ictest-pob: rm-testcache cd interchaintest && go test -race -v -run TestJunoPOB . +ictest-drip: rm-testcache + cd interchaintest && go test -race -v -run TestJunoDrip . + rm-testcache: go clean -testcache diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index 3c18b53ee..000000000 --- a/ROADMAP.md +++ /dev/null @@ -1,81 +0,0 @@ -# ROADMAP - -This document contains the roadmap for the Juno project. It is a living document and will be updated as the project progresses. For the most update to date information, please follow the Notion and tracking issue links in each section. - ---- - -## Long Term - Q4 2023+ - -- [Long term Tracking Issue](https://github.com/CosmosContracts/juno/issues/611) -- SDK v0.47, Tendermint 0.37 -- IBC v5/6, ICA v3 (optional) -- [Native Liquid Staking](https://github.com/iqlusioninc/liquidity-staking-module) -- [Improving the Nakamoto Coefficient](https://github.com/CosmosContracts/juno/issues/474) -- Wasm based oracle - ---- - -## V15 - End of Q2 2023 - - - - - -This upgrade focuses entirely on moving Juno's block times from 6 seconds to 3 seconds. - -## Features - -- 3 second block times (from 6 seconds) - ---- - -## V14 (Aurora) - Early Q2 2023 - -- [Medium Blog](https://medium.com/@JunoNetwork/jun%C3%B8-aurora-ac67a8143e22) - -- [V14 Tracking Issue](https://github.com/CosmosContracts/juno/issues/548) - -This update will focus more on upgrading the base layer of the Juno stack, bringing new features and pushing us to the latest versions of the software. - -## V14 Features - -- IBC-Hooks -- CosmWasm v0.31 -- WasmVM v1.2.1 -- Global Minimum Fees (governance controlled) -- Feeless IBC Relaying -- TokenFactory: burnFrom, burnTo, ForceTransfer -- [Interchain test](https://github.com/strangelove-ventures/interchaintest) -- Using [Skip's MEV Tendermint fork](https://github.com/skip-mev/mev-cometbft) by default - ---- - -## V13 - Q1 2023 - -Links: - -- [Medium Blog](https://medium.com/@JunoNetwork/jun%C3%B8-v-13-fefa9d2dfce5) - -- [v13 Tracking Issue](https://github.com/CosmosContracts/juno/issues/475) - -The V13 update is Juno's largest update, bringing many new features for developers, users, and relayers. - -### V13 PRs - -- [x/FeeShare (CosmWasm)](https://github.com/CosmosContracts/juno/pull/385) -- [x/TokenFactory](https://github.com/CosmosContracts/juno/pull/368) -- [Packet Forward Middleware](https://github.com/CosmosContracts/juno/pull/513) -- [x/GlobalFee](https://github.com/CosmosContracts/juno/pull/411) -- [More ICA Messages](https://github.com/CosmosContracts/juno/pull/436/files) -- [Governance Spam Prevention](https://github.com/CosmosContracts/juno/pull/394) -- [x/wasmd 30](https://github.com/CosmosContracts/juno/pull/387) -- [x/ibc V4](https://github.com/CosmosContracts/juno/pull/387) -- [x/ibc-fees](https://github.com/CosmosContracts/juno/pull/432) - -V13 is targeted at developers with relayer and user experience improvements as well. - -**FeeShare** will allow contract developers to receive 50% of gas fees executed on their contract. Providing an alternative income source for new business use cases. This also enhances current business models to support developers & grow the ecosystem further. - -The **TokenFactory** will make developers' lives easier, and also make querying users' [DAO](https://daodao.zone/) tokens via MintScan and Keplr possible. By default, CosmWasm smart contracts accept native tokens. However, the only initial native tokens are the staking demons for most chains. This gives the ability for a user to create their token, and manage the tokenomics behind it. Then accept it just as they would any other denomination via the standard [x/bank](https://github.com/cosmos/cosmos-sdk/tree/main/x/bank) module. - -Speaking of relayers, **IBCFees** now helps to fund those who relayer your packets! In the above paragraph, we mention how IBC transfers are feeless for relayers. Fees can still be sent with these packets and bring some income for relayers, thus maintaining public goods infrastructure. The relayers still have to pay the fee on the other chain's token, but this is a positive step in the right direction for variables we can control. diff --git a/app/ante.go b/app/ante.go index a5a60dd71..b3d3e0289 100644 --- a/app/ante.go +++ b/app/ante.go @@ -26,7 +26,8 @@ import ( globalfeekeeper "github.com/CosmosContracts/juno/v17/x/globalfee/keeper" ) -const maxBypassMinFeeMsgGasUsage = 1_000_000 +// Lower back to 1 mil after https://github.com/cosmos/relayer/issues/1255 +const maxBypassMinFeeMsgGasUsage = 2_000_000 // HandlerOptions extends the SDK's AnteHandler options by requiring the IBC // channel keeper and a BankKeeper with an added method for fee sharing. diff --git a/app/app.go b/app/app.go index ee245ed8c..fc664bdcf 100644 --- a/app/app.go +++ b/app/app.go @@ -481,6 +481,9 @@ func GetDefaultBypassFeeMessages() []string { sdk.MsgTypeURL(&ibctransfertypes.MsgTransfer{}), sdk.MsgTypeURL(&ibcchanneltypes.MsgTimeout{}), sdk.MsgTypeURL(&ibcchanneltypes.MsgTimeoutOnClose{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgChannelOpenTry{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgChannelOpenConfirm{}), + sdk.MsgTypeURL(&ibcchanneltypes.MsgChannelOpenAck{}), } } diff --git a/app/export.go b/app/export.go index ae91730c8..6399dbbed 100644 --- a/app/export.go +++ b/app/export.go @@ -2,6 +2,7 @@ package app import ( "encoding/json" + "fmt" "log" tmproto "github.com/cometbft/cometbft/proto/tendermint/types" @@ -71,20 +72,21 @@ func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []str // withdraw all validator commission app.AppKeepers.StakingKeeper.IterateValidators(ctx, func(_ int64, val stakingtypes.ValidatorI) (stop bool) { - _, err := app.AppKeepers.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) - if err != nil { - panic(err) - } + _, _ = app.AppKeepers.DistrKeeper.WithdrawValidatorCommission(ctx, val.GetOperator()) return false }) // withdraw all delegator rewards dels := app.AppKeepers.StakingKeeper.GetAllDelegations(ctx) for _, delegation := range dels { - _, err := app.AppKeepers.DistrKeeper.WithdrawDelegationRewards(ctx, delegation.GetDelegatorAddr(), delegation.GetValidatorAddr()) + valAddr, err := sdk.ValAddressFromBech32(delegation.ValidatorAddress) if err != nil { panic(err) } + + delAddr := sdk.MustAccAddressFromBech32(delegation.DelegatorAddress) + + _, _ = app.AppKeepers.DistrKeeper.WithdrawDelegationRewards(ctx, delAddr, valAddr) } // clear validator slash events @@ -105,20 +107,28 @@ func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []str feePool.CommunityPool = feePool.CommunityPool.Add(scraps...) app.AppKeepers.DistrKeeper.SetFeePool(ctx, feePool) - err := app.AppKeepers.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()) - - return err != nil + if err := app.AppKeepers.DistrKeeper.Hooks().AfterValidatorCreated(ctx, val.GetOperator()); err != nil { + panic(err) + } + return false }) // reinitialize all delegations for _, del := range dels { - err := app.AppKeepers.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, del.GetDelegatorAddr(), del.GetValidatorAddr()) + valAddr, err := sdk.ValAddressFromBech32(del.ValidatorAddress) if err != nil { panic(err) } - err = app.AppKeepers.DistrKeeper.Hooks().AfterDelegationModified(ctx, del.GetDelegatorAddr(), del.GetValidatorAddr()) - if err != nil { - panic(err) + delAddr := sdk.MustAccAddressFromBech32(del.DelegatorAddress) + + if err := app.AppKeepers.DistrKeeper.Hooks().BeforeDelegationCreated(ctx, delAddr, valAddr); err != nil { + // never called as BeforeDelegationCreated always returns nil + panic(fmt.Errorf("error while incrementing period: %w", err)) + } + + if err := app.AppKeepers.DistrKeeper.Hooks().AfterDelegationModified(ctx, delAddr, valAddr); err != nil { + // never called as AfterDelegationModified always returns nil + panic(fmt.Errorf("error while creating a new delegation period record: %w", err)) } } @@ -152,7 +162,7 @@ func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []str counter := int16(0) for ; iter.Valid(); iter.Next() { - addr := sdk.ValAddress(iter.Key()[1:]) + addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key())) validator, found := app.AppKeepers.StakingKeeper.GetValidator(ctx, addr) if !found { panic("expected validator, not found") @@ -167,13 +177,14 @@ func (app *App) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []str counter++ } - err := iter.Close() - if err != nil { - panic(err) + if err := iter.Close(); err != nil { + app.Logger().Error("error while closing the key-value store reverse prefix iterator: ", err) + return } - if _, err := app.AppKeepers.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx); err != nil { - panic(err) + _, err := app.AppKeepers.StakingKeeper.ApplyAndReturnValidatorSetUpdates(ctx) + if err != nil { + log.Fatal(err) } /* Handle slashing state. */ diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 2ecfae80a..0f6f1bb4f 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -82,6 +82,8 @@ import ( upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + dripkeeper "github.com/CosmosContracts/juno/v17/x/drip/keeper" + driptypes "github.com/CosmosContracts/juno/v17/x/drip/types" feesharekeeper "github.com/CosmosContracts/juno/v17/x/feeshare/keeper" feesharetypes "github.com/CosmosContracts/juno/v17/x/feeshare/types" "github.com/CosmosContracts/juno/v17/x/globalfee" @@ -173,6 +175,8 @@ type AppKeepers struct { scopedWasmKeeper capabilitykeeper.ScopedKeeper TokenFactoryKeeper tokenfactorykeeper.Keeper + DripKeeper dripkeeper.Keeper + // Middleware wrapper Ics20WasmHooks *ibc_hooks.WasmHooks HooksICS4Wrapper ibc_hooks.ICS4Middleware @@ -419,7 +423,7 @@ func NewAppKeepers( appKeepers.IBCKeeper.ChannelKeeper, &appKeepers.IBCKeeper.PortKeeper, scopedICQKeeper, - NewQuerierWrapper(bApp), + bApp.GRPCQueryRouter(), ) appKeepers.ICAHostKeeper = icahostkeeper.NewKeeper( @@ -563,6 +567,14 @@ func NewAppKeepers( govModAddress, ) + appKeepers.DripKeeper = dripkeeper.NewKeeper( + appKeepers.keys[driptypes.StoreKey], + appCodec, + appKeepers.BankKeeper, + authtypes.FeeCollectorName, + govModAddress, + ) + // register wasm gov proposal types // The gov proposal types can be individually enabled if len(enabledProposals) != 0 { @@ -765,7 +777,6 @@ func initParamsKeeper(appCodec codec.BinaryCodec, legacyAmino *codec.LegacyAmino paramsKeeper.Subspace(tokenfactorytypes.ModuleName) paramsKeeper.Subspace(feesharetypes.ModuleName) paramsKeeper.Subspace(wasmtypes.ModuleName) - paramsKeeper.Subspace(buildertypes.ModuleName) return paramsKeeper } diff --git a/app/keepers/keys.go b/app/keepers/keys.go index f5995be06..62f3476ab 100644 --- a/app/keepers/keys.go +++ b/app/keepers/keys.go @@ -31,6 +31,7 @@ import ( stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" + driptypes "github.com/CosmosContracts/juno/v17/x/drip/types" feesharetypes "github.com/CosmosContracts/juno/v17/x/feeshare/types" globalfeetypes "github.com/CosmosContracts/juno/v17/x/globalfee/types" minttypes "github.com/CosmosContracts/juno/v17/x/mint/types" @@ -55,6 +56,7 @@ func (appKeepers *AppKeepers) GenerateKeys() { feesharetypes.StoreKey, globalfeetypes.StoreKey, buildertypes.StoreKey, + driptypes.StoreKey, ) appKeepers.tkeys = sdk.NewTransientStoreKeys(paramstypes.TStoreKey) diff --git a/app/keepers/querier.go b/app/keepers/querier.go deleted file mode 100644 index 1c7591307..000000000 --- a/app/keepers/querier.go +++ /dev/null @@ -1,26 +0,0 @@ -// this file used from osmosis, ref: https://github.com/CosmosContracts/juno/v17/blob/2ce971f4c6aa85d3ef7ba33d60e0ae74b923ab83/app/keepers/querier.go -// Original Author: https://github.com/nicolaslara - -package keepers - -import ( - abci "github.com/cometbft/cometbft/abci/types" - - sdk "github.com/cosmos/cosmos-sdk/types" -) - -// QuerierWrapper is a local wrapper around BaseApp that exports only the Queryable interface. -// This is used to pass the baseApp to Async ICQ without exposing all methods -type QuerierWrapper struct { - querier sdk.Queryable -} - -var _ sdk.Queryable = QuerierWrapper{} - -func NewQuerierWrapper(querier sdk.Queryable) QuerierWrapper { - return QuerierWrapper{querier: querier} -} - -func (q QuerierWrapper) Query(req abci.RequestQuery) abci.ResponseQuery { - return q.querier.Query(req) -} diff --git a/app/modules.go b/app/modules.go index b85068794..e085d2124 100644 --- a/app/modules.go +++ b/app/modules.go @@ -60,6 +60,8 @@ import ( upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" encparams "github.com/CosmosContracts/juno/v17/app/params" + "github.com/CosmosContracts/juno/v17/x/drip" + driptypes "github.com/CosmosContracts/juno/v17/x/drip/types" feeshare "github.com/CosmosContracts/juno/v17/x/feeshare" feesharetypes "github.com/CosmosContracts/juno/v17/x/feeshare/types" "github.com/CosmosContracts/juno/v17/x/globalfee" @@ -101,6 +103,7 @@ var ModuleBasics = module.NewBasicManager( icq.AppModuleBasic{}, feegrantmodule.AppModuleBasic{}, tokenfactory.AppModuleBasic{}, + drip.AppModuleBasic{}, feeshare.AppModuleBasic{}, globalfee.AppModuleBasic{}, ibc_hooks.AppModuleBasic{}, @@ -149,6 +152,7 @@ func appModules( ica.NewAppModule(&app.AppKeepers.ICAControllerKeeper, &app.AppKeepers.ICAHostKeeper), crisis.NewAppModule(app.AppKeepers.CrisisKeeper, skipGenesisInvariants, app.GetSubspace(crisistypes.ModuleName)), buildermodule.NewAppModule(appCodec, app.AppKeepers.BuildKeeper), + drip.NewAppModule(app.AppKeepers.DripKeeper, app.AppKeepers.AccountKeeper), // IBC modules ibc_hooks.NewAppModule(app.AppKeepers.AccountKeeper), icq.NewAppModule(app.AppKeepers.ICQKeeper), @@ -219,6 +223,7 @@ func orderBeginBlockers() []string { ibcfeetypes.ModuleName, icqtypes.ModuleName, tokenfactorytypes.ModuleName, + driptypes.ModuleName, feesharetypes.ModuleName, globalfee.ModuleName, wasmtypes.ModuleName, @@ -255,6 +260,7 @@ func orderEndBlockers() []string { ibcfeetypes.ModuleName, icqtypes.ModuleName, tokenfactorytypes.ModuleName, + driptypes.ModuleName, feesharetypes.ModuleName, globalfee.ModuleName, wasmtypes.ModuleName, @@ -291,6 +297,7 @@ func orderInitBlockers() []string { ibcfeetypes.ModuleName, icqtypes.ModuleName, tokenfactorytypes.ModuleName, + driptypes.ModuleName, feesharetypes.ModuleName, globalfee.ModuleName, wasmtypes.ModuleName, diff --git a/app/upgrades/v16/upgrade_test.go b/app/upgrades/v16/upgrade_test.go deleted file mode 100644 index ffea87190..000000000 --- a/app/upgrades/v16/upgrade_test.go +++ /dev/null @@ -1,30 +0,0 @@ -package v16_test - -import ( - "testing" - - "github.com/stretchr/testify/suite" - - "github.com/CosmosContracts/juno/v17/app/apptesting" - v16 "github.com/CosmosContracts/juno/v17/app/upgrades/v16" -) - -type UpgradeTestSuite struct { - apptesting.KeeperTestHelper -} - -func (s *UpgradeTestSuite) SetupTest() { - s.Setup() -} - -func TestKeeperTestSuite(t *testing.T) { - suite.Run(t, new(UpgradeTestSuite)) -} - -// Ensures the test does not error out. -func (s *UpgradeTestSuite) TestUpgrade() { - s.Setup() - - upgradeHeight := int64(5) - s.ConfirmUpgradeSucceeded(v16.UpgradeName, upgradeHeight) -} diff --git a/app/upgrades/v17/constants.go b/app/upgrades/v17/constants.go index e9c81c0b9..6a1e6166c 100644 --- a/app/upgrades/v17/constants.go +++ b/app/upgrades/v17/constants.go @@ -4,6 +4,7 @@ import ( store "github.com/cosmos/cosmos-sdk/store/types" "github.com/CosmosContracts/juno/v17/app/upgrades" + driptypes "github.com/CosmosContracts/juno/v17/x/drip/types" ) // UpgradeName defines the on-chain upgrade name for the upgrade. @@ -12,5 +13,9 @@ const UpgradeName = "v17" var Upgrade = upgrades.Upgrade{ UpgradeName: UpgradeName, CreateUpgradeHandler: CreateV17UpgradeHandler, - StoreUpgrades: store.StoreUpgrades{}, + StoreUpgrades: store.StoreUpgrades{ + Added: []string{ + driptypes.ModuleName, + }, + }, } diff --git a/app/upgrades/v17/upgrade_test.go b/app/upgrades/v17/upgrade_test.go index e02c2e4b8..dded4c49a 100644 --- a/app/upgrades/v17/upgrade_test.go +++ b/app/upgrades/v17/upgrade_test.go @@ -25,6 +25,28 @@ func TestKeeperTestSuite(t *testing.T) { func (s *UpgradeTestSuite) TestUpgrade() { s.Setup() + preUpgradeChecks(s) + upgradeHeight := int64(5) s.ConfirmUpgradeSucceeded(v17.UpgradeName, upgradeHeight) + + postUpgradeChecks(s) +} + +func preUpgradeChecks(s *UpgradeTestSuite) { + mp := s.App.AppKeepers.MintKeeper.GetParams(s.Ctx) + s.Require().Equal(mp.BlocksPerYear, uint64(6311520)) + + sp := s.App.AppKeepers.SlashingKeeper.GetParams(s.Ctx) + s.Require().Equal(sp.SignedBlocksWindow, int64(100)) +} + +func postUpgradeChecks(s *UpgradeTestSuite) { + // Ensure the mint params have doubled + mp := s.App.AppKeepers.MintKeeper.GetParams(s.Ctx) + s.Require().Equal(mp.BlocksPerYear, uint64(6311520*2)) + + // Ensure the slashing params have doubled + sp := s.App.AppKeepers.SlashingKeeper.GetParams(s.Ctx) + s.Require().Equal(sp.SignedBlocksWindow, int64(100*2)) } diff --git a/app/upgrades/v17/upgrades.go b/app/upgrades/v17/upgrades.go index 154b65029..bdeece740 100644 --- a/app/upgrades/v17/upgrades.go +++ b/app/upgrades/v17/upgrades.go @@ -9,12 +9,13 @@ import ( "github.com/CosmosContracts/juno/v17/app/keepers" "github.com/CosmosContracts/juno/v17/app/upgrades" + driptypes "github.com/CosmosContracts/juno/v17/x/drip/types" ) func CreateV17UpgradeHandler( mm *module.Manager, cfg module.Configurator, - _ *keepers.AppKeepers, + keepers *keepers.AppKeepers, ) upgradetypes.UpgradeHandler { return func(ctx sdk.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { logger := ctx.Logger().With("upgrade", UpgradeName) @@ -30,6 +31,29 @@ func CreateV17UpgradeHandler( } logger.Info(fmt.Sprintf("post migrate version map: %v", versionMap)) + // x/Mint + // Double blocks per year (from 6 seconds to 3 = 2x blocks per year) + mintParams := keepers.MintKeeper.GetParams(ctx) + mintParams.BlocksPerYear *= 2 + if err = keepers.MintKeeper.SetParams(ctx, mintParams); err != nil { + return nil, err + } + logger.Info(fmt.Sprintf("updated minted blocks per year logic to %v", mintParams)) + + // x/Slashing + // Double slashing window due to double blocks per year + slashingParams := keepers.SlashingKeeper.GetParams(ctx) + slashingParams.SignedBlocksWindow *= 2 + if err := keepers.SlashingKeeper.SetParams(ctx, slashingParams); err != nil { + return nil, err + } + logger.Info(fmt.Sprintf("updated slashing params to %v", slashingParams)) + + // x/drip + if err := keepers.DripKeeper.SetParams(ctx, driptypes.DefaultParams()); err != nil { + return nil, err + } + return versionMap, err } } diff --git a/cmd/junod/cmd/root.go b/cmd/junod/cmd/root.go index ccb66a6a8..9e71d0e27 100644 --- a/cmd/junod/cmd/root.go +++ b/cmd/junod/cmd/root.go @@ -5,6 +5,7 @@ import ( "io" "os" "path/filepath" + "time" wasm "github.com/CosmWasm/wasmd/x/wasm" wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" @@ -89,8 +90,14 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { return err } + // 2 seconds + 1 second tendermint = 3 second blocks + timeoutCommit := 2 * time.Second + customAppTemplate, customAppConfig := initAppConfig() - customTMConfig := initTendermintConfig() + customTMConfig := initTendermintConfig(timeoutCommit) + + // Force faster block times + os.Setenv("JUNOD_CONSENSUS_TIMEOUT_COMMIT", cast.ToString(timeoutCommit)) return server.InterceptConfigsPreRunHandler(cmd, customAppTemplate, customAppConfig, customTMConfig) }, @@ -103,15 +110,15 @@ func NewRootCmd() (*cobra.Command, params.EncodingConfig) { // initTendermintConfig helps to override default Tendermint Config values. // return tmcfg.DefaultConfig if no custom configuration is required for the application. -func initTendermintConfig() *tmcfg.Config { +func initTendermintConfig(timeoutCommit time.Duration) *tmcfg.Config { cfg := tmcfg.DefaultConfig() // these values put a higher strain on node memory // cfg.P2P.MaxNumInboundPeers = 100 // cfg.P2P.MaxNumOutboundPeers = 40 - // 2 seconds + 1 second tendermint = 3 second blocks (v15 upgrade) - // cfg.Consensus.TimeoutCommit = 2 * time.Second + // While this is set, it only applies to new configs. + cfg.Consensus.TimeoutCommit = timeoutCommit return cfg } diff --git a/contrib/devtools/Dockerfile b/contrib/devtools/Dockerfile index 53592ff6a..a2d91ad31 100644 --- a/contrib/devtools/Dockerfile +++ b/contrib/devtools/Dockerfile @@ -3,7 +3,7 @@ # docker run --rm -v $(pwd):/workspace --workdir /workspace cosmossdk-proto sh ./scripts/protocgen.sh FROM bufbuild/buf:1.9.0 as BUILDER -FROM golang:1.19-alpine +FROM golang:1.20-alpine RUN apk add --no-cache \ nodejs \ diff --git a/docker/run_junod.sh b/docker/run_junod.sh index d4be280ea..2761bbbc5 100755 --- a/docker/run_junod.sh +++ b/docker/run_junod.sh @@ -6,4 +6,4 @@ if test -n "$1"; then fi mkdir -p /root/log -junod start --rpc.laddr tcp://0.0.0.0:26657 --minimum-gas-prices 0.0001ujunox--trace +junod start --rpc.laddr tcp://0.0.0.0:26657 --minimum-gas-prices 0.0001ujunox --trace diff --git a/go.mod b/go.mod index 3085651a0..2b051336b 100644 --- a/go.mod +++ b/go.mod @@ -14,9 +14,9 @@ require ( github.com/cometbft/cometbft-db v0.8.0 github.com/cosmos/cosmos-sdk v0.47.4 github.com/cosmos/gogoproto v1.4.10 - github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.0-20230629164013-34f5e666f806 - github.com/cosmos/ibc-apps/modules/async-icq/v7 v7.0.0-20230629164013-34f5e666f806 - github.com/cosmos/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20230629164013-34f5e666f806 + github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.0 + github.com/cosmos/ibc-apps/modules/async-icq/v7 v7.0.0 + github.com/cosmos/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20230803181732-7c8f814d3b79 github.com/cosmos/ibc-go/v7 v7.2.0 github.com/golang/protobuf v1.5.3 github.com/gorilla/mux v1.8.0 diff --git a/go.sum b/go.sum index 608401b74..612bb431e 100644 --- a/go.sum +++ b/go.sum @@ -400,12 +400,12 @@ github.com/cosmos/gogoproto v1.4.10 h1:QH/yT8X+c0F4ZDacDv3z+xE3WU1P1Z3wQoLMBRJoK github.com/cosmos/gogoproto v1.4.10/go.mod h1:3aAZzeRWpAwr+SS/LLkICX2/kDFyaYVzckBDzygIxek= github.com/cosmos/iavl v0.20.0 h1:fTVznVlepH0KK8NyKq8w+U7c2L6jofa27aFX6YGlm38= github.com/cosmos/iavl v0.20.0/go.mod h1:WO7FyvaZJoH65+HFOsDir7xU9FWk2w9cHXNW1XHcl7A= -github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.0-20230629164013-34f5e666f806 h1:iFWb/KrnP5jthNZ23l72wqE8nzHJHzoVe22giUfJce8= -github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.0-20230629164013-34f5e666f806/go.mod h1:fctjEnz9xaBFOlmYYPdKL8Hs1Y3GUKilSwsJdqBb5QU= -github.com/cosmos/ibc-apps/modules/async-icq/v7 v7.0.0-20230629164013-34f5e666f806 h1:QIRbYK2r9Ww8ua8j1S7c4i+cV6AiBLxghIdwZJW14C4= -github.com/cosmos/ibc-apps/modules/async-icq/v7 v7.0.0-20230629164013-34f5e666f806/go.mod h1:/P6l2bWo2AR3rrsfs0DHuFZO3Imzb93sxFD3ihrIgw4= -github.com/cosmos/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20230629164013-34f5e666f806 h1:ygkYdRulR9lGLSYOAQUBOBTyeRKZc1svBw5Bnv9Boe4= -github.com/cosmos/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20230629164013-34f5e666f806/go.mod h1:JwHFbo1oX/ht4fPpnPvmhZr+dCkYK1Vihw+vZE9umR4= +github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.0 h1:i9esYyZ5ExpZOgxrLMQhY2jDTVYiaD8yUeqXN9QBgbk= +github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v7 v7.0.0/go.mod h1:fctjEnz9xaBFOlmYYPdKL8Hs1Y3GUKilSwsJdqBb5QU= +github.com/cosmos/ibc-apps/modules/async-icq/v7 v7.0.0 h1:mMHedP3Q+mz5gpOWNz0P+X8hxPdamylrBKc/P2cFakA= +github.com/cosmos/ibc-apps/modules/async-icq/v7 v7.0.0/go.mod h1:/P6l2bWo2AR3rrsfs0DHuFZO3Imzb93sxFD3ihrIgw4= +github.com/cosmos/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20230803181732-7c8f814d3b79 h1:pCxyhIxgWTabAQC5UerkITraHG3SwajdLKKMCFDWCv4= +github.com/cosmos/ibc-apps/modules/ibc-hooks/v7 v7.0.0-20230803181732-7c8f814d3b79/go.mod h1:JwHFbo1oX/ht4fPpnPvmhZr+dCkYK1Vihw+vZE9umR4= github.com/cosmos/ibc-go/v7 v7.2.0 h1:dx0DLUl7rxdyZ8NiT6UsrbzKOJx/w7s+BOaewFRH6cg= github.com/cosmos/ibc-go/v7 v7.2.0/go.mod h1:OOcjKIRku/j1Xs1RgKK0yvKRrJ5iFuZYMetR1n3yMlc= github.com/cosmos/ics23/go v0.10.0 h1:iXqLLgp2Lp+EdpIuwXTYIQU+AiHj9mOC2X9ab++bZDM= diff --git a/interchaintest/chain_upgrade_test.go b/interchaintest/chain_upgrade_test.go index 663ca8ea6..e96a58cc3 100644 --- a/interchaintest/chain_upgrade_test.go +++ b/interchaintest/chain_upgrade_test.go @@ -18,28 +18,38 @@ import ( ) const ( + chainName = "juno" + upgradeName = "v17" + haltHeightDelta = uint64(9) // will propose upgrade this many blocks in the future blocksAfterUpgrade = uint64(7) ) +var ( + // baseChain is the current version of the chain that will be upgraded from + baseChain = ibc.DockerImage{ + Repository: JunoMainRepo, + Version: "v16.0.0", + UidGid: "1025:1025", + } +) + func TestBasicJunoUpgrade(t *testing.T) { repo, version := GetDockerImageInfo() - startVersion := "v16.0.0" - upgradeName := "v17" - CosmosChainUpgradeTest(t, "juno", startVersion, version, repo, upgradeName) + CosmosChainUpgradeTest(t, chainName, version, repo, upgradeName) } -func CosmosChainUpgradeTest(t *testing.T, chainName, initialVersion, upgradeBranchVersion, upgradeRepo, upgradeName string) { +func CosmosChainUpgradeTest(t *testing.T, chainName, upgradeBranchVersion, upgradeRepo, upgradeName string) { if testing.Short() { t.Skip("skipping in short mode") } t.Parallel() - t.Log(chainName, initialVersion, upgradeBranchVersion, upgradeRepo, upgradeName) + t.Log(chainName, upgradeBranchVersion, upgradeRepo, upgradeName) numVals, numNodes := 4, 4 - chains := CreateThisBranchChain(t, numVals, numNodes) + chains := CreateChain(t, numVals, numNodes, baseChain) chain := chains[0].(*cosmos.CosmosChain) ic, ctx, client, _ := BuildInitialChain(t, chains) @@ -62,7 +72,6 @@ func CosmosChainUpgradeTest(t *testing.T, chainName, initialVersion, upgradeBran ValidatorVoting(t, ctx, chain, proposalID, height, haltHeight) UpgradeNodes(t, ctx, chain, client, haltHeight, upgradeRepo, upgradeBranchVersion) - } func UpgradeNodes(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, client *client.Client, haltHeight uint64, upgradeRepo, upgradeBranchVersion string) { diff --git a/interchaintest/contracts/tokenfactory_core.wasm b/interchaintest/contracts/tokenfactory_core.wasm index b6994d28f..532d536c0 100644 Binary files a/interchaintest/contracts/tokenfactory_core.wasm and b/interchaintest/contracts/tokenfactory_core.wasm differ diff --git a/interchaintest/go.mod b/interchaintest/go.mod index 7af0faed5..a3820e9be 100644 --- a/interchaintest/go.mod +++ b/interchaintest/go.mod @@ -1,6 +1,6 @@ module github.com/CosmosContracts/juno/tests/interchaintest -go 1.19 +go 1.20 replace ( // interchaintest supports ICS features so we need this for now diff --git a/interchaintest/helpers/drip.go b/interchaintest/helpers/drip.go new file mode 100644 index 000000000..73eab5113 --- /dev/null +++ b/interchaintest/helpers/drip.go @@ -0,0 +1,34 @@ +package helpers + +import ( + "context" + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/require" +) + +func DripTokens(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, coinAmt string) { + // amount is #utoken + cmd := []string{"junod", "tx", "drip", "distribute-tokens", coinAmt, + "--node", chain.GetRPCAddress(), + "--home", chain.HomeDir(), + "--chain-id", chain.Config().ChainID, + "--from", user.KeyName(), + "--gas", "500000", + "--keyring-dir", chain.HomeDir(), + "--keyring-backend", keyring.BackendTest, + "-y", + } + stdout, _, err := chain.Exec(ctx, cmd, nil) + require.NoError(t, err) + + debugOutput(t, string(stdout)) + + if err := testutil.WaitForBlocks(ctx, 2, chain); err != nil { + t.Fatal(err) + } +} diff --git a/interchaintest/helpers/query_helpers.go b/interchaintest/helpers/query_helpers.go index 92813221b..4b88e5b60 100644 --- a/interchaintest/helpers/query_helpers.go +++ b/interchaintest/helpers/query_helpers.go @@ -2,7 +2,10 @@ package helpers import ( "context" + "encoding/json" + "fmt" "testing" + "time" "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" "github.com/stretchr/testify/require" @@ -22,3 +25,65 @@ func GetUnityContractWithdrawalReadyTime(t *testing.T, ctx context.Context, chai require.NoError(t, err) return res } + +// From stakingtypes.Validator +type Vals struct { + Validators []struct { + OperatorAddress string `json:"operator_address"` + ConsensusPubkey struct { + Type string `json:"@type"` + Key string `json:"key"` + } `json:"consensus_pubkey"` + Jailed bool `json:"jailed"` + Status string `json:"status"` + Tokens string `json:"tokens"` + DelegatorShares string `json:"delegator_shares"` + Description struct { + Moniker string `json:"moniker"` + Identity string `json:"identity"` + Website string `json:"website"` + SecurityContact string `json:"security_contact"` + Details string `json:"details"` + } `json:"description"` + UnbondingHeight string `json:"unbonding_height"` + UnbondingTime time.Time `json:"unbonding_time"` + Commission struct { + CommissionRates struct { + Rate string `json:"rate"` + MaxRate string `json:"max_rate"` + MaxChangeRate string `json:"max_change_rate"` + } `json:"commission_rates"` + UpdateTime time.Time `json:"update_time"` + } `json:"commission"` + MinSelfDelegation string `json:"min_self_delegation"` + UnbondingOnHoldRefCount string `json:"unbonding_on_hold_ref_count"` + UnbondingIds []any `json:"unbonding_ids"` + } `json:"validators"` + Pagination struct { + NextKey any `json:"next_key"` + Total string `json:"total"` + } `json:"pagination"` +} + +func GetValidators(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain) Vals { + var res Vals + + cmd := []string{"junod", "query", "staking", "validators", + "--node", chain.GetRPCAddress(), + "--chain-id", chain.Config().ChainID, + "--output", "json", + } + + stdout, _, err := chain.Exec(ctx, cmd, nil) + require.NoError(t, err) + + // print stdout + fmt.Println(string(stdout)) + + // put the stdout json into res + if err := json.Unmarshal(stdout, &res); err != nil { + t.Fatal(err) + } + + return res +} diff --git a/interchaintest/helpers/staking.go b/interchaintest/helpers/staking.go new file mode 100644 index 000000000..43d67799a --- /dev/null +++ b/interchaintest/helpers/staking.go @@ -0,0 +1,55 @@ +package helpers + +import ( + "context" + "testing" + + "github.com/cosmos/cosmos-sdk/crypto/keyring" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + "github.com/strangelove-ventures/interchaintest/v7/ibc" + "github.com/strangelove-ventures/interchaintest/v7/testutil" + "github.com/stretchr/testify/require" +) + +func StakeTokens(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, valoper, coinAmt string) { + // amount is #utoken + cmd := []string{"junod", "tx", "staking", "delegate", valoper, coinAmt, + "--node", chain.GetRPCAddress(), + "--home", chain.HomeDir(), + "--chain-id", chain.Config().ChainID, + "--from", user.KeyName(), + "--gas", "500000", + "--keyring-dir", chain.HomeDir(), + "--keyring-backend", keyring.BackendTest, + "-y", + } + stdout, _, err := chain.Exec(ctx, cmd, nil) + require.NoError(t, err) + + debugOutput(t, string(stdout)) + + if err := testutil.WaitForBlocks(ctx, 2, chain); err != nil { + t.Fatal(err) + } +} + +func ClaimStakingRewards(t *testing.T, ctx context.Context, chain *cosmos.CosmosChain, user ibc.Wallet, valoper string) { + cmd := []string{"junod", "tx", "distribution", "withdraw-rewards", valoper, + "--node", chain.GetRPCAddress(), + "--home", chain.HomeDir(), + "--chain-id", chain.Config().ChainID, + "--from", user.KeyName(), + "--gas", "500000", + "--keyring-dir", chain.HomeDir(), + "--keyring-backend", keyring.BackendTest, + "-y", + } + stdout, _, err := chain.Exec(ctx, cmd, nil) + require.NoError(t, err) + + debugOutput(t, string(stdout)) + + if err := testutil.WaitForBlocks(ctx, 2, chain); err != nil { + t.Fatal(err) + } +} diff --git a/interchaintest/module_drip_test.go b/interchaintest/module_drip_test.go new file mode 100644 index 000000000..3d468446a --- /dev/null +++ b/interchaintest/module_drip_test.go @@ -0,0 +1,85 @@ +package interchaintest + +import ( + "fmt" + "testing" + + "github.com/strangelove-ventures/interchaintest/v7" + "github.com/strangelove-ventures/interchaintest/v7/chain/cosmos" + + helpers "github.com/CosmosContracts/juno/tests/interchaintest/helpers" +) + +// TestJunoDrip ensures the drip module properly distributes tokens from whitelisted accounts. +func TestJunoDrip(t *testing.T) { + t.Parallel() + + // Setup new pre determined user (from test_node.sh) + mnemonic := "decorate bright ozone fork gallery riot bus exhaust worth way bone indoor calm squirrel merry zero scheme cotton until shop any excess stage laundry" + addr := "juno1hj5fveer5cjtn4wd6wstzugjfdxzl0xps73ftl" + + // Base setup + newCfg := junoConfig + newCfg.ModifyGenesis = cosmos.ModifyGenesis(append(defaultGenesisKV, []cosmos.GenesisKV{ + { + Key: "app_state.drip.params.allowed_addresses", + Value: []string{addr}, + }, + }...)) + + chains := CreateChainWithCustomConfig(t, 1, 0, newCfg) + ic, ctx, _, _ := BuildInitialChain(t, chains) + + // Chains + juno := chains[0].(*cosmos.CosmosChain) + nativeDenom := juno.Config().Denom + + // User + user, err := interchaintest.GetAndFundTestUserWithMnemonic(ctx, "default", mnemonic, int64(1000000_000_000), juno) + if err != nil { + t.Fatal(err) + } + + // New TF token to distributes + tfDenom := helpers.CreateTokenFactoryDenom(t, ctx, juno, user, "dripme", fmt.Sprintf("0%s", Denom)) + distributeAmt := uint64(1_000_000) + helpers.MintTokenFactoryDenom(t, ctx, juno, user, distributeAmt, tfDenom) + if balance, err := juno.GetBalance(ctx, user.FormattedAddress(), tfDenom); err != nil { + t.Fatal(err) + } else if uint64(balance) != distributeAmt { + t.Fatalf("balance not %d, got %d", distributeAmt, balance) + } + + // Stake some tokens + vals := helpers.GetValidators(t, ctx, juno) + valoper := vals.Validators[0].OperatorAddress + + stakeAmt := 100000_000_000 + helpers.StakeTokens(t, ctx, juno, user, valoper, fmt.Sprintf("%d%s", stakeAmt, nativeDenom)) + + // Drip the TF Tokens to all stakers + distribute := int64(1_000_000) + helpers.DripTokens(t, ctx, juno, user, fmt.Sprintf("%d%s", distribute, tfDenom)) + + // Claim staking rewards to capture the drip + helpers.ClaimStakingRewards(t, ctx, juno, user, valoper) + + // Check balances has the TF Denom from the claim + bals, _ := juno.AllBalances(ctx, user.FormattedAddress()) + fmt.Println("balances", bals) + + found := false + for _, bal := range bals { + if bal.Denom == tfDenom { + found = true + break + } + } + if !found { + t.Fatal("did not find drip token") + } + + t.Cleanup(func() { + _ = ic.Close() + }) +} diff --git a/interchaintest/module_feeshare_test.go b/interchaintest/module_feeshare_test.go index 098346a28..04a1bd663 100644 --- a/interchaintest/module_feeshare_test.go +++ b/interchaintest/module_feeshare_test.go @@ -19,7 +19,6 @@ func TestJunoFeeShare(t *testing.T) { // Chains juno := chains[0].(*cosmos.CosmosChain) - t.Log("juno.GetHostRPCAddress()", juno.GetHostRPCAddress()) nativeDenom := juno.Config().Denom diff --git a/interchaintest/module_tokenfactory_test.go b/interchaintest/module_tokenfactory_test.go index 69d855612..86004c45c 100644 --- a/interchaintest/module_tokenfactory_test.go +++ b/interchaintest/module_tokenfactory_test.go @@ -51,7 +51,7 @@ func TestJunoTokenFactory(t *testing.T) { } // This allows the uaddr here to mint tokens on behalf of the contract. Typically you only allow a contract here, but this is testing. - coreInitMsg := fmt.Sprintf(`{"allowed_mint_addresses":["%s"],"denoms":["%s"]}`, uaddr, tfDenom) + coreInitMsg := fmt.Sprintf(`{"allowed_mint_addresses":["%s"],"existing_denoms":["%s"]}`, uaddr, tfDenom) _, coreTFContract := helpers.SetupContract(t, ctx, juno, user.KeyName(), "contracts/tokenfactory_core.wasm", coreInitMsg) t.Log("coreContract", coreTFContract) diff --git a/interchaintest/setup.go b/interchaintest/setup.go index e775ef275..b2ec1f018 100644 --- a/interchaintest/setup.go +++ b/interchaintest/setup.go @@ -14,10 +14,9 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap/zaptest" - ibclocalhost "github.com/cosmos/ibc-go/v7/modules/light-clients/09-localhost" - sdk "github.com/cosmos/cosmos-sdk/types" testutil "github.com/cosmos/cosmos-sdk/types/module/testutil" + ibclocalhost "github.com/cosmos/ibc-go/v7/modules/light-clients/09-localhost" feesharetypes "github.com/CosmosContracts/juno/v17/x/feeshare/types" tokenfactorytypes "github.com/CosmosContracts/juno/v17/x/tokenfactory/types" @@ -95,25 +94,28 @@ func junoEncoding() *testutil.TestEncodingConfig { feesharetypes.RegisterInterfaces(cfg.InterfaceRegistry) tokenfactorytypes.RegisterInterfaces(cfg.InterfaceRegistry) - - return &cfg } -// This allows for us to test -func FundSpecificUsers() { +// CreateChain generates a new chain with a custom image (useful for upgrades) +func CreateChain(t *testing.T, numVals, numFull int, img ibc.DockerImage) []ibc.Chain { + cfg := junoConfig + cfg.Images = []ibc.DockerImage{img} + return CreateChainWithCustomConfig(t, numVals, numFull, cfg) } -// Base chain, no relaying off this branch (or juno:local if no branch is provided.) +// CreateThisBranchChain generates this branch's chain (ex: from the commit) func CreateThisBranchChain(t *testing.T, numVals, numFull int) []ibc.Chain { - // Create chain factory with Juno on this current branch + return CreateChain(t, numVals, numFull, JunoImage) +} +func CreateChainWithCustomConfig(t *testing.T, numVals, numFull int, config ibc.ChainConfig) []ibc.Chain { cf := interchaintest.NewBuiltinChainFactory(zaptest.NewLogger(t), []*interchaintest.ChainSpec{ { Name: "juno", ChainName: "juno", - Version: junoVersion, - ChainConfig: junoConfig, + Version: config.Images[0].Version, + ChainConfig: config, NumValidators: &numVals, NumFullNodes: &numFull, }, diff --git a/proto/Dockerfile b/proto/Dockerfile index d5a673c53..f5eb9c7fb 100644 --- a/proto/Dockerfile +++ b/proto/Dockerfile @@ -3,7 +3,7 @@ FROM bufbuild/buf:1.7.0 as BUILDER -FROM golang:1.19-alpine +FROM golang:1.20-alpine RUN apk add --no-cache \ diff --git a/proto/buf.lock b/proto/buf.lock index 373656974..7906031c4 100644 --- a/proto/buf.lock +++ b/proto/buf.lock @@ -5,19 +5,24 @@ deps: owner: cosmos repository: cosmos-proto commit: 1935555c206d4afb9e94615dfd0fad31 + digest: shake256:c74d91a3ac7ae07d579e90eee33abf9b29664047ac8816500cf22c081fec0d72d62c89ce0bebafc1f6fec7aa5315be72606717740ca95007248425102c365377 - remote: buf.build owner: cosmos repository: cosmos-sdk commit: 954f7b05f38440fc8250134b15adec47 + digest: shake256:2ab4404fd04a7d1d52df0e2d0f2d477a3d83ffd88d876957bf3fedfd702c8e52833d65b3ce1d89a3c5adf2aab512616b0e4f51d8463f07eda9a8a3317ee3ac54 - remote: buf.build owner: cosmos repository: gogo-proto commit: 34d970b699f84aa382f3c29773a60836 + digest: shake256:3d3bee5229ba579e7d19ffe6e140986a228b48a8c7fe74348f308537ab95e9135210e81812489d42cd8941d33ff71f11583174ccc5972e86e6112924b6ce9f04 - remote: buf.build owner: cosmos repository: ics23 commit: 55085f7c710a45f58fa09947208eb70b + digest: shake256:9bf0bc495b5a11c88d163d39ef521bc4b00bc1374a05758c91d82821bdc61f09e8c2c51dda8452529bf80137f34d852561eacbe9550a59015d51cecb0dacb628 - remote: buf.build owner: googleapis repository: googleapis commit: 8d7204855ec14631a499bd7393ce1970 + digest: shake256:40bf4112960cad01281930beed85829910768e32e80e986791596853eccd42c0cbd9d96690b918f658020d2d427e16f8b6514e2ac7f4a10306fd32e77be44329 diff --git a/proto/juno/drip/v1/genesis.proto b/proto/juno/drip/v1/genesis.proto new file mode 100644 index 000000000..cdde15ed0 --- /dev/null +++ b/proto/juno/drip/v1/genesis.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package juno.drip.v1; + +import "gogoproto/gogo.proto"; +option go_package = "github.com/CosmosContracts/juno/x/drip/types"; + +// GenesisState defines the module's genesis state. +message GenesisState { + // params are the drip module parameters + Params params = 1 [ (gogoproto.nullable) = false ]; +} + +// Params defines the drip module params +message Params { + // enable_drip defines a parameter to enable the drip module + bool enable_drip = 1; + + // allowed_addresses defines the list of addresses authorized to use the module + repeated string allowed_addresses = 3 [ (gogoproto.moretags) = "yaml:\"addresses\"" ]; +} diff --git a/proto/juno/drip/v1/query.proto b/proto/juno/drip/v1/query.proto new file mode 100644 index 000000000..b5dec251e --- /dev/null +++ b/proto/juno/drip/v1/query.proto @@ -0,0 +1,27 @@ +syntax = "proto3"; +package juno.drip.v1; + +import "cosmos/base/query/v1beta1/pagination.proto"; +import "juno/drip/v1/genesis.proto"; +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; + +option go_package = "github.com/CosmosContracts/juno/x/drip/types"; + +// Query defines the gRPC querier service. +service Query { + + // Params retrieves the Drip module params + rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { + option (google.api.http).get = "/juno/drip/v1/params"; + } + +} +// 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 is the returned parameter from the module + Params params = 1 [ (gogoproto.nullable) = false ]; +} diff --git a/proto/juno/drip/v1/tx.proto b/proto/juno/drip/v1/tx.proto new file mode 100644 index 000000000..c306bf729 --- /dev/null +++ b/proto/juno/drip/v1/tx.proto @@ -0,0 +1,59 @@ +syntax = "proto3"; +package juno.drip.v1; + +import "gogoproto/gogo.proto"; +import "google/api/annotations.proto"; +import "amino/amino.proto"; +import "cosmos/base/v1beta1/coin.proto"; +import "cosmos/msg/v1/msg.proto"; +import "cosmos_proto/cosmos.proto"; +import "juno/drip/v1/genesis.proto"; + +option go_package = "github.com/CosmosContracts/juno/x/drip/types"; + +// Msg defines the fees Msg service. +service Msg { + // DistributeTokens distribute the sent tokens to all stakers in the next block + rpc DistributeTokens(MsgDistributeTokens) + returns (MsgDistributeTokensResponse) { + option (google.api.http).post = "/juno/drip/v1/tx/distribute_tokens"; + }; + + rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); +} + +// MsgDistributeTokens defines a message that registers a Distribution of tokens. +message MsgDistributeTokens { + option (gogoproto.equal) = false; + // sender_address is the bech32 address of message sender. + string sender_address = 1; + + // amount is the amount being airdropped to stakers + repeated cosmos.base.v1beta1.Coin amount = 2 [ + (gogoproto.nullable) = false, + (amino.dont_omitempty) = true, + (amino.encoding) = "legacy_coins", + (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins" + ]; +} + +// MsgDistributeTokensResponse defines the MsgDistributeTokens response type +message MsgDistributeTokensResponse {} + +// MsgUpdateParams is the Msg/UpdateParams request type. +// +// Since: cosmos-sdk 0.47 +message MsgUpdateParams { + option (cosmos.msg.v1.signer) = "authority"; + option (amino.name) = "cosmos-sdk/x/auth/MsgUpdateParams"; + + // authority is the address that controls the module (defaults to x/gov unless overwritten). + string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; + + // params defines the x/auth parameters to update. + // + // NOTE: All parameters must be supplied. + Params params = 2 [(gogoproto.nullable) = false, (amino.dont_omitempty) = true]; +} + +message MsgUpdateParamsResponse {} diff --git a/scripts/protocgen.sh b/scripts/protocgen.sh index 2853fa7a7..c92ba4d56 100644 --- a/scripts/protocgen.sh +++ b/scripts/protocgen.sh @@ -30,7 +30,7 @@ cd .. buf generate # move proto files to the right places -cp -r ./github.com/CosmosContracts/x/* x/ +cp -r ./github.com/CosmosContracts/juno/x/* x/ cp -r ./github.com/cosmos/gaia/x/* x/ diff --git a/scripts/test_node.sh b/scripts/test_node.sh index 548944a6b..5cf959bbf 100755 --- a/scripts/test_node.sh +++ b/scripts/test_node.sh @@ -70,6 +70,8 @@ from_scratch () { # Custom Modules # GlobalFee update_test_genesis '.app_state["globalfee"]["params"]["minimum_gas_prices"]=[{"amount":"0.002500000000000000","denom":"ujuno"}]' + # Drip + update_test_genesis '.app_state["drip"]["params"]["allowed_addresses"]=["juno1hj5fveer5cjtn4wd6wstzugjfdxzl0xps73ftl","juno1efd63aw40lxf3n4mhf7dzhjkr453axurv2zdzk"]' # TokenFactory # update_test_genesis '.app_state["tokenfactory"]["params"]["denom_creation_fee"]=[{"denom":"ujuno","amount":"100"}]' diff --git a/x/drip/README.md b/x/drip/README.md new file mode 100644 index 000000000..52943b8b7 --- /dev/null +++ b/x/drip/README.md @@ -0,0 +1,7 @@ +# Drip + +This module allows specific addresses (usually smart contracts) to send tokens to the fee_pool module in order to perform a live airdrop to Juno Stakers. + +[Drip Spec](spec/README.md) + +--- diff --git a/x/drip/client/cli/query.go b/x/drip/client/cli/query.go new file mode 100644 index 000000000..0e7386761 --- /dev/null +++ b/x/drip/client/cli/query.go @@ -0,0 +1,59 @@ +package cli + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + + "github.com/CosmosContracts/juno/v17/x/drip/types" +) + +// GetQueryCmd returns the cli query commands for this module +func GetQueryCmd() *cobra.Command { + feesQueryCmd := &cobra.Command{ + Use: types.ModuleName, + Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + feesQueryCmd.AddCommand( + GetCmdQueryParams(), + ) + + return feesQueryCmd +} + +// GetCmdQueryParams implements a command to return the current parameters. +func GetCmdQueryParams() *cobra.Command { + cmd := &cobra.Command{ + Use: "params", + Short: "Query the current feeshare module parameters", + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, _ []string) error { + clientCtx, err := client.GetClientQueryContext(cmd) + if err != nil { + return err + } + queryClient := types.NewQueryClient(clientCtx) + + params := &types.QueryParamsRequest{} + + res, err := queryClient.Params(context.Background(), params) + if err != nil { + return err + } + + return clientCtx.PrintProto(&res.Params) + }, + } + + flags.AddQueryFlagsToCmd(cmd) + + return cmd +} diff --git a/x/drip/client/cli/tx.go b/x/drip/client/cli/tx.go new file mode 100644 index 000000000..194815a18 --- /dev/null +++ b/x/drip/client/cli/tx.go @@ -0,0 +1,64 @@ +package cli + +import ( + "github.com/spf13/cobra" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/client/flags" + "github.com/cosmos/cosmos-sdk/client/tx" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/CosmosContracts/juno/v17/x/drip/types" +) + +// NewTxCmd returns a root CLI command handler for certain modules transaction commands. +func NewTxCmd() *cobra.Command { + txCmd := &cobra.Command{ + Use: types.ModuleName, + Short: "Drip subcommands", + DisableFlagParsing: true, + SuggestionsMinimumDistance: 2, + RunE: client.ValidateCmd, + } + + txCmd.AddCommand( + NewDistributeToken(), + ) + return txCmd +} + +// NewDistributeToken returns a CLI command handler for distributing tokens. +func NewDistributeToken() *cobra.Command { + cmd := &cobra.Command{ + Use: "distribute-tokens [amount]", + Short: "Distribute tokens to all stakers in the next block.", + Long: "Distribute tokens to all stakers in the next block **NOTE** ALL the tokens sent will be distributed to stakers in one shot at the next block. If you want to do a gradual airdrop, execute this transaction multiple times splitting the amount. This message can be executed only by authorized addresses.", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cliCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + + sender := cliCtx.GetFromAddress() + amount, err := sdk.ParseCoinsNormalized(args[0]) + if err != nil { + return err + } + + msg := &types.MsgDistributeTokens{ + SenderAddress: sender.String(), + Amount: amount, + } + + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxCLI(cliCtx, cmd.Flags(), msg) + }, + } + + flags.AddTxFlagsToCmd(cmd) + return cmd +} diff --git a/x/drip/genesis.go b/x/drip/genesis.go new file mode 100644 index 000000000..7da050cb9 --- /dev/null +++ b/x/drip/genesis.go @@ -0,0 +1,26 @@ +package drip + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/CosmosContracts/juno/v17/x/drip/keeper" + "github.com/CosmosContracts/juno/v17/x/drip/types" +) + +// InitGenesis import module genesis +func InitGenesis( + ctx sdk.Context, + k keeper.Keeper, + data types.GenesisState, +) { + if err := k.SetParams(ctx, data.Params); err != nil { + panic(err) + } +} + +// ExportGenesis export module state +func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { + return &types.GenesisState{ + Params: k.GetParams(ctx), + } +} diff --git a/x/drip/genesis_test.go b/x/drip/genesis_test.go new file mode 100644 index 000000000..17a9a1b76 --- /dev/null +++ b/x/drip/genesis_test.go @@ -0,0 +1,114 @@ +package drip_test + +import ( + "fmt" + "testing" + + "github.com/stretchr/testify/suite" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/CosmosContracts/juno/v17/app" + drip "github.com/CosmosContracts/juno/v17/x/drip" + "github.com/CosmosContracts/juno/v17/x/drip/types" +) + +type GenesisTestSuite struct { + suite.Suite + + ctx sdk.Context + + app *app.App + genesis types.GenesisState +} + +func TestGenesisTestSuite(t *testing.T) { + suite.Run(t, new(GenesisTestSuite)) +} + +func (suite *GenesisTestSuite) SetupTest() { + app := app.Setup(suite.T()) + ctx := app.BaseApp.NewContext(false, tmproto.Header{ + ChainID: "testing", + }) + + suite.app = app + suite.ctx = ctx + + suite.genesis = *types.DefaultGenesisState() +} + +func (suite *GenesisTestSuite) TestDripInitGenesis() { + testCases := []struct { + name string + genesis types.GenesisState + expPanic bool + }{ + { + "default genesis", + suite.genesis, + false, + }, + { + "custom genesis - drip enabled, no one allowed", + types.GenesisState{ + Params: types.Params{ + EnableDrip: true, + AllowedAddresses: []string(nil), + }, + }, + false, + }, + { + "custom genesis - drip enabled, only one addr allowed", + types.GenesisState{ + Params: types.Params{ + EnableDrip: true, + AllowedAddresses: []string{"juno1v6vlpuqlhhpwujvaqs4pe5dmljapdev4s827ql"}, + }, + }, + false, + }, + { + "custom genesis - drip enabled, 2 addr allowed", + types.GenesisState{ + Params: types.Params{ + EnableDrip: true, + AllowedAddresses: []string{"juno1v6vlpuqlhhpwujvaqs4pe5dmljapdev4s827ql", "juno1hq2p69p4kmwndxlss7dqk0sr5pe5mmcpf7wqec"}, + }, + }, + false, + }, + { + "custom genesis - drip enabled, address invalid", + types.GenesisState{ + Params: types.Params{ + EnableDrip: true, + AllowedAddresses: []string{"juno1v6vllollollollollolloldmljapdev4s827ql"}, + }, + }, + true, + }, + } + + for _, tc := range testCases { + suite.Run(fmt.Sprintf("Case %s", tc.name), func() { + suite.SetupTest() // reset + + if tc.expPanic { + suite.Require().Panics(func() { + drip.InitGenesis(suite.ctx, suite.app.AppKeepers.DripKeeper, tc.genesis) + }) + } else { + suite.Require().NotPanics(func() { + drip.InitGenesis(suite.ctx, suite.app.AppKeepers.DripKeeper, tc.genesis) + }) + + params := suite.app.AppKeepers.DripKeeper.GetParams(suite.ctx) + suite.Require().Equal(tc.genesis.Params, params) + } + }) + } +} diff --git a/x/drip/keeper/grpc_query.go b/x/drip/keeper/grpc_query.go new file mode 100644 index 000000000..8c5d6ce17 --- /dev/null +++ b/x/drip/keeper/grpc_query.go @@ -0,0 +1,31 @@ +package keeper + +import ( + "context" + + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/CosmosContracts/juno/v17/x/drip/types" +) + +var _ types.QueryServer = Querier{} + +// Querier defines a wrapper around the x/FeeShare keeper providing gRPC method +// handlers. +type Querier struct { + Keeper +} + +func NewQuerier(k Keeper) Querier { + return Querier{Keeper: k} +} + +// Params returns the fees module params +func (q Querier) Params( + c context.Context, + _ *types.QueryParamsRequest, +) (*types.QueryParamsResponse, error) { + ctx := sdk.UnwrapSDKContext(c) + params := q.GetParams(ctx) + return &types.QueryParamsResponse{Params: params}, nil +} diff --git a/x/drip/keeper/grpc_query_test.go b/x/drip/keeper/grpc_query_test.go new file mode 100644 index 000000000..c5869e1ef --- /dev/null +++ b/x/drip/keeper/grpc_query_test.go @@ -0,0 +1,59 @@ +package keeper_test + +import ( + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/CosmosContracts/juno/v17/x/drip/types" +) + +func (s *IntegrationTestSuite) TestDripQueryParams() { + _, _, addr := testdata.KeyTestPubAddr() + _, _, addr2 := testdata.KeyTestPubAddr() + + for _, tc := range []struct { + desc string + Expected types.Params + }{ + { + desc: "On empty", + Expected: types.Params{ + EnableDrip: true, + AllowedAddresses: []string(nil), + }, + }, + { + desc: "off empty", + Expected: types.Params{ + EnableDrip: false, + AllowedAddresses: []string(nil), + }, + }, + { + desc: "On 1 address", + Expected: types.Params{ + EnableDrip: true, + AllowedAddresses: []string{addr.String()}, + }, + }, + { + desc: "On 2 Unique", + Expected: types.Params{ + EnableDrip: true, + AllowedAddresses: []string{addr.String(), addr2.String()}, + }, + }, + } { + tc := tc + s.Run(tc.desc, func() { + // Set the params to what is expected, then query and ensure the query is the same + err := s.app.AppKeepers.DripKeeper.SetParams(s.ctx, tc.Expected) + s.Require().NoError(err) + + goCtx := sdk.WrapSDKContext(s.ctx) + resp, err := s.queryClient.Params(goCtx, &types.QueryParamsRequest{}) + s.Require().NoError(err) + s.Require().Equal(tc.Expected, resp.Params) + }) + } +} diff --git a/x/drip/keeper/keeper.go b/x/drip/keeper/keeper.go new file mode 100644 index 000000000..86ce83836 --- /dev/null +++ b/x/drip/keeper/keeper.go @@ -0,0 +1,49 @@ +package keeper + +import ( + "github.com/cosmos/cosmos-sdk/codec" + storetypes "github.com/cosmos/cosmos-sdk/store/types" + sdk "github.com/cosmos/cosmos-sdk/types" + + driptypes "github.com/CosmosContracts/juno/v17/x/drip/types" +) + +// Keeper of this module maintains distributing tokens to all stakers. +type Keeper struct { + storeKey storetypes.StoreKey + cdc codec.BinaryCodec + + bankKeeper driptypes.BankKeeper + + feeCollectorName string + // the address capable of executing a MsgUpdateParams message. Typically, this + // should be the x/gov module account. + authority string +} + +// NewKeeper creates new instances of the Keeper +func NewKeeper( + storeKey storetypes.StoreKey, + cdc codec.BinaryCodec, + bk driptypes.BankKeeper, + feeCollector string, + authority string, +) Keeper { + return Keeper{ + storeKey: storeKey, + cdc: cdc, + bankKeeper: bk, + feeCollectorName: feeCollector, + authority: authority, + } +} + +// GetAuthority returns the x/mint module's authority. +func (k Keeper) GetAuthority() string { + return k.authority +} + +// SendCoinsFromAccountToFeeCollector transfers amt to the fee collector account, where it will be catch up by the distribution module at the next block +func (k Keeper) SendCoinsFromAccountToFeeCollector(ctx sdk.Context, senderAddr sdk.AccAddress, amt sdk.Coins) error { + return k.bankKeeper.SendCoinsFromAccountToModule(ctx, senderAddr, k.feeCollectorName, amt) +} diff --git a/x/drip/keeper/keeper_test.go b/x/drip/keeper/keeper_test.go new file mode 100644 index 000000000..6bbb83c6e --- /dev/null +++ b/x/drip/keeper/keeper_test.go @@ -0,0 +1,58 @@ +package keeper_test + +import ( + "testing" + "time" + + "github.com/stretchr/testify/suite" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + + "github.com/cosmos/cosmos-sdk/baseapp" + sdk "github.com/cosmos/cosmos-sdk/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + + "github.com/CosmosContracts/juno/v17/app" + "github.com/CosmosContracts/juno/v17/x/drip/keeper" + "github.com/CosmosContracts/juno/v17/x/drip/types" +) + +type IntegrationTestSuite struct { + suite.Suite + + ctx sdk.Context + app *app.App + bankKeeper types.BankKeeper + queryClient types.QueryClient + dripMsgServer types.MsgServer +} + +func (s *IntegrationTestSuite) SetupTest() { + isCheckTx := false + s.app = app.Setup(s.T()) + + s.ctx = s.app.BaseApp.NewContext(isCheckTx, tmproto.Header{ + ChainID: "testing", + Height: 9, + Time: time.Now().UTC(), + }) + + queryHelper := baseapp.NewQueryServerTestHelper(s.ctx, s.app.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, keeper.NewQuerier(s.app.AppKeepers.DripKeeper)) + + s.queryClient = types.NewQueryClient(queryHelper) + s.bankKeeper = s.app.AppKeepers.BankKeeper + s.dripMsgServer = s.app.AppKeepers.DripKeeper +} + +func (s *IntegrationTestSuite) FundAccount(ctx sdk.Context, addr sdk.AccAddress, amounts sdk.Coins) error { + if err := s.bankKeeper.MintCoins(ctx, minttypes.ModuleName, amounts); err != nil { + return err + } + + return s.bankKeeper.SendCoinsFromModuleToAccount(ctx, minttypes.ModuleName, addr, amounts) +} + +func TestKeeperTestSuite(t *testing.T) { + suite.Run(t, new(IntegrationTestSuite)) +} diff --git a/x/drip/keeper/msg_server.go b/x/drip/keeper/msg_server.go new file mode 100644 index 000000000..411e8d24a --- /dev/null +++ b/x/drip/keeper/msg_server.go @@ -0,0 +1,69 @@ +package keeper + +import ( + "context" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + + "github.com/CosmosContracts/juno/v17/x/drip/types" +) + +var _ types.MsgServer = &Keeper{} + +// DistributeTokens distribute tokens to all stakers at the next block +func (k Keeper) DistributeTokens( + goCtx context.Context, + msg *types.MsgDistributeTokens, +) (*types.MsgDistributeTokensResponse, error) { + ctx := sdk.UnwrapSDKContext(goCtx) + + if err := msg.ValidateBasic(); err != nil { + return nil, err + } + + params := k.GetParams(ctx) + if !params.EnableDrip { + return nil, types.ErrDripDisabled + } + + // Check if sender is allowed + authorized := false + for _, addr := range params.AllowedAddresses { + if msg.SenderAddress == addr { + authorized = true + break + } + } + + if !authorized { + return nil, types.ErrDripNotAllowed + } + + // Get sender + sender, err := sdk.AccAddressFromBech32(msg.SenderAddress) + if err != nil { + return nil, err + } + + if err := k.SendCoinsFromAccountToFeeCollector(ctx, sender, msg.Amount); err != nil { + return nil, err + } + + return &types.MsgDistributeTokensResponse{}, nil +} + +func (k Keeper) UpdateParams(goCtx context.Context, req *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { + if k.authority != req.Authority { + return nil, errorsmod.Wrapf(govtypes.ErrInvalidSigner, "invalid authority; expected %s, got %s", k.authority, req.Authority) + } + + ctx := sdk.UnwrapSDKContext(goCtx) + if err := k.SetParams(ctx, req.Params); err != nil { + return nil, err + } + + return &types.MsgUpdateParamsResponse{}, nil +} diff --git a/x/drip/keeper/msg_server_test.go b/x/drip/keeper/msg_server_test.go new file mode 100644 index 000000000..aff1e0cc5 --- /dev/null +++ b/x/drip/keeper/msg_server_test.go @@ -0,0 +1,153 @@ +package keeper_test + +import ( + _ "embed" + + "github.com/cosmos/cosmos-sdk/testutil/testdata" + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/CosmosContracts/juno/v17/x/drip/types" +) + +func (s *IntegrationTestSuite) TestDripDistributeTokensMsgs() { + _, _, allowedSender := testdata.KeyTestPubAddr() + _, _, notAllowedSender := testdata.KeyTestPubAddr() + _ = s.FundAccount(s.ctx, allowedSender, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000)))) + _ = s.FundAccount(s.ctx, notAllowedSender, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000)))) + + _ = s.app.AppKeepers.DripKeeper.SetParams(s.ctx, types.Params{ + EnableDrip: true, + AllowedAddresses: []string{ + allowedSender.String(), + }, + }) + + for _, tc := range []struct { + desc string + senderAddr string + coins sdk.Coins + success bool + }{ + { + desc: "Success - Allowed sender with proper funds", + senderAddr: allowedSender.String(), + coins: sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1))), + success: true, + }, + { + desc: "Fail - Allowed sender no proper funds", + senderAddr: allowedSender.String(), + coins: sdk.NewCoins(sdk.NewCoin("notarealtoken", sdk.NewInt(1))), + success: false, + }, + { + desc: "Fail - Allowed sender no tokens", + senderAddr: allowedSender.String(), + coins: nil, + success: false, + }, + { + desc: "Fail - Allowed sender empty tokens", + senderAddr: allowedSender.String(), + coins: sdk.NewCoins(), + success: false, + }, + { + desc: "Fail - No sender withproper funds", + senderAddr: "", + coins: sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1))), + success: false, + }, + { + desc: "Fail - Non Allowed sender proper funds", + senderAddr: notAllowedSender.String(), + coins: sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1))), + success: false, + }, + { + desc: "Fail - Non Allowed sender improper funds", + senderAddr: notAllowedSender.String(), + coins: sdk.NewCoins(sdk.NewCoin("notarealtoken", sdk.NewInt(1))), + success: false, + }, + } { + tc := tc + s.Run(tc.desc, func() { + msg := types.MsgDistributeTokens{ + SenderAddress: tc.senderAddr, + Amount: tc.coins, + } + _, err := s.app.AppKeepers.DripKeeper.DistributeTokens(s.ctx, &msg) + + if !tc.success { + s.Require().Error(err) + } else { + s.Require().NoError(err) + } + }) + } +} + +func (s *IntegrationTestSuite) TestUpdateDripParams() { + _, _, addr := testdata.KeyTestPubAddr() + _, _, addr2 := testdata.KeyTestPubAddr() + + for _, tc := range []struct { + desc string + isEnabled bool + AllowedAddresses []string + success bool + }{ + { + desc: "Success - Valid on", + isEnabled: true, + AllowedAddresses: []string{}, + success: true, + }, + { + desc: "Success - Valid off", + isEnabled: false, + AllowedAddresses: []string{}, + success: true, + }, + { + desc: "Success - On and 1 allowed address", + isEnabled: true, + AllowedAddresses: []string{addr.String()}, + success: true, + }, + { + desc: "Fail - On and 2 duplicate addresses", + isEnabled: true, + AllowedAddresses: []string{addr.String(), addr.String()}, + success: false, + }, + { + desc: "Success - On and 2 unique", + isEnabled: true, + AllowedAddresses: []string{addr.String(), addr2.String()}, + success: true, + }, + { + desc: "Success - On and 2 duplicate 1 unique", + isEnabled: true, + AllowedAddresses: []string{addr.String(), addr2.String(), addr.String()}, + success: false, + }, + } { + tc := tc + s.Run(tc.desc, func() { + params := types.Params{ + EnableDrip: tc.isEnabled, + AllowedAddresses: tc.AllowedAddresses, + } + err := s.app.AppKeepers.DripKeeper.SetParams(s.ctx, params) + + if !tc.success { + s.Require().Error(err) + } else { + s.Require().NoError(err) + } + }) + } +} diff --git a/x/drip/keeper/params.go b/x/drip/keeper/params.go new file mode 100644 index 000000000..e23c7e9c5 --- /dev/null +++ b/x/drip/keeper/params.go @@ -0,0 +1,31 @@ +package keeper + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + + "github.com/CosmosContracts/juno/v17/x/drip/types" +) + +// GetParams returns the current x/drip module parameters. +func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { + store := ctx.KVStore(k.storeKey) + bz := store.Get(types.ParamsKey) + if bz == nil { + return params + } + k.cdc.MustUnmarshal(bz, ¶ms) + return params +} + +// SetParams sets the x/drip module parameters. +func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error { + if err := params.Validate(); err != nil { + return err + } + + store := ctx.KVStore(k.storeKey) + bz := k.cdc.MustMarshal(¶ms) + store.Set(types.ParamsKey, bz) + + return nil +} diff --git a/x/drip/module.go b/x/drip/module.go new file mode 100644 index 000000000..403857ec2 --- /dev/null +++ b/x/drip/module.go @@ -0,0 +1,189 @@ +package drip + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/gorilla/mux" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/spf13/cobra" + + abci "github.com/cometbft/cometbft/abci/types" + + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/module" + simtypes "github.com/cosmos/cosmos-sdk/types/simulation" + authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" + + "github.com/CosmosContracts/juno/v17/x/drip/client/cli" + "github.com/CosmosContracts/juno/v17/x/drip/keeper" + "github.com/CosmosContracts/juno/v17/x/drip/types" +) + +// type check to ensure the interface is properly implemented +var ( + _ module.AppModule = AppModule{} + _ module.AppModuleBasic = AppModuleBasic{} + _ module.AppModuleSimulation = AppModule{} +) + +// AppModuleBasic type for the fees module +type AppModuleBasic struct{} + +// Name returns the fees module's name. +func (AppModuleBasic) Name() string { + return types.ModuleName +} + +// RegisterLegacyAminoCodec performs a no-op as the fees do not support Amino +// encoding. +func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + types.RegisterLegacyAminoCodec(cdc) +} + +// ConsensusVersion returns the consensus state-breaking version for the module. +func (AppModuleBasic) ConsensusVersion() uint64 { + return 1 +} + +// RegisterInterfaces registers interfaces and implementations of the fees +// module. +func (AppModuleBasic) RegisterInterfaces(interfaceRegistry codectypes.InterfaceRegistry) { + types.RegisterInterfaces(interfaceRegistry) +} + +// DefaultGenesis returns default genesis state as raw bytes for the fees +// module. +func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage { + return cdc.MustMarshalJSON(types.DefaultGenesisState()) +} + +// ValidateGenesis performs genesis state validation for the fees module. +func (b AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error { + var genesisState types.GenesisState + if err := cdc.UnmarshalJSON(bz, &genesisState); err != nil { + return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err) + } + + return genesisState.Validate() +} + +// RegisterRESTRoutes performs a no-op as the fees module doesn't expose REST +// endpoints +func (AppModuleBasic) RegisterRESTRoutes(_ client.Context, _ *mux.Router) {} + +// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the fees +// module. +func (b AppModuleBasic) RegisterGRPCGatewayRoutes(c client.Context, serveMux *runtime.ServeMux) { + if err := types.RegisterQueryHandlerClient(context.Background(), serveMux, types.NewQueryClient(c)); err != nil { + panic(err) + } +} + +// GetTxCmd returns the root tx command for the fees module. +func (AppModuleBasic) GetTxCmd() *cobra.Command { + return cli.NewTxCmd() +} + +// GetQueryCmd returns the fees module's root query command. +func (AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetQueryCmd() +} + +// ___________________________________________________________________________ + +// AppModule implements the AppModule interface for the fees module. +type AppModule struct { + AppModuleBasic + keeper keeper.Keeper + ak authkeeper.AccountKeeper +} + +// NewAppModule creates a new AppModule Object +func NewAppModule( + k keeper.Keeper, + ak authkeeper.AccountKeeper, +) AppModule { + return AppModule{ + AppModuleBasic: AppModuleBasic{}, + keeper: k, + ak: ak, + } +} + +// Name returns the fees module's name. +func (AppModule) Name() string { + return types.ModuleName +} + +// RegisterInvariants registers the fees module's invariants. +func (am AppModule) RegisterInvariants(_ sdk.InvariantRegistry) {} + +// NewHandler returns nil - fees module doesn't expose tx gRPC endpoints +func (am AppModule) NewHandler() sdk.Handler { + return nil +} + +// QuerierRoute returns the claim module's query routing key. +func (am AppModule) QuerierRoute() string { + return types.RouterKey +} + +// RegisterServices registers a GRPC query service to respond to the +// module-specific GRPC queries. +func (am AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), am.keeper) + types.RegisterQueryServer(cfg.QueryServer(), keeper.NewQuerier(am.keeper)) +} + +// BeginBlock executes all ABCI BeginBlock logic respective to the fees module. +func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) { +} + +// EndBlock executes all ABCI EndBlock logic respective to the fee-share module. It +// returns no validator updates. +func (am AppModule) EndBlock(_ sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + return []abci.ValidatorUpdate{} +} + +// InitGenesis performs the fees module's genesis initialization. It returns +// no validator updates. +func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate { + var genesisState types.GenesisState + + cdc.MustUnmarshalJSON(data, &genesisState) + InitGenesis(ctx, am.keeper, genesisState) + return []abci.ValidatorUpdate{} +} + +// ExportGenesis returns the fees module's exported genesis state as raw JSON bytes. +func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage { + gs := ExportGenesis(ctx, am.keeper) + return cdc.MustMarshalJSON(gs) +} + +// ___________________________________________________________________________ + +// AppModuleSimulation functions + +// GenerateGenesisState creates a randomized GenState of the fees module. +func (am AppModule) GenerateGenesisState(_ *module.SimulationState) { +} + +// ProposalContents returns content functions for governance proposals. +func (am AppModule) ProposalContents(_ module.SimulationState) []simtypes.WeightedProposalMsg { + return []simtypes.WeightedProposalMsg{} +} + +// RegisterStoreDecoder registers a decoder for fees module's types. +func (am AppModule) RegisterStoreDecoder(_ sdk.StoreDecoderRegistry) { +} + +// WeightedOperations returns fees module weighted operations +func (am AppModule) WeightedOperations(_ module.SimulationState) []simtypes.WeightedOperation { + return []simtypes.WeightedOperation{} +} diff --git a/x/drip/spec/01_authorization.md b/x/drip/spec/01_authorization.md new file mode 100644 index 000000000..9ca115070 --- /dev/null +++ b/x/drip/spec/01_authorization.md @@ -0,0 +1,43 @@ + + +# Authorization + +For security reasons, only specific addresses can distribute tokens to $JUNO stakers. We accept any kind of address: multisig, smart contracts, regular and [https://daodao.zone](DAODAO) DAOs. + +Governance can decide wether to approve or deny a new address to be added to the authorized list. + +## Query the allowed addresses + +You can query the list of allowed addresses directly from x/drip params + +``` +% junod q drip params --output json +{"enable_drip":true,"allowed_addresses":[]} +``` + +## Governance proposal + +To update the authorized address is possible to create a onchain new proposal. You can use the following example `proposal.json` file + +```json +{ + "messages": [ + { + "@type": "/juno.drip.v1.MsgUpdateParams", + "authority": "juno10d07y265gmmuvt4z0w9aw880jnsr700jvss730", + "params": { + "enable_drip": false, + "allowed_addresses": ["juno1j0a9ymgngasfn3l5me8qpd53l5zlm9wurfdk7r65s5mg6tkxal3qpgf5se"] + } + } + ], + "metadata": "{\"title\": \"Allow an amazing contract to distribute tokens using drip\", \"authors\": [\"dimi\"], \"summary\": \"If this proposal passes juno1j0a9ymgngasfn3l5me8qpd53l5zlm9wurfdk7r65s5mg6tkxal3qpgf5se will be added to the authorized addresses of the drip module\", \"details\": \"If this proposal passes juno1j0a9ymgngasfn3l5me8qpd53l5zlm9wurfdk7r65s5mg6tkxal3qpgf5se will be added to the authorized addresses of the drip module\", \"proposal_forum_url\": \"https://commonwealth.im/juno/discussion/9697-juno-protocol-level-defi-incentives\", \"vote_option_context\": \"yes\"}", + "deposit": "1000ujuno", + "title": "Allow an amazing contract to distribute tokens using drip", + "summary": "If this proposal passes juno1j0a9ymgngasfn3l5me8qpd53l5zlm9wurfdk7r65s5mg6tkxal3qpgf5se will be added to the authorized addresses of the drip module" +} +``` + +It can be submitted with the standard `junod tx gov submit-proposal proposal.json --from yourkey` command. diff --git a/x/drip/spec/02_distribute_tokens.md b/x/drip/spec/02_distribute_tokens.md new file mode 100644 index 000000000..fba6dcea7 --- /dev/null +++ b/x/drip/spec/02_distribute_tokens.md @@ -0,0 +1,17 @@ + + +# Distributing Tokens + +Once an address is authorized, at any time it can use the `MsgDistributToken` message to distribute all the attached funds at the next block. + +From command line is as easy as running the follwing instruction + +``` +junod tx drip distribute-tokens 100000tf/yourcontract/yourtoken +``` + +Only native tokens and the ones made with tokenfactory are allowed. + +If you have a CW-20 token, you can wrap it to native using [https://github.com/CosmosContracts/tokenfactory-contracts/tree/main/contracts/migrate](this contract). \ No newline at end of file diff --git a/x/drip/spec/03_example.md b/x/drip/spec/03_example.md new file mode 100644 index 000000000..340d70ed0 --- /dev/null +++ b/x/drip/spec/03_example.md @@ -0,0 +1,33 @@ + + +# Example contract + +The following code is an example function to encode a `MsgDistributeTokens` message in CosmWasm + +```rust +fn encode_msg_create_vesting_acct(vest_to: &Addr, env: Env) -> Result { + + // Test with 1 ucosm + let one_cosm: Coin = coin(1_000_000, "ucosm"); + + // MsgDistributeTokens + + let proto = Anybuf::new() + .append_string(1, &env.contract.address) + .append_message(2, &Anybuf::new() + .append_string(1, &one_cosm.denom) + .append_string(2, &one_cosm.amount.to_string()) + ) + .into_vec(); + + let msg = CosmosMsg::Stargate { + type_url: "/juno.drip.v1.MsgDistributeTokens".to_string(), + value: proto.into() + }; + +} +``` + +More information about [https://lib.rs/crates/anybuf](Anybuf) \ No newline at end of file diff --git a/x/drip/spec/README.md b/x/drip/spec/README.md new file mode 100644 index 000000000..79b596eec --- /dev/null +++ b/x/drip/spec/README.md @@ -0,0 +1,24 @@ + + +# `drip` + +## Abstract + +This document specifies the internal `x/drip` module of Juno Network. + +The `x/drip` allows specific addresses (usually smart contracts) to send tokens to the fee_pool module in order to perform a live airdrop to Juno Stakers. + +It consists only on one new message `MsgDistributeTokens`, when called from an authorized address all the funds sent with it are distributed at the next block. + +On an ideal scenario, projects are allocating tokens to a smart contract that then split the amount over a custom schedule, using for example [https://www.cron.cat/](CronCat). + +## Contents + +1. **[Authorization](01_authorization.md)** +2. **[Distribute Tokens](02_distribute_tokens.md)** +3. **[Example Contract](03_example.md)** \ No newline at end of file diff --git a/x/drip/types/codec.go b/x/drip/types/codec.go new file mode 100644 index 000000000..211936562 --- /dev/null +++ b/x/drip/types/codec.go @@ -0,0 +1,57 @@ +package types + +import ( + "github.com/cosmos/cosmos-sdk/codec" + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/types/msgservice" + authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" +) + +var ( + amino = codec.NewLegacyAmino() + + // ModuleCdc references the global erc20 module codec. Note, the codec should + // ONLY be used in certain instances of tests and for JSON encoding. + // + // The actual codec used for serialization should be provided to modules/erc20 and + // defined at the application level. + ModuleCdc = codec.NewProtoCodec(codectypes.NewInterfaceRegistry()) + + // AminoCdc is a amino codec created to support amino JSON compatible msgs. + AminoCdc = codec.NewAminoCodec(amino) +) + +const ( + // Amino names + distributeTokensName = "juno/MsgDistributeTokens" //nolint:gosec // these are not hard coded credentials +) + +// NOTE: This is required for the GetSignBytes function +func init() { + RegisterLegacyAminoCodec(amino) + sdk.RegisterLegacyAminoCodec(amino) + amino.Seal() + + // Register all Amino interfaces and concrete types on the authz Amino codec + // so that this can later be used to properly serialize MsgGrant and MsgExec + // instances. + RegisterLegacyAminoCodec(authzcodec.Amino) +} + +// RegisterInterfaces register implementations +func RegisterInterfaces(registry codectypes.InterfaceRegistry) { + registry.RegisterImplementations( + (*sdk.Msg)(nil), + &MsgDistributeTokens{}, + ) + + msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) +} + +// RegisterLegacyAminoCodec registers the necessary x/FeeShare interfaces and +// concrete types on the provided LegacyAmino codec. These types are used for +// Amino JSON serialization and EIP-712 compatibility. +func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { + cdc.RegisterConcrete(&MsgDistributeTokens{}, distributeTokensName, nil) +} diff --git a/x/drip/types/codec_test.go b/x/drip/types/codec_test.go new file mode 100644 index 000000000..5f436da19 --- /dev/null +++ b/x/drip/types/codec_test.go @@ -0,0 +1,31 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + codectypes "github.com/cosmos/cosmos-sdk/codec/types" + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type CodecTestSuite struct { + suite.Suite +} + +func TestCodecSuite(t *testing.T) { + suite.Run(t, new(CodecTestSuite)) +} + +func (suite *CodecTestSuite) TestRegisterInterfaces() { + registry := codectypes.NewInterfaceRegistry() + registry.RegisterInterface(sdk.MsgInterfaceProtoName, (*sdk.Msg)(nil)) + RegisterInterfaces(registry) + + impls := registry.ListImplementations(sdk.MsgInterfaceProtoName) + suite.Require().Equal(2, len(impls)) + suite.Require().ElementsMatch([]string{ + "/juno.drip.v1.MsgDistributeTokens", + "/juno.drip.v1.MsgUpdateParams", + }, impls) +} diff --git a/x/drip/types/errors.go b/x/drip/types/errors.go new file mode 100644 index 000000000..15348c3cd --- /dev/null +++ b/x/drip/types/errors.go @@ -0,0 +1,13 @@ +package types + +import ( + errorsmod "cosmossdk.io/errors" +) + +var ( + ErrDripDisabled = errorsmod.Register(ModuleName, 1, "drip module is disabled by governance") + ErrDripNotAllowed = errorsmod.Register(ModuleName, 2, "this address is not allowed to use the module, you can request access from governance") + ErrEmpty = errorsmod.Register(ModuleName, 3, "empty") + ErrDuplicate = errorsmod.Register(ModuleName, 4, "duplicate") + ErrBlank = errorsmod.Register(ModuleName, 5, "address cannot be blank") +) diff --git a/x/drip/types/expected_keepers.go b/x/drip/types/expected_keepers.go new file mode 100644 index 000000000..8669c8749 --- /dev/null +++ b/x/drip/types/expected_keepers.go @@ -0,0 +1,13 @@ +package types + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// BankKeeper defines the expected interface needed to retrieve account balances. +type BankKeeper interface { + SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + + MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error +} diff --git a/x/drip/types/genesis.go b/x/drip/types/genesis.go new file mode 100644 index 000000000..0b1225690 --- /dev/null +++ b/x/drip/types/genesis.go @@ -0,0 +1,22 @@ +package types + +// NewGenesisState creates a new genesis state. +func NewGenesisState(params Params) GenesisState { + return GenesisState{ + Params: params, + } +} + +// DefaultGenesisState sets default evm genesis state with empty accounts and +// default params and chain config values. +func DefaultGenesisState() *GenesisState { + return &GenesisState{ + Params: DefaultParams(), + } +} + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + return gs.Params.Validate() +} diff --git a/x/drip/types/genesis.pb.go b/x/drip/types/genesis.pb.go new file mode 100644 index 000000000..8ee0df0c5 --- /dev/null +++ b/x/drip/types/genesis.pb.go @@ -0,0 +1,544 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: juno/drip/v1/genesis.proto + +package types + +import ( + fmt "fmt" + _ "github.com/cosmos/gogoproto/gogoproto" + proto "github.com/cosmos/gogoproto/proto" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// GenesisState defines the module's genesis state. +type GenesisState struct { + // params are the drip module parameters + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *GenesisState) Reset() { *m = GenesisState{} } +func (m *GenesisState) String() string { return proto.CompactTextString(m) } +func (*GenesisState) ProtoMessage() {} +func (*GenesisState) Descriptor() ([]byte, []int) { + return fileDescriptor_a281ae9bcc19c501, []int{0} +} +func (m *GenesisState) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *GenesisState) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_GenesisState.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *GenesisState) XXX_Merge(src proto.Message) { + xxx_messageInfo_GenesisState.Merge(m, src) +} +func (m *GenesisState) XXX_Size() int { + return m.Size() +} +func (m *GenesisState) XXX_DiscardUnknown() { + xxx_messageInfo_GenesisState.DiscardUnknown(m) +} + +var xxx_messageInfo_GenesisState proto.InternalMessageInfo + +func (m *GenesisState) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +// Params defines the drip module params +type Params struct { + // enable_drip defines a parameter to enable the drip module + EnableDrip bool `protobuf:"varint,1,opt,name=enable_drip,json=enableDrip,proto3" json:"enable_drip,omitempty"` + // allowed_addresses defines the list of addresses authorized to use the module + AllowedAddresses []string `protobuf:"bytes,3,rep,name=allowed_addresses,json=allowedAddresses,proto3" json:"allowed_addresses,omitempty" yaml:"addresses"` +} + +func (m *Params) Reset() { *m = Params{} } +func (m *Params) String() string { return proto.CompactTextString(m) } +func (*Params) ProtoMessage() {} +func (*Params) Descriptor() ([]byte, []int) { + return fileDescriptor_a281ae9bcc19c501, []int{1} +} +func (m *Params) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *Params) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_Params.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *Params) XXX_Merge(src proto.Message) { + xxx_messageInfo_Params.Merge(m, src) +} +func (m *Params) XXX_Size() int { + return m.Size() +} +func (m *Params) XXX_DiscardUnknown() { + xxx_messageInfo_Params.DiscardUnknown(m) +} + +var xxx_messageInfo_Params proto.InternalMessageInfo + +func (m *Params) GetEnableDrip() bool { + if m != nil { + return m.EnableDrip + } + return false +} + +func (m *Params) GetAllowedAddresses() []string { + if m != nil { + return m.AllowedAddresses + } + return nil +} + +func init() { + proto.RegisterType((*GenesisState)(nil), "juno.drip.v1.GenesisState") + proto.RegisterType((*Params)(nil), "juno.drip.v1.Params") +} + +func init() { proto.RegisterFile("juno/drip/v1/genesis.proto", fileDescriptor_a281ae9bcc19c501) } + +var fileDescriptor_a281ae9bcc19c501 = []byte{ + // 272 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xca, 0x2a, 0xcd, 0xcb, + 0xd7, 0x4f, 0x29, 0xca, 0x2c, 0xd0, 0x2f, 0x33, 0xd4, 0x4f, 0x4f, 0xcd, 0x4b, 0x2d, 0xce, 0x2c, + 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x01, 0xc9, 0xe9, 0x81, 0xe4, 0xf4, 0xca, 0x0c, + 0xa5, 0x44, 0xd2, 0xf3, 0xd3, 0xf3, 0xc1, 0x12, 0xfa, 0x20, 0x16, 0x44, 0x8d, 0x92, 0x13, 0x17, + 0x8f, 0x3b, 0x44, 0x53, 0x70, 0x49, 0x62, 0x49, 0xaa, 0x90, 0x11, 0x17, 0x5b, 0x41, 0x62, 0x51, + 0x62, 0x6e, 0xb1, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0xb7, 0x91, 0x88, 0x1e, 0xb2, 0x21, 0x7a, 0x01, + 0x60, 0x39, 0x27, 0x96, 0x13, 0xf7, 0xe4, 0x19, 0x82, 0xa0, 0x2a, 0x95, 0x72, 0xb8, 0xd8, 0x20, + 0xe2, 0x42, 0xf2, 0x5c, 0xdc, 0xa9, 0x79, 0x89, 0x49, 0x39, 0xa9, 0xf1, 0x20, 0x0d, 0x60, 0x23, + 0x38, 0x82, 0xb8, 0x20, 0x42, 0x2e, 0x45, 0x99, 0x05, 0x42, 0x8e, 0x5c, 0x82, 0x89, 0x39, 0x39, + 0xf9, 0xe5, 0xa9, 0x29, 0xf1, 0x89, 0x29, 0x29, 0x45, 0xa9, 0xc5, 0xc5, 0xa9, 0xc5, 0x12, 0xcc, + 0x0a, 0xcc, 0x1a, 0x9c, 0x4e, 0x22, 0x9f, 0xee, 0xc9, 0x0b, 0x54, 0x26, 0xe6, 0xe6, 0x58, 0x29, + 0xc1, 0xa5, 0x94, 0x82, 0x04, 0xa0, 0xca, 0x1d, 0x61, 0x42, 0x4e, 0x6e, 0x27, 0x1e, 0xc9, 0x31, + 0x5e, 0x78, 0x24, 0xc7, 0xf8, 0xe0, 0x91, 0x1c, 0xe3, 0x84, 0xc7, 0x72, 0x0c, 0x17, 0x1e, 0xcb, + 0x31, 0xdc, 0x78, 0x2c, 0xc7, 0x10, 0xa5, 0x93, 0x9e, 0x59, 0x92, 0x51, 0x9a, 0xa4, 0x97, 0x9c, + 0x9f, 0xab, 0xef, 0x9c, 0x5f, 0x9c, 0x9b, 0x5f, 0xec, 0x9c, 0x9f, 0x57, 0x52, 0x94, 0x98, 0x5c, + 0x52, 0xac, 0x0f, 0x0e, 0xa6, 0x0a, 0x48, 0x40, 0x95, 0x54, 0x16, 0xa4, 0x16, 0x27, 0xb1, 0x81, + 0x03, 0xc0, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xb3, 0x9c, 0xad, 0xd3, 0x42, 0x01, 0x00, 0x00, +} + +func (m *GenesisState) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *GenesisState) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *GenesisState) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintGenesis(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func (m *Params) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *Params) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *Params) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.AllowedAddresses) > 0 { + for iNdEx := len(m.AllowedAddresses) - 1; iNdEx >= 0; iNdEx-- { + i -= len(m.AllowedAddresses[iNdEx]) + copy(dAtA[i:], m.AllowedAddresses[iNdEx]) + i = encodeVarintGenesis(dAtA, i, uint64(len(m.AllowedAddresses[iNdEx]))) + i-- + dAtA[i] = 0x1a + } + } + if m.EnableDrip { + i-- + if m.EnableDrip { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x8 + } + return len(dAtA) - i, nil +} + +func encodeVarintGenesis(dAtA []byte, offset int, v uint64) int { + offset -= sovGenesis(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *GenesisState) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovGenesis(uint64(l)) + return n +} + +func (m *Params) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + if m.EnableDrip { + n += 2 + } + if len(m.AllowedAddresses) > 0 { + for _, s := range m.AllowedAddresses { + l = len(s) + n += 1 + l + sovGenesis(uint64(l)) + } + } + return n +} + +func sovGenesis(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozGenesis(x uint64) (n int) { + return sovGenesis(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *GenesisState) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: GenesisState: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: GenesisState: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *Params) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: Params: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: Params: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field EnableDrip", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + m.EnableDrip = bool(v != 0) + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field AllowedAddresses", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenesis + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthGenesis + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthGenesis + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.AllowedAddresses = append(m.AllowedAddresses, string(dAtA[iNdEx:postIndex])) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipGenesis(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthGenesis + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipGenesis(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowGenesis + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthGenesis + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupGenesis + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthGenesis + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthGenesis = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowGenesis = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupGenesis = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/drip/types/keys.go b/x/drip/types/keys.go new file mode 100644 index 000000000..75a829fd6 --- /dev/null +++ b/x/drip/types/keys.go @@ -0,0 +1,17 @@ +package types + +const ( + // module name + ModuleName = "drip" + + // StoreKey to be used when creating the KVStore + StoreKey = ModuleName + + // RouterKey to be used for message routing + RouterKey = ModuleName +) + +// KVStore key prefixes +var ( + ParamsKey = []byte{0x00} // Prefix for params key +) diff --git a/x/drip/types/msg.go b/x/drip/types/msg.go new file mode 100644 index 000000000..e0b825978 --- /dev/null +++ b/x/drip/types/msg.go @@ -0,0 +1,89 @@ +package types + +import ( + fmt "fmt" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + _ sdk.Msg = &MsgDistributeTokens{} + _ sdk.Msg = &MsgUpdateParams{} +) + +const ( + TypeMsgDistributeTokens = "distribute_tokens" +) + +// NewMsgDistributeTokens creates new instance of MsgDistributeTokens +func NewMsgDistributeTokens( + amount sdk.Coins, + sender sdk.Address, +) *MsgDistributeTokens { + return &MsgDistributeTokens{ + SenderAddress: sender.String(), + Amount: amount, + } +} + +// Route returns the name of the module +func (msg MsgDistributeTokens) Route() string { return RouterKey } + +// Type returns the the action +func (msg MsgDistributeTokens) Type() string { return TypeMsgDistributeTokens } + +// ValidateBasic runs stateless checks on the message +func (msg MsgDistributeTokens) ValidateBasic() error { + if msg.SenderAddress == "" { + return fmt.Errorf("sender address cannot be empty") + } + + if _, err := sdk.AccAddressFromBech32(msg.SenderAddress); err != nil { + return errorsmod.Wrapf(err, "invalid sender address: %s", err.Error()) + } + + if msg.Amount == nil || msg.Amount.Empty() { + return fmt.Errorf("invalid coins: %s", msg.Amount.String()) + } + + if !msg.Amount.IsValid() { + return fmt.Errorf("invalid coins: %s", msg.Amount.String()) + } + + return nil +} + +// GetSignBytes encodes the message for signing +func (msg *MsgDistributeTokens) GetSignBytes() []byte { + return sdk.MustSortJSON(AminoCdc.MustMarshalJSON(msg)) +} + +// GetSigners defines whose signature is required +func (msg MsgDistributeTokens) GetSigners() []sdk.AccAddress { + from, _ := sdk.AccAddressFromBech32(msg.SenderAddress) + return []sdk.AccAddress{from} +} + +// GetSignBytes implements the LegacyMsg interface. +func (msg MsgUpdateParams) GetSignBytes() []byte { + return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&msg)) +} + +// GetSigners returns the expected signers for a MsgUpdateParams message. +func (msg *MsgUpdateParams) GetSigners() []sdk.AccAddress { + addr, _ := sdk.AccAddressFromBech32(msg.Authority) + return []sdk.AccAddress{addr} +} + +// ValidateBasic does a sanity check on the provided data. +func (msg *MsgUpdateParams) ValidateBasic() error { + if _, err := sdk.AccAddressFromBech32(msg.Authority); err != nil { + return errorsmod.Wrap(err, "invalid authority address") + } + + err := msg.Params.Validate() + + return err +} diff --git a/x/drip/types/msg_test.go b/x/drip/types/msg_test.go new file mode 100644 index 000000000..8b60a620c --- /dev/null +++ b/x/drip/types/msg_test.go @@ -0,0 +1,81 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +type MsgsTestSuite struct { + suite.Suite + amount sdk.Coins + sender sdk.AccAddress +} + +func TestMsgsTestSuite(t *testing.T) { + suite.Run(t, new(MsgsTestSuite)) +} + +func (suite *MsgsTestSuite) SetupTest() { + sender := "cosmos1" + suite.sender = sdk.AccAddress([]byte(sender)) + suite.amount = sdk.NewCoins(sdk.NewCoin("ujuice", sdk.NewInt(1000000))) +} + +func (suite *MsgsTestSuite) TestMsgDistributeTokensGetters() { + msgInvalid := MsgDistributeTokens{} + msg := NewMsgDistributeTokens( + suite.amount, + suite.sender, + ) + suite.Require().Equal(RouterKey, msg.Route()) + suite.Require().Equal(TypeMsgDistributeTokens, msg.Type()) + suite.Require().NotNil(msgInvalid.GetSignBytes()) + suite.Require().NotNil(msg.GetSigners()) +} + +func (suite *MsgsTestSuite) TestMsgDistributeTokensNew() { + testCases := []struct { + msg string + amount sdk.Coins + sender string + expectPass bool + }{ + { + "pass", + suite.amount, + suite.sender.String(), + true, + }, + { + "sender address cannot be empty", + suite.amount, + "", + false, + }, + { + "invalid coins", + nil, + suite.sender.String(), + false, + }, + } + + for i, tc := range testCases { + tx := MsgDistributeTokens{ + Amount: tc.amount, + SenderAddress: tc.sender, + } + + err := tx.ValidateBasic() + + if tc.expectPass { + suite.Require().NoError(err, "valid test %d failed: %s", i, tc.msg) + } else { + suite.Require().Error(err, "invalid test %d passed: %s", i, tc.msg) + suite.Require().Contains(err.Error(), tc.msg) + } + } +} diff --git a/x/drip/types/params.go b/x/drip/types/params.go new file mode 100644 index 000000000..b72760068 --- /dev/null +++ b/x/drip/types/params.go @@ -0,0 +1,80 @@ +package types + +import ( + "fmt" + + errorsmod "cosmossdk.io/errors" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +var ( + DefaultEnableDrip = true + DefaultAllowedAddresses = []string(nil) // no one allowed +) + +// NewParams creates a new Params object +func NewParams( + enableDrip bool, + allowedAddresses []string, +) Params { + return Params{ + EnableDrip: enableDrip, + AllowedAddresses: allowedAddresses, + } +} + +// DefaultParams returns default x/drip module parameters. +func DefaultParams() Params { + return Params{ + EnableDrip: DefaultEnableDrip, + AllowedAddresses: DefaultAllowedAddresses, + } +} + +func validateBool(i interface{}) error { + _, ok := i.(bool) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + return nil +} + +func validateArray(i interface{}) error { + _, ok := i.([]string) + if !ok { + return fmt.Errorf("invalid parameter type: %T", i) + } + + return nil +} + +func (p Params) Validate() error { + if err := validateBool(p.EnableDrip); err != nil { + return err + } + + if err := validateArray(p.AllowedAddresses); err != nil { + return err + } + + return assertValidAddresses(p.AllowedAddresses) +} + +func assertValidAddresses(addrs []string) error { + idx := make(map[string]struct{}, len(addrs)) + for _, a := range addrs { + if a == "" { + return ErrBlank.Wrapf("address: %s", a) + } + if _, err := sdk.AccAddressFromBech32(a); err != nil { + return errorsmod.Wrapf(err, "address: %s", a) + } + if _, exists := idx[a]; exists { + return ErrDuplicate.Wrapf("address: %s", a) + } + idx[a] = struct{}{} + } + return nil +} diff --git a/x/drip/types/params_test.go b/x/drip/types/params_test.go new file mode 100644 index 000000000..8666e15f4 --- /dev/null +++ b/x/drip/types/params_test.go @@ -0,0 +1,50 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestParamsValidate(t *testing.T) { + testCases := []struct { + name string + params Params + expError bool + }{ + {"default", DefaultParams(), false}, + { + "valid: disabled, no one allowed", + NewParams(false, []string(nil)), + false, + }, + { + "invalid: enabled, address malformed", + NewParams(false, []string{"invalid address"}), + true, + }, + } + + for _, tc := range testCases { + err := tc.params.Validate() + + if tc.expError { + require.Error(t, err, tc.name) + } else { + require.NoError(t, err, tc.name) + } + } +} + +func TestParamsValidateBool(t *testing.T) { + err := validateBool(DefaultEnableDrip) + require.NoError(t, err) + err = validateBool(true) + require.NoError(t, err) + err = validateBool(false) + require.NoError(t, err) + err = validateBool("") + require.Error(t, err) + err = validateBool(int64(123)) + require.Error(t, err) +} diff --git a/x/drip/types/query.pb.go b/x/drip/types/query.pb.go new file mode 100644 index 000000000..7acbb4dff --- /dev/null +++ b/x/drip/types/query.pb.go @@ -0,0 +1,537 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: juno/drip/v1/query.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-sdk/types/query" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// QueryParamsRequest is the request type for the Query/Params RPC method. +type QueryParamsRequest struct { +} + +func (m *QueryParamsRequest) Reset() { *m = QueryParamsRequest{} } +func (m *QueryParamsRequest) String() string { return proto.CompactTextString(m) } +func (*QueryParamsRequest) ProtoMessage() {} +func (*QueryParamsRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_eec39884c203d30d, []int{0} +} +func (m *QueryParamsRequest) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsRequest.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsRequest.Merge(m, src) +} +func (m *QueryParamsRequest) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsRequest) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsRequest proto.InternalMessageInfo + +// QueryParamsResponse is the response type for the Query/Params RPC method. +type QueryParamsResponse struct { + // params is the returned parameter from the module + Params Params `protobuf:"bytes,1,opt,name=params,proto3" json:"params"` +} + +func (m *QueryParamsResponse) Reset() { *m = QueryParamsResponse{} } +func (m *QueryParamsResponse) String() string { return proto.CompactTextString(m) } +func (*QueryParamsResponse) ProtoMessage() {} +func (*QueryParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_eec39884c203d30d, []int{1} +} +func (m *QueryParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *QueryParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_QueryParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *QueryParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_QueryParamsResponse.Merge(m, src) +} +func (m *QueryParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *QueryParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_QueryParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_QueryParamsResponse proto.InternalMessageInfo + +func (m *QueryParamsResponse) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +func init() { + proto.RegisterType((*QueryParamsRequest)(nil), "juno.drip.v1.QueryParamsRequest") + proto.RegisterType((*QueryParamsResponse)(nil), "juno.drip.v1.QueryParamsResponse") +} + +func init() { proto.RegisterFile("juno/drip/v1/query.proto", fileDescriptor_eec39884c203d30d) } + +var fileDescriptor_eec39884c203d30d = []byte{ + // 301 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x50, 0xcf, 0x4a, 0xc3, 0x30, + 0x18, 0x6f, 0x45, 0x77, 0x88, 0x9e, 0x62, 0x91, 0x51, 0x46, 0x9c, 0x3b, 0x89, 0x48, 0x42, 0xe7, + 0x1b, 0x6c, 0x20, 0x78, 0xd3, 0x1d, 0xbd, 0xa5, 0x33, 0xc4, 0xa8, 0xcb, 0x97, 0x35, 0xe9, 0x70, + 0x57, 0x9f, 0x40, 0xf0, 0xa5, 0x76, 0x1c, 0x78, 0xf1, 0x24, 0xd2, 0xfa, 0x20, 0xd2, 0xa4, 0x07, + 0x8b, 0xe0, 0xed, 0xe3, 0xf7, 0x37, 0xf9, 0xa1, 0xfe, 0x43, 0xa9, 0x81, 0xdd, 0x15, 0xca, 0xb0, + 0x55, 0xc6, 0x96, 0xa5, 0x28, 0xd6, 0xd4, 0x14, 0xe0, 0x00, 0x1f, 0x34, 0x0c, 0x6d, 0x18, 0xba, + 0xca, 0xd2, 0xb3, 0x39, 0xd8, 0x05, 0x58, 0x96, 0x73, 0x2b, 0x82, 0x8c, 0xad, 0xb2, 0x5c, 0x38, + 0x9e, 0x31, 0xc3, 0xa5, 0xd2, 0xdc, 0x29, 0xd0, 0xc1, 0x99, 0xa6, 0x9d, 0x4c, 0x29, 0xb4, 0xb0, + 0xca, 0xb6, 0x5c, 0x22, 0x41, 0x82, 0x3f, 0x59, 0x73, 0xb5, 0xe8, 0x40, 0x02, 0xc8, 0x27, 0xc1, + 0xb8, 0x51, 0x8c, 0x6b, 0x0d, 0xce, 0xc7, 0xb5, 0x9e, 0x51, 0x82, 0xf0, 0x4d, 0xd3, 0x78, 0xcd, + 0x0b, 0xbe, 0xb0, 0x33, 0xb1, 0x2c, 0x85, 0x75, 0xa3, 0x2b, 0x74, 0xd8, 0x41, 0xad, 0x01, 0x6d, + 0x05, 0x1e, 0xa3, 0x9e, 0xf1, 0x48, 0x3f, 0x1e, 0xc6, 0xa7, 0xfb, 0xe3, 0x84, 0xfe, 0xfe, 0x07, + 0x0d, 0xea, 0xc9, 0xee, 0xe6, 0xf3, 0x38, 0x9a, 0xb5, 0xca, 0xb1, 0x43, 0x7b, 0x3e, 0x0a, 0x3f, + 0xa2, 0x5e, 0x10, 0xe0, 0x61, 0xd7, 0xf6, 0xb7, 0x3f, 0x3d, 0xf9, 0x47, 0x11, 0xde, 0x32, 0x1a, + 0xbc, 0xbc, 0x7f, 0xbf, 0xed, 0x1c, 0xe1, 0x84, 0x75, 0x16, 0x09, 0xad, 0x93, 0xcb, 0x4d, 0x45, + 0xe2, 0x6d, 0x45, 0xe2, 0xaf, 0x8a, 0xc4, 0xaf, 0x35, 0x89, 0xb6, 0x35, 0x89, 0x3e, 0x6a, 0x12, + 0xdd, 0x9e, 0x4b, 0xe5, 0xee, 0xcb, 0x9c, 0xce, 0x61, 0xc1, 0xa6, 0x7e, 0xf7, 0x29, 0x68, 0x57, + 0xf0, 0xb9, 0xb3, 0x21, 0xe9, 0x39, 0x64, 0xb9, 0xb5, 0x11, 0x36, 0xef, 0xf9, 0x95, 0x2e, 0x7e, + 0x02, 0x00, 0x00, 0xff, 0xff, 0xe9, 0xf5, 0x09, 0xb1, 0xcb, 0x01, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// QueryClient is the client API for Query service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type QueryClient interface { + // Params retrieves the Drip module params + Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) +} + +type queryClient struct { + cc grpc1.ClientConn +} + +func NewQueryClient(cc grpc1.ClientConn) QueryClient { + return &queryClient{cc} +} + +func (c *queryClient) Params(ctx context.Context, in *QueryParamsRequest, opts ...grpc.CallOption) (*QueryParamsResponse, error) { + out := new(QueryParamsResponse) + err := c.cc.Invoke(ctx, "/juno.drip.v1.Query/Params", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// QueryServer is the server API for Query service. +type QueryServer interface { + // Params retrieves the Drip module params + Params(context.Context, *QueryParamsRequest) (*QueryParamsResponse, error) +} + +// UnimplementedQueryServer can be embedded to have forward compatible implementations. +type UnimplementedQueryServer struct { +} + +func (*UnimplementedQueryServer) Params(ctx context.Context, req *QueryParamsRequest) (*QueryParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method Params not implemented") +} + +func RegisterQueryServer(s grpc1.Server, srv QueryServer) { + s.RegisterService(&_Query_serviceDesc, srv) +} + +func _Query_Params_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(QueryParamsRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(QueryServer).Params(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/juno.drip.v1.Query/Params", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(QueryServer).Params(ctx, req.(*QueryParamsRequest)) + } + return interceptor(ctx, in, info, handler) +} + +var _Query_serviceDesc = grpc.ServiceDesc{ + ServiceName: "juno.drip.v1.Query", + HandlerType: (*QueryServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "Params", + Handler: _Query_Params_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "juno/drip/v1/query.proto", +} + +func (m *QueryParamsRequest) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsRequest) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *QueryParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *QueryParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *QueryParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintQuery(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0xa + return len(dAtA) - i, nil +} + +func encodeVarintQuery(dAtA []byte, offset int, v uint64) int { + offset -= sovQuery(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *QueryParamsRequest) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *QueryParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = m.Params.Size() + n += 1 + l + sovQuery(uint64(l)) + return n +} + +func sovQuery(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozQuery(x uint64) (n int) { + return sovQuery(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *QueryParamsRequest) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsRequest: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsRequest: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *QueryParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: QueryParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: QueryParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowQuery + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthQuery + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthQuery + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipQuery(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthQuery + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipQuery(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowQuery + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthQuery + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupQuery + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthQuery + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthQuery = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowQuery = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupQuery = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/drip/types/query.pb.gw.go b/x/drip/types/query.pb.gw.go new file mode 100644 index 000000000..71cf204df --- /dev/null +++ b/x/drip/types/query.pb.gw.go @@ -0,0 +1,153 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: juno/drip/v1/query.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +func request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, client QueryClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := client.Params(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Query_Params_0(ctx context.Context, marshaler runtime.Marshaler, server QueryServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq QueryParamsRequest + var metadata runtime.ServerMetadata + + msg, err := server.Params(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterQueryHandlerServer registers the http handlers for service Query to "mux". +// UnaryRPC :call QueryServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterQueryHandlerFromEndpoint instead. +func RegisterQueryHandlerServer(ctx context.Context, mux *runtime.ServeMux, server QueryServer) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Query_Params_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterQueryHandlerFromEndpoint is same as RegisterQueryHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterQueryHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterQueryHandler(ctx, mux, conn) +} + +// RegisterQueryHandler registers the http handlers for service Query to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterQueryHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterQueryHandlerClient(ctx, mux, NewQueryClient(conn)) +} + +// RegisterQueryHandlerClient registers the http handlers for service Query +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "QueryClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "QueryClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "QueryClient" to call the correct interceptors. +func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, client QueryClient) error { + + mux.Handle("GET", pattern_Query_Params_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Query_Params_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Query_Params_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Query_Params_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"juno", "drip", "v1", "params"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Query_Params_0 = runtime.ForwardResponseMessage +) diff --git a/x/drip/types/tx.pb.go b/x/drip/types/tx.pb.go new file mode 100644 index 000000000..c0c8495a4 --- /dev/null +++ b/x/drip/types/tx.pb.go @@ -0,0 +1,1003 @@ +// Code generated by protoc-gen-gogo. DO NOT EDIT. +// source: juno/drip/v1/tx.proto + +package types + +import ( + context "context" + fmt "fmt" + _ "github.com/cosmos/cosmos-proto" + github_com_cosmos_cosmos_sdk_types "github.com/cosmos/cosmos-sdk/types" + types "github.com/cosmos/cosmos-sdk/types" + _ "github.com/cosmos/cosmos-sdk/types/msgservice" + _ "github.com/cosmos/cosmos-sdk/types/tx/amino" + _ "github.com/cosmos/gogoproto/gogoproto" + grpc1 "github.com/cosmos/gogoproto/grpc" + proto "github.com/cosmos/gogoproto/proto" + _ "google.golang.org/genproto/googleapis/api/annotations" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" + io "io" + math "math" + math_bits "math/bits" +) + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package + +// MsgDistributeTokens defines a message that registers a Distribution of tokens. +type MsgDistributeTokens struct { + // sender_address is the bech32 address of message sender. + SenderAddress string `protobuf:"bytes,1,opt,name=sender_address,json=senderAddress,proto3" json:"sender_address,omitempty"` + // amount is the amount being airdropped to stakers + Amount github_com_cosmos_cosmos_sdk_types.Coins `protobuf:"bytes,2,rep,name=amount,proto3,castrepeated=github.com/cosmos/cosmos-sdk/types.Coins" json:"amount"` +} + +func (m *MsgDistributeTokens) Reset() { *m = MsgDistributeTokens{} } +func (m *MsgDistributeTokens) String() string { return proto.CompactTextString(m) } +func (*MsgDistributeTokens) ProtoMessage() {} +func (*MsgDistributeTokens) Descriptor() ([]byte, []int) { + return fileDescriptor_73c0f1d75f17f4bc, []int{0} +} +func (m *MsgDistributeTokens) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDistributeTokens) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDistributeTokens.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDistributeTokens) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDistributeTokens.Merge(m, src) +} +func (m *MsgDistributeTokens) XXX_Size() int { + return m.Size() +} +func (m *MsgDistributeTokens) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDistributeTokens.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDistributeTokens proto.InternalMessageInfo + +func (m *MsgDistributeTokens) GetSenderAddress() string { + if m != nil { + return m.SenderAddress + } + return "" +} + +func (m *MsgDistributeTokens) GetAmount() github_com_cosmos_cosmos_sdk_types.Coins { + if m != nil { + return m.Amount + } + return nil +} + +// MsgDistributeTokensResponse defines the MsgDistributeTokens response type +type MsgDistributeTokensResponse struct { +} + +func (m *MsgDistributeTokensResponse) Reset() { *m = MsgDistributeTokensResponse{} } +func (m *MsgDistributeTokensResponse) String() string { return proto.CompactTextString(m) } +func (*MsgDistributeTokensResponse) ProtoMessage() {} +func (*MsgDistributeTokensResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_73c0f1d75f17f4bc, []int{1} +} +func (m *MsgDistributeTokensResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgDistributeTokensResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgDistributeTokensResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgDistributeTokensResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgDistributeTokensResponse.Merge(m, src) +} +func (m *MsgDistributeTokensResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgDistributeTokensResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgDistributeTokensResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgDistributeTokensResponse proto.InternalMessageInfo + +// MsgUpdateParams is the Msg/UpdateParams request type. +// +// Since: cosmos-sdk 0.47 +type MsgUpdateParams struct { + // authority is the address that controls the module (defaults to x/gov unless overwritten). + Authority string `protobuf:"bytes,1,opt,name=authority,proto3" json:"authority,omitempty"` + // params defines the x/auth parameters to update. + // + // NOTE: All parameters must be supplied. + Params Params `protobuf:"bytes,2,opt,name=params,proto3" json:"params"` +} + +func (m *MsgUpdateParams) Reset() { *m = MsgUpdateParams{} } +func (m *MsgUpdateParams) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParams) ProtoMessage() {} +func (*MsgUpdateParams) Descriptor() ([]byte, []int) { + return fileDescriptor_73c0f1d75f17f4bc, []int{2} +} +func (m *MsgUpdateParams) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParams) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParams.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParams) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParams.Merge(m, src) +} +func (m *MsgUpdateParams) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParams) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParams.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParams proto.InternalMessageInfo + +func (m *MsgUpdateParams) GetAuthority() string { + if m != nil { + return m.Authority + } + return "" +} + +func (m *MsgUpdateParams) GetParams() Params { + if m != nil { + return m.Params + } + return Params{} +} + +type MsgUpdateParamsResponse struct { +} + +func (m *MsgUpdateParamsResponse) Reset() { *m = MsgUpdateParamsResponse{} } +func (m *MsgUpdateParamsResponse) String() string { return proto.CompactTextString(m) } +func (*MsgUpdateParamsResponse) ProtoMessage() {} +func (*MsgUpdateParamsResponse) Descriptor() ([]byte, []int) { + return fileDescriptor_73c0f1d75f17f4bc, []int{3} +} +func (m *MsgUpdateParamsResponse) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *MsgUpdateParamsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + if deterministic { + return xxx_messageInfo_MsgUpdateParamsResponse.Marshal(b, m, deterministic) + } else { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil + } +} +func (m *MsgUpdateParamsResponse) XXX_Merge(src proto.Message) { + xxx_messageInfo_MsgUpdateParamsResponse.Merge(m, src) +} +func (m *MsgUpdateParamsResponse) XXX_Size() int { + return m.Size() +} +func (m *MsgUpdateParamsResponse) XXX_DiscardUnknown() { + xxx_messageInfo_MsgUpdateParamsResponse.DiscardUnknown(m) +} + +var xxx_messageInfo_MsgUpdateParamsResponse proto.InternalMessageInfo + +func init() { + proto.RegisterType((*MsgDistributeTokens)(nil), "juno.drip.v1.MsgDistributeTokens") + proto.RegisterType((*MsgDistributeTokensResponse)(nil), "juno.drip.v1.MsgDistributeTokensResponse") + proto.RegisterType((*MsgUpdateParams)(nil), "juno.drip.v1.MsgUpdateParams") + proto.RegisterType((*MsgUpdateParamsResponse)(nil), "juno.drip.v1.MsgUpdateParamsResponse") +} + +func init() { proto.RegisterFile("juno/drip/v1/tx.proto", fileDescriptor_73c0f1d75f17f4bc) } + +var fileDescriptor_73c0f1d75f17f4bc = []byte{ + // 557 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x93, 0xbd, 0x6f, 0x13, 0x4d, + 0x10, 0xc6, 0x7d, 0xc9, 0xab, 0x48, 0xde, 0xf8, 0xe5, 0xe3, 0x30, 0x8a, 0x6d, 0xc8, 0xd9, 0x39, + 0x11, 0xc9, 0x58, 0xe4, 0x56, 0x36, 0x08, 0xa4, 0x74, 0xd8, 0x28, 0x9d, 0x25, 0x64, 0x42, 0x43, + 0x63, 0xad, 0x7d, 0xab, 0xcd, 0x92, 0xdc, 0xee, 0xe9, 0x66, 0x6d, 0xd9, 0x6d, 0x4a, 0x44, 0x81, + 0x44, 0x47, 0x45, 0x89, 0xa8, 0x5c, 0x50, 0xd0, 0xd0, 0xa7, 0x8c, 0xa0, 0xa1, 0x02, 0x64, 0x23, + 0x19, 0xf1, 0x57, 0xa0, 0xbb, 0xdd, 0x24, 0xb6, 0x13, 0x41, 0x73, 0x1f, 0xcf, 0x33, 0x3b, 0xf3, + 0x9b, 0xb9, 0x39, 0x74, 0xfd, 0x79, 0x4f, 0x48, 0xec, 0x47, 0x3c, 0xc4, 0xfd, 0x2a, 0x56, 0x03, + 0x2f, 0x8c, 0xa4, 0x92, 0x76, 0x26, 0x96, 0xbd, 0x58, 0xf6, 0xfa, 0xd5, 0x42, 0x96, 0x49, 0x26, + 0x13, 0x03, 0xc7, 0x4f, 0x3a, 0xa6, 0x70, 0x93, 0x49, 0xc9, 0x0e, 0x28, 0x26, 0x21, 0xc7, 0x44, + 0x08, 0xa9, 0x88, 0xe2, 0x52, 0x80, 0x71, 0xaf, 0x92, 0x80, 0x0b, 0x89, 0x93, 0xab, 0x91, 0x9c, + 0xae, 0x84, 0x40, 0x02, 0xee, 0x10, 0xa0, 0xb8, 0x5f, 0xed, 0x50, 0x45, 0xaa, 0xb8, 0x2b, 0xb9, + 0x30, 0xfe, 0x9a, 0xf1, 0x03, 0x60, 0x31, 0x4c, 0x00, 0xcc, 0x18, 0x79, 0x6d, 0xb4, 0x35, 0x82, + 0x7e, 0x31, 0x56, 0x61, 0x8e, 0x9f, 0x51, 0x41, 0x81, 0x1b, 0xcf, 0xfd, 0x64, 0xa1, 0x6b, 0x4d, + 0x60, 0x8f, 0x38, 0xa8, 0x88, 0x77, 0x7a, 0x8a, 0xee, 0xca, 0x7d, 0x2a, 0xc0, 0xde, 0x44, 0x97, + 0x80, 0x0a, 0x9f, 0x46, 0x6d, 0xe2, 0xfb, 0x11, 0x05, 0xc8, 0x59, 0x25, 0xab, 0x9c, 0x6e, 0xfd, + 0xaf, 0xd5, 0x87, 0x5a, 0xb4, 0x87, 0x68, 0x85, 0x04, 0xb2, 0x27, 0x54, 0x6e, 0xa9, 0xb4, 0x5c, + 0x5e, 0xad, 0xe5, 0x3d, 0x53, 0x39, 0xe6, 0xf7, 0x0c, 0xbf, 0xd7, 0x90, 0x5c, 0xd4, 0x77, 0x8e, + 0xbe, 0x15, 0x53, 0xef, 0xbf, 0x17, 0xcb, 0x8c, 0xab, 0xbd, 0x5e, 0xc7, 0xeb, 0xca, 0xc0, 0x60, + 0x9a, 0xdb, 0x16, 0xf8, 0xfb, 0x58, 0x0d, 0x43, 0x0a, 0xc9, 0x01, 0x78, 0x33, 0x1d, 0x55, 0x32, + 0x07, 0x94, 0x91, 0xee, 0xb0, 0x1d, 0x4f, 0x00, 0xde, 0x4d, 0x47, 0x15, 0xab, 0x65, 0x0a, 0x6e, + 0xff, 0xf7, 0xeb, 0x6d, 0x31, 0xe5, 0xae, 0xa3, 0x1b, 0x17, 0xe0, 0xb7, 0x28, 0x84, 0x52, 0x00, + 0x75, 0x3f, 0x5a, 0xe8, 0x72, 0x13, 0xd8, 0xd3, 0xd0, 0x27, 0x8a, 0x3e, 0x26, 0x11, 0x09, 0xc0, + 0xbe, 0x8f, 0xd2, 0xa4, 0xa7, 0xf6, 0x64, 0xc4, 0xd5, 0x50, 0x77, 0x55, 0xcf, 0x7d, 0xfe, 0xb0, + 0x95, 0x35, 0xe4, 0xa6, 0xb5, 0x27, 0x2a, 0xe2, 0x82, 0xb5, 0xce, 0x42, 0xed, 0x07, 0x68, 0x25, + 0x4c, 0x32, 0xe4, 0x96, 0x4a, 0x56, 0x79, 0xb5, 0x96, 0xf5, 0x66, 0x17, 0xc0, 0xd3, 0xd9, 0xeb, + 0xe9, 0xb8, 0x4d, 0x43, 0xaa, 0xc3, 0xb7, 0xef, 0x1d, 0x4e, 0x47, 0x95, 0xb3, 0x44, 0x2f, 0xa6, + 0xa3, 0xca, 0xc6, 0x4c, 0xcb, 0x03, 0x1c, 0x5b, 0x78, 0x01, 0xd3, 0xcd, 0xa3, 0xb5, 0x05, 0xe9, + 0xa4, 0xab, 0xda, 0x6f, 0x0b, 0x2d, 0x37, 0x81, 0xd9, 0x2f, 0x2d, 0x74, 0xe5, 0xdc, 0x97, 0xdb, + 0x98, 0xc7, 0xba, 0x60, 0x3a, 0x85, 0xdb, 0xff, 0x0c, 0x39, 0x1d, 0x60, 0xe5, 0xf0, 0xcb, 0xcf, + 0xd7, 0x4b, 0xb7, 0x5c, 0x17, 0x2f, 0xfc, 0x04, 0xd8, 0x3f, 0x3d, 0xd2, 0x56, 0xba, 0xf2, 0x2e, + 0xca, 0xcc, 0x0d, 0x7a, 0xfd, 0x5c, 0x99, 0x59, 0xbb, 0xb0, 0xf9, 0x57, 0xfb, 0x84, 0xa0, 0xbe, + 0x73, 0x34, 0x76, 0xac, 0xe3, 0xb1, 0x63, 0xfd, 0x18, 0x3b, 0xd6, 0xab, 0x89, 0x93, 0x3a, 0x9e, + 0x38, 0xa9, 0xaf, 0x13, 0x27, 0xf5, 0xec, 0xce, 0xcc, 0x26, 0x35, 0x92, 0x79, 0x36, 0xa4, 0x50, + 0x11, 0xe9, 0x2a, 0xd0, 0xb4, 0x03, 0xcd, 0x9b, 0xec, 0x54, 0x67, 0x25, 0x59, 0xf8, 0xbb, 0x7f, + 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0x1f, 0x02, 0x23, 0xce, 0x03, 0x00, 0x00, +} + +// Reference imports to suppress errors if they are not otherwise used. +var _ context.Context +var _ grpc.ClientConn + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +const _ = grpc.SupportPackageIsVersion4 + +// MsgClient is the client API for Msg service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type MsgClient interface { + // DistributeTokens distribute the sent tokens to all stakers in the next block + DistributeTokens(ctx context.Context, in *MsgDistributeTokens, opts ...grpc.CallOption) (*MsgDistributeTokensResponse, error) + UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) +} + +type msgClient struct { + cc grpc1.ClientConn +} + +func NewMsgClient(cc grpc1.ClientConn) MsgClient { + return &msgClient{cc} +} + +func (c *msgClient) DistributeTokens(ctx context.Context, in *MsgDistributeTokens, opts ...grpc.CallOption) (*MsgDistributeTokensResponse, error) { + out := new(MsgDistributeTokensResponse) + err := c.cc.Invoke(ctx, "/juno.drip.v1.Msg/DistributeTokens", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *msgClient) UpdateParams(ctx context.Context, in *MsgUpdateParams, opts ...grpc.CallOption) (*MsgUpdateParamsResponse, error) { + out := new(MsgUpdateParamsResponse) + err := c.cc.Invoke(ctx, "/juno.drip.v1.Msg/UpdateParams", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MsgServer is the server API for Msg service. +type MsgServer interface { + // DistributeTokens distribute the sent tokens to all stakers in the next block + DistributeTokens(context.Context, *MsgDistributeTokens) (*MsgDistributeTokensResponse, error) + UpdateParams(context.Context, *MsgUpdateParams) (*MsgUpdateParamsResponse, error) +} + +// UnimplementedMsgServer can be embedded to have forward compatible implementations. +type UnimplementedMsgServer struct { +} + +func (*UnimplementedMsgServer) DistributeTokens(ctx context.Context, req *MsgDistributeTokens) (*MsgDistributeTokensResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method DistributeTokens not implemented") +} +func (*UnimplementedMsgServer) UpdateParams(ctx context.Context, req *MsgUpdateParams) (*MsgUpdateParamsResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method UpdateParams not implemented") +} + +func RegisterMsgServer(s grpc1.Server, srv MsgServer) { + s.RegisterService(&_Msg_serviceDesc, srv) +} + +func _Msg_DistributeTokens_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgDistributeTokens) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).DistributeTokens(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/juno.drip.v1.Msg/DistributeTokens", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).DistributeTokens(ctx, req.(*MsgDistributeTokens)) + } + return interceptor(ctx, in, info, handler) +} + +func _Msg_UpdateParams_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(MsgUpdateParams) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MsgServer).UpdateParams(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/juno.drip.v1.Msg/UpdateParams", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MsgServer).UpdateParams(ctx, req.(*MsgUpdateParams)) + } + return interceptor(ctx, in, info, handler) +} + +var _Msg_serviceDesc = grpc.ServiceDesc{ + ServiceName: "juno.drip.v1.Msg", + HandlerType: (*MsgServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "DistributeTokens", + Handler: _Msg_DistributeTokens_Handler, + }, + { + MethodName: "UpdateParams", + Handler: _Msg_UpdateParams_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "juno/drip/v1/tx.proto", +} + +func (m *MsgDistributeTokens) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDistributeTokens) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDistributeTokens) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Amount) > 0 { + for iNdEx := len(m.Amount) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Amount[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + } + if len(m.SenderAddress) > 0 { + i -= len(m.SenderAddress) + copy(dAtA[i:], m.SenderAddress) + i = encodeVarintTx(dAtA, i, uint64(len(m.SenderAddress))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgDistributeTokensResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgDistributeTokensResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgDistributeTokensResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParams) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParams) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParams) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + { + size, err := m.Params.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintTx(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + if len(m.Authority) > 0 { + i -= len(m.Authority) + copy(dAtA[i:], m.Authority) + i = encodeVarintTx(dAtA, i, uint64(len(m.Authority))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + +func (m *MsgUpdateParamsResponse) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *MsgUpdateParamsResponse) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *MsgUpdateParamsResponse) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + return len(dAtA) - i, nil +} + +func encodeVarintTx(dAtA []byte, offset int, v uint64) int { + offset -= sovTx(v) + base := offset + for v >= 1<<7 { + dAtA[offset] = uint8(v&0x7f | 0x80) + v >>= 7 + offset++ + } + dAtA[offset] = uint8(v) + return base +} +func (m *MsgDistributeTokens) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.SenderAddress) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + if len(m.Amount) > 0 { + for _, e := range m.Amount { + l = e.Size() + n += 1 + l + sovTx(uint64(l)) + } + } + return n +} + +func (m *MsgDistributeTokensResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func (m *MsgUpdateParams) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Authority) + if l > 0 { + n += 1 + l + sovTx(uint64(l)) + } + l = m.Params.Size() + n += 1 + l + sovTx(uint64(l)) + return n +} + +func (m *MsgUpdateParamsResponse) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + return n +} + +func sovTx(x uint64) (n int) { + return (math_bits.Len64(x|1) + 6) / 7 +} +func sozTx(x uint64) (n int) { + return sovTx(uint64((x << 1) ^ uint64((int64(x) >> 63)))) +} +func (m *MsgDistributeTokens) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDistributeTokens: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDistributeTokens: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field SenderAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.SenderAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Amount", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Amount = append(m.Amount, types.Coin{}) + if err := m.Amount[len(m.Amount)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgDistributeTokensResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgDistributeTokensResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgDistributeTokensResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateParams) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParams: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParams: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Authority", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Authority = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Params", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthTx + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthTx + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if err := m.Params.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func (m *MsgUpdateParamsResponse) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowTx + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: MsgUpdateParamsResponse: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + default: + iNdEx = preIndex + skippy, err := skipTx(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthTx + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} +func skipTx(dAtA []byte) (n int, err error) { + l := len(dAtA) + iNdEx := 0 + depth := 0 + for iNdEx < l { + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= (uint64(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + wireType := int(wire & 0x7) + switch wireType { + case 0: + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + iNdEx++ + if dAtA[iNdEx-1] < 0x80 { + break + } + } + case 1: + iNdEx += 8 + case 2: + var length int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return 0, ErrIntOverflowTx + } + if iNdEx >= l { + return 0, io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + length |= (int(b) & 0x7F) << shift + if b < 0x80 { + break + } + } + if length < 0 { + return 0, ErrInvalidLengthTx + } + iNdEx += length + case 3: + depth++ + case 4: + if depth == 0 { + return 0, ErrUnexpectedEndOfGroupTx + } + depth-- + case 5: + iNdEx += 4 + default: + return 0, fmt.Errorf("proto: illegal wireType %d", wireType) + } + if iNdEx < 0 { + return 0, ErrInvalidLengthTx + } + if depth == 0 { + return iNdEx, nil + } + } + return 0, io.ErrUnexpectedEOF +} + +var ( + ErrInvalidLengthTx = fmt.Errorf("proto: negative length found during unmarshaling") + ErrIntOverflowTx = fmt.Errorf("proto: integer overflow") + ErrUnexpectedEndOfGroupTx = fmt.Errorf("proto: unexpected end of group") +) diff --git a/x/drip/types/tx.pb.gw.go b/x/drip/types/tx.pb.gw.go new file mode 100644 index 000000000..1ce9048aa --- /dev/null +++ b/x/drip/types/tx.pb.gw.go @@ -0,0 +1,171 @@ +// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT. +// source: juno/drip/v1/tx.proto + +/* +Package types is a reverse proxy. + +It translates gRPC into RESTful JSON APIs. +*/ +package types + +import ( + "context" + "io" + "net/http" + + "github.com/golang/protobuf/descriptor" + "github.com/golang/protobuf/proto" + "github.com/grpc-ecosystem/grpc-gateway/runtime" + "github.com/grpc-ecosystem/grpc-gateway/utilities" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" +) + +// Suppress "imported and not used" errors +var _ codes.Code +var _ io.Reader +var _ status.Status +var _ = runtime.String +var _ = utilities.NewDoubleArray +var _ = descriptor.ForMessage +var _ = metadata.Join + +var ( + filter_Msg_DistributeTokens_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)} +) + +func request_Msg_DistributeTokens_0(ctx context.Context, marshaler runtime.Marshaler, client MsgClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq MsgDistributeTokens + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_DistributeTokens_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := client.DistributeTokens(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD)) + return msg, metadata, err + +} + +func local_request_Msg_DistributeTokens_0(ctx context.Context, marshaler runtime.Marshaler, server MsgServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) { + var protoReq MsgDistributeTokens + var metadata runtime.ServerMetadata + + if err := req.ParseForm(); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_Msg_DistributeTokens_0); err != nil { + return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err) + } + + msg, err := server.DistributeTokens(ctx, &protoReq) + return msg, metadata, err + +} + +// RegisterMsgHandlerServer registers the http handlers for service Msg to "mux". +// UnaryRPC :call MsgServer directly. +// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906. +// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterMsgHandlerFromEndpoint instead. +func RegisterMsgHandlerServer(ctx context.Context, mux *runtime.ServeMux, server MsgServer) error { + + mux.Handle("POST", pattern_Msg_DistributeTokens_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + var stream runtime.ServerTransportStream + ctx = grpc.NewContextWithServerTransportStream(ctx, &stream) + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := local_request_Msg_DistributeTokens_0(rctx, inboundMarshaler, server, req, pathParams) + md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer()) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Msg_DistributeTokens_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +// RegisterMsgHandlerFromEndpoint is same as RegisterMsgHandler but +// automatically dials to "endpoint" and closes the connection when "ctx" gets done. +func RegisterMsgHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { + conn, err := grpc.Dial(endpoint, opts...) + if err != nil { + return err + } + defer func() { + if err != nil { + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + return + } + go func() { + <-ctx.Done() + if cerr := conn.Close(); cerr != nil { + grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr) + } + }() + }() + + return RegisterMsgHandler(ctx, mux, conn) +} + +// RegisterMsgHandler registers the http handlers for service Msg to "mux". +// The handlers forward requests to the grpc endpoint over "conn". +func RegisterMsgHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error { + return RegisterMsgHandlerClient(ctx, mux, NewMsgClient(conn)) +} + +// RegisterMsgHandlerClient registers the http handlers for service Msg +// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MsgClient". +// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MsgClient" +// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in +// "MsgClient" to call the correct interceptors. +func RegisterMsgHandlerClient(ctx context.Context, mux *runtime.ServeMux, client MsgClient) error { + + mux.Handle("POST", pattern_Msg_DistributeTokens_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) { + ctx, cancel := context.WithCancel(req.Context()) + defer cancel() + inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req) + rctx, err := runtime.AnnotateContext(ctx, mux, req) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + resp, md, err := request_Msg_DistributeTokens_0(rctx, inboundMarshaler, client, req, pathParams) + ctx = runtime.NewServerMetadataContext(ctx, md) + if err != nil { + runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err) + return + } + + forward_Msg_DistributeTokens_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...) + + }) + + return nil +} + +var ( + pattern_Msg_DistributeTokens_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 2, 4}, []string{"juno", "drip", "v1", "tx", "distribute_tokens"}, "", runtime.AssumeColonVerbOpt(false))) +) + +var ( + forward_Msg_DistributeTokens_0 = runtime.ForwardResponseMessage +) diff --git a/x/feeshare/ante/ante.go b/x/feeshare/ante/ante.go index ef12d000c..52035e701 100644 --- a/x/feeshare/ante/ante.go +++ b/x/feeshare/ante/ante.go @@ -1,6 +1,8 @@ package ante import ( + "encoding/json" + wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" errorsmod "cosmossdk.io/errors" @@ -55,6 +57,11 @@ func FeePayLogic(fees sdk.Coins, govPercent sdk.Dec, numPairs int) sdk.Coins { return splitFees } +type FeeSharePayoutEventOutput struct { + WithdrawAddress sdk.AccAddress `json:"withdraw_address"` + FeesPaid sdk.Coins `json:"fees_paid"` +} + // FeeSharePayout takes the total fees and redistributes 50% (or param set) to the contract developers // provided they opted-in to payments. func FeeSharePayout(ctx sdk.Context, bankKeeper BankKeeper, totalFees sdk.Coins, revKeeper FeeShareKeeper, msgs []sdk.Msg) error { @@ -101,20 +108,37 @@ func FeeSharePayout(ctx sdk.Context, bankKeeper BankKeeper, totalFees sdk.Coins, } } - // FeeShare logic payouts for contracts numPairs := len(toPay) + + feesPaidOutput := make([]FeeSharePayoutEventOutput, numPairs) if numPairs > 0 { govPercent := params.DeveloperShares splitFees := FeePayLogic(fees, govPercent, numPairs) // pay fees evenly between all withdraw addresses - for _, withdrawAddr := range toPay { + for i, withdrawAddr := range toPay { err := bankKeeper.SendCoinsFromModuleToAccount(ctx, authtypes.FeeCollectorName, withdrawAddr, splitFees) + feesPaidOutput[i] = FeeSharePayoutEventOutput{ + WithdrawAddress: withdrawAddr, + FeesPaid: splitFees, + } + if err != nil { return errorsmod.Wrapf(feeshare.ErrFeeSharePayment, "failed to pay fees to contract developer: %s", err.Error()) } } } + bz, err := json.Marshal(feesPaidOutput) + if err != nil { + return errorsmod.Wrapf(feeshare.ErrFeeSharePayment, "failed to marshal feesPaidOutput: %s", err.Error()) + } + + ctx.EventManager().EmitEvent( + sdk.NewEvent( + feeshare.EventTypePayoutFeeShare, + sdk.NewAttribute(feeshare.AttributeWithdrawPayouts, string(bz))), + ) + return nil } diff --git a/x/feeshare/keeper/msg_server.go b/x/feeshare/keeper/msg_server.go index 2eab96925..dcd48a1e9 100644 --- a/x/feeshare/keeper/msg_server.go +++ b/x/feeshare/keeper/msg_server.go @@ -16,75 +16,77 @@ import ( var _ types.MsgServer = &Keeper{} -func (k Keeper) GetIfContractWasCreatedFromFactory(ctx sdk.Context, _ sdk.AccAddress, info *wasmTypes.ContractInfo) bool { - // This will allow ANYONE to register FeeShare funds to its own contract if it was created from a factory contract - // Note: if there is no admin but a creator made it, then the creator can register it how they wish - - govAddress := k.accountKeeper.GetModuleAddress(govtypes.ModuleName).String() - - creator, err := sdk.AccAddressFromBech32(info.Creator) - if err != nil { - return false - } - - // If the admin is the gov module (ex: some subDAOs, its a factory contract. Allow register to itself) - if info.Admin == govAddress { +func (k Keeper) GetIfContractWasCreatedFromFactory(ctx sdk.Context, msgSender sdk.AccAddress, info *wasmTypes.ContractInfo) bool { + // Gov Module Admin + govMod := k.accountKeeper.GetModuleAddress(govtypes.ModuleName).String() + if info.Admin == govMod { + // only register to self. return true } - isFactoryContract := false if len(info.Admin) == 0 { - // if there is no admin & the creator is a contract, then its a factory contract - isFactoryContract = k.wasmKeeper.HasContractInfo(ctx, creator) - } else { - admin, err := sdk.AccAddressFromBech32(info.Admin) + // There is no admin. Return if the creator is a contract or normal user. + creator, err := sdk.AccAddressFromBech32(info.Creator) if err != nil { return false } - // if there is an admin & the admin is a contract, then its a factory contract - isFactoryContract = k.wasmKeeper.HasContractInfo(ctx, admin) + + // is factory if creator is a contract. + return k.wasmKeeper.HasContractInfo(ctx, creator) + } + + // There is an admin + admin, err := sdk.AccAddressFromBech32(info.Admin) + if err != nil { + return false + } + + if admin.String() == msgSender.String() { + return false } - return isFactoryContract + return k.wasmKeeper.HasContractInfo(ctx, admin) } // GetContractAdminOrCreatorAddress ensures the deployer is the contract's admin OR creator if no admin is set for all msg_server feeshare functions. func (k Keeper) GetContractAdminOrCreatorAddress(ctx sdk.Context, contract sdk.AccAddress, deployer string) (sdk.AccAddress, error) { - var controllingAccount sdk.AccAddress - - // Ensures deployer String is valid + // Ensure the deployer address is valid _, err := sdk.AccAddressFromBech32(deployer) if err != nil { return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid deployer address %s", deployer) } + // Retrieve contract info info := k.wasmKeeper.GetContractInfo(ctx, contract) + // Check if the contract has an admin if len(info.Admin) == 0 { - // no admin, see if they are the creator of the contract + // No admin, so check if the deployer is the creator of the contract if info.Creator != deployer { return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "you are not the creator of this contract %s", info.Creator) } - creatorAddr, err := sdk.AccAddressFromBech32(info.Creator) + _, err := sdk.AccAddressFromBech32(info.Creator) if err != nil { return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address %s", info.Creator) } - controllingAccount = creatorAddr - } else { - // Admin is set, so we check if the deployer is the admin - if info.Admin != deployer { - return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "you are not an admin of this contract %s", deployer) - } - adminAddr, err := sdk.AccAddressFromBech32(info.Admin) - if err != nil { - return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid admin address %s", info.Admin) - } - controllingAccount = adminAddr + // Deployer is the creator, return the controlling account as the creator's address + return sdk.AccAddressFromBech32(info.Creator) + } + + // Admin is set, so check if the deployer is the admin of the contract + if info.Admin != deployer { + return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "you are not an admin of this contract %s", deployer) + } + + // Verify the admin address is valid + _, err = sdk.AccAddressFromBech32(info.Admin) + if err != nil { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid admin address %s", info.Admin) } - return controllingAccount, nil + return sdk.AccAddressFromBech32(info.Admin) } // RegisterFeeShare registers a contract to receive transaction fees @@ -116,9 +118,15 @@ func (k Keeper) RegisterFeeShare( return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid withdrawer address %s", msg.WithdrawerAddress) } + // ensure msg.DeployerAddress is valid + msgSender, err := sdk.AccAddressFromBech32(msg.DeployerAddress) + if err != nil { + return nil, errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid deployer address %s", msg.DeployerAddress) + } + var deployer sdk.AccAddress - if k.GetIfContractWasCreatedFromFactory(ctx, contract, k.wasmKeeper.GetContractInfo(ctx, contract)) { + if k.GetIfContractWasCreatedFromFactory(ctx, msgSender, k.wasmKeeper.GetContractInfo(ctx, contract)) { // Anyone is allowed to register a contract to itself if it was created from a factory contract if msg.WithdrawerAddress != msg.ContractAddress { return nil, errorsmod.Wrapf(types.ErrFeeShareInvalidWithdrawer, "withdrawer address must be the same as the contract address if it is from a factory contract withdraw:%s contract:%s", msg.WithdrawerAddress, msg.ContractAddress) diff --git a/x/feeshare/keeper/msg_server_test.go b/x/feeshare/keeper/msg_server_test.go index a744f81f0..de9126605 100644 --- a/x/feeshare/keeper/msg_server_test.go +++ b/x/feeshare/keeper/msg_server_test.go @@ -9,6 +9,7 @@ import ( "github.com/cosmos/cosmos-sdk/testutil/testdata" sdk "github.com/cosmos/cosmos-sdk/types" + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/CosmosContracts/juno/v17/x/feeshare/types" ) @@ -113,9 +114,15 @@ func (s *IntegrationTestSuite) TestRegisterFeeShare() { _, _, sender := testdata.KeyTestPubAddr() _ = s.FundAccount(s.ctx, sender, sdk.NewCoins(sdk.NewCoin("stake", sdk.NewInt(1_000_000)))) + gov := s.accountKeeper.GetModuleAddress(govtypes.ModuleName).String() + govContract := s.InstantiateContract(sender.String(), gov) + contractAddress := s.InstantiateContract(sender.String(), "") contractAddress2 := s.InstantiateContract(contractAddress, contractAddress) + DAODAO := s.InstantiateContract(sender.String(), "") + subContract := s.InstantiateContract(DAODAO, DAODAO) + _, _, withdrawer := testdata.KeyTestPubAddr() for _, tc := range []struct { @@ -184,6 +191,36 @@ func (s *IntegrationTestSuite) TestRegisterFeeShare() { resp: &types.MsgRegisterFeeShareResponse{}, shouldErr: false, }, + { + desc: "Invalid register gov contract withdraw address", + msg: &types.MsgRegisterFeeShare{ + ContractAddress: govContract, + DeployerAddress: sender.String(), + WithdrawerAddress: sender.String(), + }, + resp: &types.MsgRegisterFeeShareResponse{}, + shouldErr: true, + }, + { + desc: "Success register gov contract withdraw address to self", + msg: &types.MsgRegisterFeeShare{ + ContractAddress: govContract, + DeployerAddress: sender.String(), + WithdrawerAddress: govContract, + }, + resp: &types.MsgRegisterFeeShareResponse{}, + shouldErr: false, + }, + { + desc: "Success register contract from DAODAO contract as admin", + msg: &types.MsgRegisterFeeShare{ + ContractAddress: subContract, + DeployerAddress: DAODAO, + WithdrawerAddress: DAODAO, + }, + resp: &types.MsgRegisterFeeShareResponse{}, + shouldErr: false, + }, } { tc := tc s.Run(tc.desc, func() { diff --git a/x/feeshare/types/errors.go b/x/feeshare/types/errors.go index 231c8557e..61ec1d99f 100644 --- a/x/feeshare/types/errors.go +++ b/x/feeshare/types/errors.go @@ -4,7 +4,6 @@ import ( errorsmod "cosmossdk.io/errors" ) -// errors var ( ErrFeeShareDisabled = errorsmod.Register(ModuleName, 1, "feeshare module is disabled by governance") ErrFeeShareAlreadyRegistered = errorsmod.Register(ModuleName, 2, "feeshare already exists for given contract") diff --git a/x/feeshare/types/events.go b/x/feeshare/types/events.go index a21a7601f..861e68624 100644 --- a/x/feeshare/types/events.go +++ b/x/feeshare/types/events.go @@ -1,11 +1,13 @@ package types -// feeshare events const ( EventTypeRegisterFeeShare = "register_feeshare" EventTypeCancelFeeShare = "cancel_feeshare" EventTypeUpdateFeeShare = "update_feeshare" + EventTypePayoutFeeShare = "payout_feeshare" + AttributeKeyContract = "contract" AttributeKeyWithdrawerAddress = "withdrawer_address" + AttributeWithdrawPayouts = "payouts" ) diff --git a/x/feeshare/types/expected_keepers.go b/x/feeshare/types/expected_keepers.go index 1592cf2bf..21b79ca8e 100644 --- a/x/feeshare/types/expected_keepers.go +++ b/x/feeshare/types/expected_keepers.go @@ -3,7 +3,6 @@ package types import ( wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" - // "github.com/cosmos/cosmos-sdk/client" sdk "github.com/cosmos/cosmos-sdk/types" acctypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) diff --git a/x/feeshare/types/keys.go b/x/feeshare/types/keys.go index a8444836f..02f25c3ed 100644 --- a/x/feeshare/types/keys.go +++ b/x/feeshare/types/keys.go @@ -2,7 +2,6 @@ package types import sdk "github.com/cosmos/cosmos-sdk/types" -// constants const ( // module name ModuleName = "feeshare" diff --git a/x/globalfee/ante/fee.go b/x/globalfee/ante/fee.go index d99bcb261..58c27e14d 100644 --- a/x/globalfee/ante/fee.go +++ b/x/globalfee/ante/fee.go @@ -88,7 +88,7 @@ func (mfd FeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, ne // if feeCoinsNoZeroDenom=[], DenomsSubsetOf returns true // if feeCoinsNoZeroDenom is not empty, but nonZeroCoinFeesReq empty, return false if !feeCoinsNonZeroDenom.DenomsSubsetOf(nonZeroCoinFeesReq) { - return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "fee is not a subset of required fees; got %s, required: %s", feeCoins, combinedFeeRequirement) + return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "this fee denom is not accepted; got %s, one is required: %s", feeCoins, PrettyPrint(combinedFeeRequirement)) } // Accept zero fee transactions only if both of the following statements are true: @@ -125,7 +125,11 @@ func (mfd FeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, ne // because when nonZeroCoinFeesReq empty, and DenomsSubsetOf check passed, // the tx should already passed before) if !feeCoinsNonZeroDenom.IsAnyGTE(nonZeroCoinFeesReq) { - return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; got: %s required: %s", feeCoins, combinedFeeRequirement) + if len(feeCoins) == 0 { + return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "no fees were specified; one fee must be provided %s", PrettyPrint(combinedFeeRequirement)) + } + + return ctx, errorsmod.Wrapf(sdkerrors.ErrInsufficientFee, "insufficient fees; only got: %s. one is required: %s. ", feeCoins, PrettyPrint(combinedFeeRequirement)) } } diff --git a/x/globalfee/ante/fee_utils.go b/x/globalfee/ante/fee_utils.go index b3d5e2c8e..6b70add32 100644 --- a/x/globalfee/ante/fee_utils.go +++ b/x/globalfee/ante/fee_utils.go @@ -1,6 +1,9 @@ package ante import ( + "encoding/json" + "fmt" + sdk "github.com/cosmos/cosmos-sdk/types" ) @@ -19,6 +22,21 @@ func ContainZeroCoins(coins sdk.Coins) bool { return false } +// PrettyPrint returns a pretty printed representation of the given coins. +func PrettyPrint(coins sdk.Coins) string { + arr := make([]string, len(coins)) + for idx, coin := range coins { + arr[idx] = fmt.Sprintf("%s%s", coin.Amount.String(), coin.Denom) + } + + bz, err := json.MarshalIndent(arr, "", " ") + if err != nil { + return "" + } + + return string(bz) +} + // CombinedFeeRequirement returns the global fee and min_gas_price combined and sorted. // Both globalFees and minGasPrices must be valid, but CombinedFeeRequirement // does not validate them, so it may return 0denom. diff --git a/x/globalfee/module.go b/x/globalfee/module.go index 382e2e480..b5ea15938 100644 --- a/x/globalfee/module.go +++ b/x/globalfee/module.go @@ -131,6 +131,7 @@ func (a AppModule) QuerierRoute() string { } func (a AppModule) RegisterServices(cfg module.Configurator) { + types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(a.keeper)) types.RegisterQueryServer(cfg.QueryServer(), NewGrpcQuerier(a.keeper)) m := keeper.NewMigrator(a.keeper, a.bondDenom) diff --git a/x/globalfee/types/codec.go b/x/globalfee/types/codec.go index 5eb8d3d9b..884148689 100644 --- a/x/globalfee/types/codec.go +++ b/x/globalfee/types/codec.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/msgservice" authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec" + govcodec "github.com/cosmos/cosmos-sdk/x/gov/codec" ) var ( @@ -24,6 +25,7 @@ func init() { // so that this can later be used to properly serialize MsgGrant and MsgExec // instances. RegisterLegacyAminoCodec(authzcodec.Amino) + RegisterLegacyAminoCodec(govcodec.Amino) } // RegisterLegacyAminoCodec registers concrete types on the LegacyAmino codec diff --git a/x/globalfee/types/query.pb.gw.go b/x/globalfee/types/query.pb.gw.go index 0e33d9df1..a7b2956f3 100644 --- a/x/globalfee/types/query.pb.gw.go +++ b/x/globalfee/types/query.pb.gw.go @@ -145,7 +145,7 @@ func RegisterQueryHandlerClient(ctx context.Context, mux *runtime.ServeMux, clie } var ( - pattern_Query_MinimumGasPrices_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"gaia", "globalfee", "v1beta1", "minimum_gas_prices"}, "", runtime.AssumeColonVerbOpt(true))) + pattern_Query_MinimumGasPrices_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"gaia", "globalfee", "v1beta1", "minimum_gas_prices"}, "", runtime.AssumeColonVerbOpt(false))) ) var ( diff --git a/x/mint/simulation/genesis_test.go b/x/mint/simulation/genesis_test.go index 99f81b284..016747707 100644 --- a/x/mint/simulation/genesis_test.go +++ b/x/mint/simulation/genesis_test.go @@ -77,6 +77,7 @@ func TestRandomizedGenState1(t *testing.T) { } for _, tt := range tests { + tt := tt require.Panicsf(t, func() { simulation.RandomizedGenState(&tt.simState) }, tt.panicMsg) } } diff --git a/x/tokenfactory/bindings/custom_msg_test.go b/x/tokenfactory/bindings/custom_msg_test.go index a094db501..373c6fd0d 100644 --- a/x/tokenfactory/bindings/custom_msg_test.go +++ b/x/tokenfactory/bindings/custom_msg_test.go @@ -28,14 +28,14 @@ func TestCreateDenomMsg(t *testing.T) { reflectAmount := sdk.NewCoins(sdk.NewCoin(types.DefaultParams().DenomCreationFee[0].Denom, types.DefaultParams().DenomCreationFee[0].Amount.MulRaw(100))) fundAccount(t, ctx, junoapp, reflect, reflectAmount) - msg := bindings.TokenMsg{CreateDenom: &bindings.CreateDenom{ + msg := bindings.TokenFactoryMsg{CreateDenom: &bindings.CreateDenom{ Subdenom: "SUN", }} err := executeCustom(t, ctx, junoapp, reflect, lucky, msg, sdk.Coin{}) require.NoError(t, err) // query the denom and see if it matches - query := bindings.TokenQuery{ + query := bindings.TokenFactoryQuery{ FullDenom: &bindings.FullDenom{ CreatorAddr: reflect.String(), Subdenom: "SUN", @@ -64,7 +64,7 @@ func TestMintMsg(t *testing.T) { require.Empty(t, balances) // Create denom for minting - msg := bindings.TokenMsg{CreateDenom: &bindings.CreateDenom{ + msg := bindings.TokenFactoryMsg{CreateDenom: &bindings.CreateDenom{ Subdenom: "SUN", }} err := executeCustom(t, ctx, junoapp, reflect, lucky, msg, sdk.Coin{}) @@ -73,7 +73,7 @@ func TestMintMsg(t *testing.T) { amount, ok := sdk.NewIntFromString("808010808") require.True(t, ok) - msg = bindings.TokenMsg{MintTokens: &bindings.MintTokens{ + msg = bindings.TokenFactoryMsg{MintTokens: &bindings.MintTokens{ Denom: sunDenom, Amount: amount, MintToAddress: lucky.String(), @@ -88,7 +88,7 @@ func TestMintMsg(t *testing.T) { require.Contains(t, coin.Denom, "factory/") // query the denom and see if it matches - query := bindings.TokenQuery{ + query := bindings.TokenFactoryQuery{ FullDenom: &bindings.FullDenom{ CreatorAddr: reflect.String(), Subdenom: "SUN", @@ -110,7 +110,7 @@ func TestMintMsg(t *testing.T) { require.Contains(t, coin.Denom, "factory/") // query the denom and see if it matches - query = bindings.TokenQuery{ + query = bindings.TokenFactoryQuery{ FullDenom: &bindings.FullDenom{ CreatorAddr: reflect.String(), Subdenom: "SUN", @@ -123,7 +123,7 @@ func TestMintMsg(t *testing.T) { // now mint another amount / denom // create it first - msg = bindings.TokenMsg{CreateDenom: &bindings.CreateDenom{ + msg = bindings.TokenFactoryMsg{CreateDenom: &bindings.CreateDenom{ Subdenom: "MOON", }} err = executeCustom(t, ctx, junoapp, reflect, lucky, msg, sdk.Coin{}) @@ -131,7 +131,7 @@ func TestMintMsg(t *testing.T) { moonDenom := fmt.Sprintf("factory/%s/%s", reflect.String(), msg.CreateDenom.Subdenom) amount = amount.SubRaw(1) - msg = bindings.TokenMsg{MintTokens: &bindings.MintTokens{ + msg = bindings.TokenFactoryMsg{MintTokens: &bindings.MintTokens{ Denom: moonDenom, Amount: amount, MintToAddress: lucky.String(), @@ -146,7 +146,7 @@ func TestMintMsg(t *testing.T) { require.Contains(t, coin.Denom, "factory/") // query the denom and see if it matches - query = bindings.TokenQuery{ + query = bindings.TokenFactoryQuery{ FullDenom: &bindings.FullDenom{ CreatorAddr: reflect.String(), Subdenom: "MOON", @@ -163,7 +163,7 @@ func TestMintMsg(t *testing.T) { require.Contains(t, coin.Denom, "factory/") // query the denom and see if it matches - query = bindings.TokenQuery{ + query = bindings.TokenFactoryQuery{ FullDenom: &bindings.FullDenom{ CreatorAddr: reflect.String(), Subdenom: "SUN", @@ -193,7 +193,7 @@ func TestForceTransfer(t *testing.T) { require.Empty(t, balances) // Create denom for minting - msg := bindings.TokenMsg{CreateDenom: &bindings.CreateDenom{ + msg := bindings.TokenFactoryMsg{CreateDenom: &bindings.CreateDenom{ Subdenom: "SUN", }} err := executeCustom(t, ctx, junoapp, reflect, lucky, msg, sdk.Coin{}) @@ -204,7 +204,7 @@ func TestForceTransfer(t *testing.T) { require.True(t, ok) // Mint new tokens to lucky - msg = bindings.TokenMsg{MintTokens: &bindings.MintTokens{ + msg = bindings.TokenFactoryMsg{MintTokens: &bindings.MintTokens{ Denom: sunDenom, Amount: amount, MintToAddress: lucky.String(), @@ -213,7 +213,7 @@ func TestForceTransfer(t *testing.T) { require.NoError(t, err) // Force move 100 tokens from lucky to rcpt - msg = bindings.TokenMsg{ForceTransfer: &bindings.ForceTransfer{ + msg = bindings.TokenFactoryMsg{ForceTransfer: &bindings.ForceTransfer{ Denom: sunDenom, Amount: sdk.NewInt(100), FromAddress: lucky.String(), @@ -246,7 +246,7 @@ func TestBurnMsg(t *testing.T) { require.Empty(t, balances) // Create denom for minting - msg := bindings.TokenMsg{CreateDenom: &bindings.CreateDenom{ + msg := bindings.TokenFactoryMsg{CreateDenom: &bindings.CreateDenom{ Subdenom: "SUN", }} err := executeCustom(t, ctx, junoapp, reflect, lucky, msg, sdk.Coin{}) @@ -256,7 +256,7 @@ func TestBurnMsg(t *testing.T) { amount, ok := sdk.NewIntFromString("808010809") require.True(t, ok) - msg = bindings.TokenMsg{MintTokens: &bindings.MintTokens{ + msg = bindings.TokenFactoryMsg{MintTokens: &bindings.MintTokens{ Denom: sunDenom, Amount: amount, MintToAddress: lucky.String(), @@ -267,7 +267,7 @@ func TestBurnMsg(t *testing.T) { // can burn from different address with burnFrom amt, ok := sdk.NewIntFromString("1") require.True(t, ok) - msg = bindings.TokenMsg{BurnTokens: &bindings.BurnTokens{ + msg = bindings.TokenFactoryMsg{BurnTokens: &bindings.BurnTokens{ Denom: sunDenom, Amount: amt, BurnFromAddress: lucky.String(), @@ -280,7 +280,7 @@ func TestBurnMsg(t *testing.T) { err = junoapp.AppKeepers.BankKeeper.SendCoins(ctx, lucky, reflect, luckyBalance) require.NoError(t, err) - msg = bindings.TokenMsg{BurnTokens: &bindings.BurnTokens{ + msg = bindings.TokenFactoryMsg{BurnTokens: &bindings.BurnTokens{ Denom: sunDenom, Amount: amount.Abs().Sub(sdk.NewInt(1)), BurnFromAddress: reflect.String(), @@ -302,11 +302,8 @@ type ReflectSubMsgs struct { Msgs []wasmvmtypes.SubMsg `json:"msgs"` } -func executeCustom(t *testing.T, ctx sdk.Context, junoapp *app.App, contract sdk.AccAddress, sender sdk.AccAddress, msg bindings.TokenMsg, funds sdk.Coin) error { //nolint:unparam // funds is always nil but could change in the future. - wrapped := bindings.TokenFactoryMsg{ - Token: &msg, - } - customBz, err := json.Marshal(wrapped) +func executeCustom(t *testing.T, ctx sdk.Context, junoapp *app.App, contract sdk.AccAddress, sender sdk.AccAddress, msg bindings.TokenFactoryMsg, funds sdk.Coin) error { //nolint:unparam // funds is always nil but could change in the future. + customBz, err := json.Marshal(msg) require.NoError(t, err) reflectMsg := ReflectExec{ diff --git a/x/tokenfactory/bindings/custom_query_test.go b/x/tokenfactory/bindings/custom_query_test.go index 77711a8e0..926c68656 100644 --- a/x/tokenfactory/bindings/custom_query_test.go +++ b/x/tokenfactory/bindings/custom_query_test.go @@ -22,7 +22,7 @@ func TestQueryFullDenom(t *testing.T) { require.NotEmpty(t, reflect) // query full denom - query := bindings.TokenQuery{ + query := bindings.TokenFactoryQuery{ FullDenom: &bindings.FullDenom{ CreatorAddr: reflect.String(), Subdenom: "ustart", @@ -47,11 +47,8 @@ type ChainResponse struct { Data []byte `json:"data"` } -func queryCustom(t *testing.T, ctx sdk.Context, junoapp *app.App, contract sdk.AccAddress, request bindings.TokenQuery, response interface{}) { - wrapped := bindings.TokenFactoryQuery{ - Token: &request, - } - msgBz, err := json.Marshal(wrapped) +func queryCustom(t *testing.T, ctx sdk.Context, junoapp *app.App, contract sdk.AccAddress, request bindings.TokenFactoryQuery, response interface{}) { + msgBz, err := json.Marshal(request) require.NoError(t, err) fmt.Println("queryCustom1", string(msgBz)) diff --git a/x/tokenfactory/bindings/message_plugin.go b/x/tokenfactory/bindings/message_plugin.go index 04e21f714..d5de403e8 100644 --- a/x/tokenfactory/bindings/message_plugin.go +++ b/x/tokenfactory/bindings/message_plugin.go @@ -9,7 +9,6 @@ import ( errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" @@ -46,28 +45,24 @@ func (m *CustomMessenger) DispatchMsg(ctx sdk.Context, contractAddr sdk.AccAddre if err := json.Unmarshal(msg.Custom, &contractMsg); err != nil { return nil, nil, errorsmod.Wrap(err, "token factory msg") } - if contractMsg.Token == nil { - return nil, nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "nil token field") - } - tokenMsg := contractMsg.Token - if tokenMsg.CreateDenom != nil { - return m.createDenom(ctx, contractAddr, tokenMsg.CreateDenom) + if contractMsg.CreateDenom != nil { + return m.createDenom(ctx, contractAddr, contractMsg.CreateDenom) } - if tokenMsg.MintTokens != nil { - return m.mintTokens(ctx, contractAddr, tokenMsg.MintTokens) + if contractMsg.MintTokens != nil { + return m.mintTokens(ctx, contractAddr, contractMsg.MintTokens) } - if tokenMsg.ChangeAdmin != nil { - return m.changeAdmin(ctx, contractAddr, tokenMsg.ChangeAdmin) + if contractMsg.ChangeAdmin != nil { + return m.changeAdmin(ctx, contractAddr, contractMsg.ChangeAdmin) } - if tokenMsg.BurnTokens != nil { - return m.burnTokens(ctx, contractAddr, tokenMsg.BurnTokens) + if contractMsg.BurnTokens != nil { + return m.burnTokens(ctx, contractAddr, contractMsg.BurnTokens) } - if tokenMsg.SetMetadata != nil { - return m.setMetadata(ctx, contractAddr, tokenMsg.SetMetadata) + if contractMsg.SetMetadata != nil { + return m.setMetadata(ctx, contractAddr, contractMsg.SetMetadata) } - if tokenMsg.ForceTransfer != nil { - return m.forceTransfer(ctx, contractAddr, tokenMsg.ForceTransfer) + if contractMsg.ForceTransfer != nil { + return m.forceTransfer(ctx, contractAddr, contractMsg.ForceTransfer) } } return m.wrapped.DispatchMsg(ctx, contractAddr, contractIBCPortID, msg) diff --git a/x/tokenfactory/bindings/query_plugin.go b/x/tokenfactory/bindings/query_plugin.go index 72d0073b3..3332bc8bb 100644 --- a/x/tokenfactory/bindings/query_plugin.go +++ b/x/tokenfactory/bindings/query_plugin.go @@ -9,7 +9,6 @@ import ( errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" bindingstypes "github.com/CosmosContracts/juno/v17/x/tokenfactory/bindings/types" ) @@ -21,15 +20,11 @@ func CustomQuerier(qp *QueryPlugin) func(ctx sdk.Context, request json.RawMessag if err := json.Unmarshal(request, &contractQuery); err != nil { return nil, errorsmod.Wrap(err, "osmosis query") } - if contractQuery.Token == nil { - return nil, errorsmod.Wrap(sdkerrors.ErrUnknownRequest, "nil token field") - } - tokenQuery := contractQuery.Token switch { - case tokenQuery.FullDenom != nil: - creator := tokenQuery.FullDenom.CreatorAddr - subdenom := tokenQuery.FullDenom.Subdenom + case contractQuery.FullDenom != nil: + creator := contractQuery.FullDenom.CreatorAddr + subdenom := contractQuery.FullDenom.Subdenom fullDenom, err := GetFullDenom(creator, subdenom) if err != nil { @@ -47,8 +42,8 @@ func CustomQuerier(qp *QueryPlugin) func(ctx sdk.Context, request json.RawMessag return bz, nil - case tokenQuery.Admin != nil: - res, err := qp.GetDenomAdmin(ctx, tokenQuery.Admin.Denom) + case contractQuery.Admin != nil: + res, err := qp.GetDenomAdmin(ctx, contractQuery.Admin.Denom) if err != nil { return nil, err } @@ -60,8 +55,8 @@ func CustomQuerier(qp *QueryPlugin) func(ctx sdk.Context, request json.RawMessag return bz, nil - case tokenQuery.Metadata != nil: - res, err := qp.GetMetadata(ctx, tokenQuery.Metadata.Denom) + case contractQuery.Metadata != nil: + res, err := qp.GetMetadata(ctx, contractQuery.Metadata.Denom) if err != nil { return nil, err } @@ -73,8 +68,8 @@ func CustomQuerier(qp *QueryPlugin) func(ctx sdk.Context, request json.RawMessag return bz, nil - case tokenQuery.DenomsByCreator != nil: - res, err := qp.GetDenomsByCreator(ctx, tokenQuery.DenomsByCreator.Creator) + case contractQuery.DenomsByCreator != nil: + res, err := qp.GetDenomsByCreator(ctx, contractQuery.DenomsByCreator.Creator) if err != nil { return nil, err } @@ -86,7 +81,7 @@ func CustomQuerier(qp *QueryPlugin) func(ctx sdk.Context, request json.RawMessag return bz, nil - case tokenQuery.Params != nil: + case contractQuery.Params != nil: res, err := qp.GetParams(ctx) if err != nil { return nil, err diff --git a/x/tokenfactory/bindings/testdata/README.md b/x/tokenfactory/bindings/testdata/README.md new file mode 100644 index 000000000..221c6518f --- /dev/null +++ b/x/tokenfactory/bindings/testdata/README.md @@ -0,0 +1,5 @@ +# token-reflect-contract + + + +Commit: 834bb36573fb21c74f8e78207308d9001df127a2 diff --git a/x/tokenfactory/bindings/testdata/download_releases.sh b/x/tokenfactory/bindings/testdata/download_releases.sh deleted file mode 100755 index 5413f3bf7..000000000 --- a/x/tokenfactory/bindings/testdata/download_releases.sh +++ /dev/null @@ -1,19 +0,0 @@ -#!/bin/bash -set -o errexit -o nounset -o pipefail -command -v shellcheck > /dev/null && shellcheck "$0" - -if [ $# -ne 1 ]; then - echo "Usage: ./download_releases.sh RELEASE_TAG" - exit 1 -fi - -tag="$1" - -# From CosmosContracts/token-bindings - -url="https://github.com/CosmWasm/token-bindings/releases/download/$tag/token_reflect.wasm" -echo "Downloading $url ..." -wget -O "token_reflect.wasm" "$url" - -rm -f version.txt -echo "$tag" >version.txt \ No newline at end of file diff --git a/x/tokenfactory/bindings/testdata/token_reflect.wasm b/x/tokenfactory/bindings/testdata/token_reflect.wasm index c27ec80a0..0526f1749 100755 Binary files a/x/tokenfactory/bindings/testdata/token_reflect.wasm and b/x/tokenfactory/bindings/testdata/token_reflect.wasm differ diff --git a/x/tokenfactory/bindings/testdata/version.txt b/x/tokenfactory/bindings/testdata/version.txt deleted file mode 100644 index f979adec6..000000000 --- a/x/tokenfactory/bindings/testdata/version.txt +++ /dev/null @@ -1 +0,0 @@ -v0.9.0 diff --git a/x/tokenfactory/bindings/types/msg.go b/x/tokenfactory/bindings/types/msg.go index 538d42651..81d9a68fd 100644 --- a/x/tokenfactory/bindings/types/msg.go +++ b/x/tokenfactory/bindings/types/msg.go @@ -3,10 +3,6 @@ package types import "cosmossdk.io/math" type TokenFactoryMsg struct { - Token *TokenMsg `json:"token,omitempty"` -} - -type TokenMsg struct { /// Contracts can create denoms, namespaced under the contract's address. /// A contract may create any number of independent sub-denoms. CreateDenom *CreateDenom `json:"create_denom,omitempty"` diff --git a/x/tokenfactory/bindings/types/query.go b/x/tokenfactory/bindings/types/query.go index f2dab4994..60f0ac3ed 100644 --- a/x/tokenfactory/bindings/types/query.go +++ b/x/tokenfactory/bindings/types/query.go @@ -1,11 +1,7 @@ package types -type TokenFactoryQuery struct { - Token *TokenQuery `json:"token,omitempty"` -} - // See https://github.com/CosmWasm/token-bindings/blob/main/packages/bindings/src/query.rs -type TokenQuery struct { +type TokenFactoryQuery struct { /// Given a subdenom minted by a contract via `OsmosisMsg::MintTokens`, /// returns the full denom as used by `BankMsg::Send`. FullDenom *FullDenom `json:"full_denom,omitempty"` diff --git a/x/tokenfactory/keeper/admins_test.go b/x/tokenfactory/keeper/admins_test.go index 5e2e54026..3757bb26a 100644 --- a/x/tokenfactory/keeper/admins_test.go +++ b/x/tokenfactory/keeper/admins_test.go @@ -131,6 +131,7 @@ func (suite *KeeperTestSuite) TestMintDenom() { }, } { suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + tc := tc _, err := suite.msgServer.Mint(sdk.WrapSDKContext(suite.Ctx), &tc.mintMsg) if tc.expectPass { suite.Require().NoError(err) @@ -207,6 +208,7 @@ func (suite *KeeperTestSuite) TestBurnDenom() { }, } { suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + tc := tc _, err := suite.msgServer.Burn(sdk.WrapSDKContext(suite.Ctx), &tc.burnMsg) if tc.expectPass { suite.Require().NoError(err) @@ -281,6 +283,7 @@ func (suite *KeeperTestSuite) TestForceTransferDenom() { }, } { suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + tc := tc _, err := suite.msgServer.ForceTransfer(sdk.WrapSDKContext(suite.Ctx), &tc.forceTransferMsg) if tc.expectPass { suite.Require().NoError(err) @@ -505,6 +508,7 @@ func (suite *KeeperTestSuite) TestSetDenomMetaData() { }, } { suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + tc := tc bankKeeper := suite.App.AppKeepers.BankKeeper res, err := suite.msgServer.SetDenomMetadata(sdk.WrapSDKContext(suite.Ctx), &tc.msgSetDenomMetadata) if tc.expectedPass { diff --git a/x/tokenfactory/keeper/msg_server_test.go b/x/tokenfactory/keeper/msg_server_test.go index 9cb93d8de..7e99a7773 100644 --- a/x/tokenfactory/keeper/msg_server_test.go +++ b/x/tokenfactory/keeper/msg_server_test.go @@ -238,6 +238,7 @@ func (suite *KeeperTestSuite) TestSetDenomMetaDataMsg() { }, } { suite.Run(fmt.Sprintf("Case %s", tc.desc), func() { + tc := tc ctx := suite.Ctx.WithEventManager(sdk.NewEventManager()) suite.Require().Equal(0, len(ctx.EventManager().Events())) // Test set denom metadata message diff --git a/x/tokenfactory/types/msgs.go b/x/tokenfactory/types/msgs.go index f6f06e536..0a1dd5c24 100644 --- a/x/tokenfactory/types/msgs.go +++ b/x/tokenfactory/types/msgs.go @@ -8,7 +8,6 @@ import ( banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" ) -// constants const ( TypeMsgCreateDenom = "create_denom" TypeMsgMint = "tf_mint"