├── .build.sh ├── .dockerignore ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── feature-request.md │ └── issue-template.md ├── PULL_REQUEST_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE │ ├── docs.md │ ├── others.md │ └── production.md ├── codecov.yml ├── stale.yml └── workflows │ ├── codeql-analysis.yml │ ├── docker-push.yml │ ├── lint.yml │ ├── md-link-checker.yml │ ├── nightly-tests.yml │ ├── release-sims.yml │ ├── release.yml │ ├── sim-label.yml │ ├── sims.yml │ ├── stale.yml │ └── test.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── .mergify.yml ├── ATTRIBUTION ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── RELEASES.md ├── RELEASE_PROCESS.md ├── SECURITY.md ├── STATE-COMPATIBILITY.md ├── UPGRADING.md ├── ante ├── ante.go ├── gov_vote_ante.go └── gov_vote_ante_test.go ├── api └── atomone │ ├── feemarket │ ├── module │ │ └── v1 │ │ │ └── module.pulsar.go │ └── v1 │ │ ├── genesis.pulsar.go │ │ ├── params.pulsar.go │ │ ├── query.pulsar.go │ │ ├── query_grpc.pb.go │ │ ├── tx.pulsar.go │ │ └── tx_grpc.pb.go │ ├── gov │ ├── module │ │ └── v1 │ │ │ └── module.pulsar.go │ ├── v1 │ │ ├── genesis.pulsar.go │ │ ├── gov.pulsar.go │ │ ├── query.pulsar.go │ │ ├── query_grpc.pb.go │ │ ├── tx.pulsar.go │ │ └── tx_grpc.pb.go │ └── v1beta1 │ │ ├── genesis.pulsar.go │ │ ├── gov.pulsar.go │ │ ├── query.pulsar.go │ │ ├── query_grpc.pb.go │ │ ├── tx.pulsar.go │ │ └── tx_grpc.pb.go │ └── photon │ └── v1 │ ├── genesis.pulsar.go │ ├── photon.pulsar.go │ ├── query.pulsar.go │ ├── query_grpc.pb.go │ ├── tx.pulsar.go │ └── tx_grpc.pb.go ├── app ├── app.go ├── app_helpers.go ├── app_test.go ├── const.go ├── encoding.go ├── export.go ├── genesis.go ├── genesis_account.go ├── genesis_account_fuzz_test.go ├── gov_handlers.go ├── helpers │ └── test_helpers.go ├── keepers │ ├── keepers.go │ └── keys.go ├── modules.go ├── params │ ├── amino.go │ ├── config.go │ ├── doc.go │ ├── encoding.go │ ├── proto.go │ └── weights.go ├── sim │ ├── sim_config.go │ ├── sim_state.go │ └── sim_utils.go ├── sim_bench_test.go ├── sim_test.go └── upgrades │ ├── types.go │ ├── v2 │ ├── constants.go │ └── upgrades.go │ └── v3 │ ├── constants.go │ └── upgrades.go ├── buf.work.yaml ├── client └── docs │ ├── config.json │ ├── statik │ └── statik.go │ └── swagger-ui │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── index.html │ ├── oauth2-redirect.html │ ├── swagger-ui-bundle.js │ ├── swagger-ui-bundle.js.map │ ├── swagger-ui-es-bundle-core.js │ ├── swagger-ui-es-bundle-core.js.map │ ├── swagger-ui-es-bundle.js │ ├── swagger-ui-es-bundle.js.map │ ├── swagger-ui-standalone-preset.js │ ├── swagger-ui-standalone-preset.js.map │ ├── swagger-ui.css │ ├── swagger-ui.css.map │ ├── swagger-ui.js │ ├── swagger-ui.js.map │ └── swagger.yaml ├── cmd └── atomoned │ ├── cmd │ ├── bech32_convert.go │ ├── config.go │ ├── genaccounts.go │ ├── root.go │ ├── root_test.go │ ├── testnet.go │ └── tx.go │ └── main.go ├── contrib ├── Dockerfile.test ├── denom.json ├── devdeps │ ├── go.mod │ ├── go.sum │ └── tools.go ├── generate_release_note │ └── main.go ├── get_node.sh ├── githooks │ ├── README.md │ ├── pre-commit │ └── precommit ├── localnet │ ├── proposal_legacy_param_change.json │ ├── proposal_text.json │ └── proposal_upgrade.json ├── scripts │ ├── local-atomone.sh │ └── test_localnet_liveness.sh └── single-node.sh ├── docs ├── AtomOne - Zellic Audit Report.pdf ├── README.md └── architecture │ ├── PROCESS.md │ ├── README.md │ ├── adr-001-late-quorum-extension.md │ ├── adr-002-photon-token.md │ ├── adr-003-governance-proposal-deposit-auto-throttler.md │ ├── adr-004-nakamoto-bonus.md │ ├── adr-template.md │ ├── demo │ ├── NakamotoBonus.ipynb │ └── README_NakamotoBonus.md │ └── img │ ├── adr-001-voting-period-extension-late-quorum-case1.png │ └── adr-001-voting-period-extension-late-quorum-case2.png ├── e2e.Dockerfile ├── go.mod ├── go.sum ├── mlc_config.json ├── pkg └── address │ ├── address.go │ └── address_test.go ├── post └── post.go ├── proto ├── atomone │ ├── feemarket │ │ ├── module │ │ │ └── v1 │ │ │ │ └── module.proto │ │ └── v1 │ │ │ ├── genesis.proto │ │ │ ├── params.proto │ │ │ ├── query.proto │ │ │ └── tx.proto │ ├── gov │ │ ├── module │ │ │ └── v1 │ │ │ │ └── module.proto │ │ ├── v1 │ │ │ ├── genesis.proto │ │ │ ├── gov.proto │ │ │ ├── query.proto │ │ │ └── tx.proto │ │ └── v1beta1 │ │ │ ├── genesis.proto │ │ │ ├── gov.proto │ │ │ ├── query.proto │ │ │ └── tx.proto │ └── photon │ │ └── v1 │ │ ├── genesis.proto │ │ ├── photon.proto │ │ ├── query.proto │ │ └── tx.proto ├── buf.gen.gogo.yaml ├── buf.gen.pulsar.yaml ├── buf.gen.swagger.yaml ├── buf.lock ├── buf.yaml └── scripts │ ├── protoc-swagger-gen.sh │ ├── protocgen-pulsar.sh │ └── protocgen.sh ├── sims.mk ├── tests └── e2e │ ├── address.go │ ├── chain_test.go │ ├── doc.go │ ├── docker │ └── hermes.Dockerfile │ ├── e2e_bank_test.go │ ├── e2e_distribution_test.go │ ├── e2e_encode_test.go │ ├── e2e_evidence_test.go │ ├── e2e_exec_test.go │ ├── e2e_feegrant_test.go │ ├── e2e_feemarket_test.go │ ├── e2e_gov_test.go │ ├── e2e_ibc_test.go │ ├── e2e_photon_test.go │ ├── e2e_rest_regression_test.go │ ├── e2e_setup_test.go │ ├── e2e_slashing_test.go │ ├── e2e_staking_test.go │ ├── e2e_test.go │ ├── e2e_vesting_test.go │ ├── genesis_test.go │ ├── http_util_test.go │ ├── io.go │ ├── keys.go │ ├── query_test.go │ ├── scripts │ └── hermes_bootstrap.sh │ ├── util_test.go │ └── validator_test.go ├── types └── errors │ └── errors.go └── x ├── feemarket ├── AIMD.md ├── CLIENTS.md ├── README.md ├── ante │ ├── expected_keepers.go │ ├── expected_keepers_mocks_test.go │ ├── fee.go │ └── fee_test.go ├── client │ └── cli │ │ └── query.go ├── fuzz │ ├── aimd_eip1559_test.go │ ├── eip1559_test.go │ └── tx_priority_test.go ├── keeper │ ├── abci.go │ ├── feemarket.go │ ├── feemarket_test.go │ ├── genesis.go │ ├── genesis_test.go │ ├── keeper.go │ ├── keeper_test.go │ ├── msg_server.go │ ├── msg_server_test.go │ ├── query_server.go │ └── query_server_test.go ├── module.go ├── module_simulation.go ├── post │ ├── expected_keepers.go │ ├── expected_keepers_mocks_test.go │ ├── post.go │ └── post_test.go ├── testutil │ ├── expected_keepers_mocks.go │ └── keeper.go └── types │ ├── codec.go │ ├── eip1559.go │ ├── eip1559_aimd.go │ ├── errors.go │ ├── expected_keepers.go │ ├── genesis.go │ ├── genesis.pb.go │ ├── genesis_test.go │ ├── keys.go │ ├── msgs.go │ ├── msgs_test.go │ ├── params.go │ ├── params.pb.go │ ├── params_test.go │ ├── query.pb.go │ ├── query.pb.gw.go │ ├── resolver.go │ ├── state.go │ ├── state_fuzz_test.go │ ├── state_test.go │ └── tx.pb.go ├── gov ├── README.md ├── abci.go ├── abci_internal_test.go ├── abci_test.go ├── autocli.go ├── client │ ├── cli │ │ ├── prompt.go │ │ ├── prompt_test.go │ │ ├── query.go │ │ ├── query_test.go │ │ ├── tx.go │ │ ├── tx_test.go │ │ ├── util.go │ │ └── util_test.go │ ├── proposal_handler.go │ ├── testutil │ │ └── helpers.go │ └── utils │ │ ├── query.go │ │ ├── query_test.go │ │ ├── unified_diff.go │ │ ├── unified_diff_test.go │ │ ├── utils.go │ │ └── utils_test.go ├── codec │ ├── cdc.go │ └── doc.go ├── common_test.go ├── exported │ └── exported.go ├── genesis.go ├── genesis_test.go ├── keeper │ ├── common_test.go │ ├── constitution.go │ ├── constitution_test.go │ ├── deposit.go │ ├── deposit_test.go │ ├── export_test.go │ ├── grpc_query.go │ ├── grpc_query_test.go │ ├── hooks_test.go │ ├── internal_test.go │ ├── invariants.go │ ├── keeper.go │ ├── keeper_test.go │ ├── migrations.go │ ├── min_deposit.go │ ├── min_deposit_test.go │ ├── min_initial_deposit.go │ ├── min_initial_deposit_test.go │ ├── msg_server.go │ ├── msg_server_test.go │ ├── params.go │ ├── proposal.go │ ├── proposal_test.go │ ├── tally.go │ ├── tally_test.go │ ├── vote.go │ └── vote_test.go ├── migrations │ ├── v3 │ │ ├── convert.go │ │ └── keys.go │ └── v5 │ │ ├── store.go │ │ └── store_test.go ├── module.go ├── simulation │ ├── decoder.go │ ├── decoder_test.go │ ├── genesis.go │ ├── genesis_test.go │ ├── operations.go │ ├── operations_test.go │ ├── proposals.go │ └── proposals_test.go ├── testutil │ ├── configurator.go │ ├── expected_keepers.go │ └── expected_keepers_mocks.go └── types │ ├── config.go │ ├── errors.go │ ├── events.go │ ├── expected_keepers.go │ ├── hooks.go │ ├── keys.go │ ├── keys_test.go │ ├── metadata.go │ ├── unified_diff.go │ ├── unified_diff_test.go │ ├── v1 │ ├── codec.go │ ├── content.go │ ├── deposit.go │ ├── genesis.go │ ├── genesis.pb.go │ ├── genesis_test.go │ ├── gov.pb.go │ ├── min_deposit.go │ ├── min_deposit_test.go │ ├── msgs.go │ ├── msgs_test.go │ ├── params.go │ ├── params_legacy.go │ ├── proposal.go │ ├── proposals_test.go │ ├── querier.go │ ├── query.pb.go │ ├── query.pb.gw.go │ ├── quorum_check.go │ ├── tally.go │ ├── tx.pb.go │ └── vote.go │ └── v1beta1 │ ├── codec.go │ ├── content.go │ ├── deposit.go │ ├── genesis.go │ ├── genesis.pb.go │ ├── gov.pb.go │ ├── msgs.go │ ├── msgs_test.go │ ├── params.go │ ├── proposal.go │ ├── proposals_test.go │ ├── querier.go │ ├── query.pb.go │ ├── query.pb.gw.go │ ├── router.go │ ├── tally.go │ ├── tx.pb.go │ └── vote.go └── photon ├── README.md ├── ante ├── ante.go ├── ante_test.go ├── expected_keepers.go └── expected_keepers_mocks_test.go ├── client └── cli │ ├── query.go │ └── tx.go ├── genesis.go ├── genesis_test.go ├── keeper ├── grpc_query.go ├── grpc_query_test.go ├── keeper.go ├── msg_server.go ├── msg_server_test.go ├── params.go ├── params_test.go └── resolver.go ├── module.go ├── module_simulation.go ├── simulation ├── decoder.go ├── genesis.go ├── operations.go └── proposals.go ├── testutil ├── expected_keepers_mocks.go └── keeper.go └── types ├── codec.go ├── const.go ├── errors.go ├── events.go ├── expected_keepers.go ├── genesis.go ├── genesis.pb.go ├── genesis_test.go ├── keys.go ├── msgs.go ├── msgs_test.go ├── params.go ├── photon.pb.go ├── query.pb.go ├── query.pb.gw.go ├── tx.pb.go └── types.go /.build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ue 4 | 5 | # Expect the following envvars to be set: 6 | # - APP 7 | # - VERSION 8 | # - COMMIT 9 | # - TARGET_OS 10 | # - LEDGER_ENABLED 11 | # - DEBUG 12 | 13 | # Source builder's functions library 14 | . /usr/local/share/tendermint/buildlib.sh 15 | 16 | # These variables are now available 17 | # - BASEDIR 18 | # - OUTDIR 19 | 20 | # Build for each os-architecture pair 21 | for platform in ${TARGET_PLATFORMS} ; do 22 | # This function sets GOOS, GOARCH, and OS_FILE_EXT environment variables 23 | # according to the build target platform. OS_FILE_EXT is empty in all 24 | # cases except when the target platform is 'windows'. 25 | setup_build_env_for_platform "${platform}" 26 | 27 | make clean 28 | echo Building for $(go env GOOS)/$(go env GOARCH) >&2 29 | GOROOT_FINAL="$(go env GOROOT)" \ 30 | make build \ 31 | LDFLAGS=-buildid=${VERSION} \ 32 | VERSION=${VERSION} \ 33 | COMMIT=${COMMIT} \ 34 | LEDGER_ENABLED=${LEDGER_ENABLED} 35 | mv ./build/${APP}${OS_FILE_EXT} ${OUTDIR}/${APP}-${VERSION}-$(go env GOOS)-$(go env GOARCH)${OS_FILE_EXT} 36 | 37 | # This function restore the build environment variables to their 38 | # original state. 39 | restore_build_env 40 | done 41 | 42 | # Generate and display build report. 43 | generate_build_report 44 | cat ${OUTDIR}/build_report 45 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .github 4 | .vscode -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomone-hub/atomone/f82dce0ebfcae9a297fdc5c79e2c234b86f23b8e/.gitattributes -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # CODEOWNERS: https://help.github.com/articles/about-codeowners/ 2 | 3 | # Primary repo maintainers 4 | * @giunatale @tbruyelle @jaekwon @giuliostramondo 5 | 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Create a report to help us squash bugs! 4 | labels: bug, needs-triage 5 | --- 6 | 7 | 12 | 13 | ## Summary of Bug 14 | 15 | 16 | 17 | ## Version 18 | 19 | 20 | 21 | ## Steps to Reproduce 22 | 23 | 24 | 25 | ____ 26 | 27 | #### For Admin Use 28 | 29 | - [ ] Not duplicate issue 30 | - [ ] Appropriate labels applied 31 | - [ ] Appropriate contributors tagged 32 | - [ ] Contributor assigned/self-assigned 33 | - [ ] Is a spike necessary to map out how the issue should be approached? 34 | 35 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Create a proposal to request a feature 4 | labels: enhancement, epic, needs-triage 5 | --- 6 | 7 | 13 | 14 | ## Summary 15 | 16 | 17 | 18 | ## Problem Definition 19 | 20 | 24 | 25 | ## Proposal 26 | 27 | 28 | 29 | ## Task list 30 | 31 | ```[tasklist] 32 | ### Must have 33 | - [ ] discuss proposal (if proposal rejected, close EPIC) 34 | - [ ] create ADR (if ADR rejected, close EPIC) 35 | - [ ] add sub-tasks needed to implement the proposed feature 36 | ``` 37 | 38 | ```[tasklist] 39 | ### Nice to have 40 | - [ ] add sub-tasks that are nice to have for the proposed feature 41 | ``` 42 | ____ 43 | 44 | #### For Admin Use 45 | 46 | - [ ] Not duplicate issue 47 | - [ ] Appropriate labels applied 48 | - [ ] Appropriate contributors tagged 49 | - [ ] Contributor assigned/self-assigned 50 | - [ ] Is a spike necessary to map out how the issue should be approached? 51 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/issue-template.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Issue Template 3 | about: Basic template for issues (used by the team) 4 | labels: needs-triage 5 | --- 6 | 7 | 12 | 13 | # Problem 14 | 15 | 16 | 17 | # Closing criteria 18 | 19 | 20 | 21 | 22 | # Problem details 23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Please go the the `Preview` tab and select the appropriate sub-template: 2 | 3 | * [Production code](?expand=1&template=production.md) - for types `fix`, `feat`, and `refactor`. 4 | * [Docs](?expand=1&template=docs.md) - for documentation changes. 5 | * [Others](?expand=1&template=others.md) - for changes that do not affect production code. -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/docs.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Closes: #XXXX 4 | 5 | 7 | 8 | 9 | --- 10 | 11 | ### Author Checklist 12 | 13 | *All items are required. Please add a note to the item if the item is not applicable and 14 | please add links to any relevant follow up issues.* 15 | 16 | I have... 17 | 18 | - [ ] included the correct `docs:` prefix in the PR title 19 | - [ ] targeted the correct branch (see [PR Targeting](https://github.com/atomone-hub/atomone/blob/main/CONTRIBUTING.md#pr-targeting)) 20 | - [ ] provided a link to the relevant issue or specification 21 | - [ ] reviewed "Files changed" and left comments if necessary 22 | - [ ] confirmed all CI checks have passed 23 | 24 | ### Reviewers Checklist 25 | 26 | *All items are required. Please add a note if the item is not applicable and please add 27 | your handle next to the items reviewed if you only reviewed selected items.* 28 | 29 | I have... 30 | 31 | - [ ] Confirmed the correct `docs:` prefix in the PR title 32 | - [ ] Confirmed all author checklist items have been addressed 33 | - [ ] Confirmed that this PR only changes documentation 34 | - [ ] Reviewed content for consistency 35 | - [ ] Reviewed content for thoroughness 36 | - [ ] Reviewed content for spelling and grammar 37 | - [ ] Tested instructions (if applicable) 38 | 39 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/others.md: -------------------------------------------------------------------------------- 1 | ## Description 2 | 3 | Closes: #XXXX 4 | 5 | 7 | 8 | --- 9 | 10 | ### Author Checklist 11 | 12 | *All items are required. Please add a note to the item if the item is not applicable and 13 | please add links to any relevant follow up issues.* 14 | 15 | I have... 16 | 17 | - [ ] Included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title 18 | - [ ] Targeted the correct branch (see [PR Targeting](https://github.com/atomone-hub/atomone/blob/main/CONTRIBUTING.md#pr-targeting)) 19 | - [ ] Provided a link to the relevant issue or specification 20 | - [ ] Reviewed "Files changed" and left comments if necessary 21 | - [ ] Confirmed all CI checks have passed 22 | 23 | ### Reviewers Checklist 24 | 25 | *All items are required. Please add a note if the item is not applicable and please add 26 | your handle next to the items reviewed if you only reviewed selected items.* 27 | 28 | I have... 29 | 30 | - [ ] Confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title 31 | - [ ] Confirmed all author checklist items have been addressed 32 | - [ ] Confirmed that this PR does not change production code 33 | 34 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE/production.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ## Description 6 | 7 | Closes: #XXXX 8 | 9 | 11 | 12 | 13 | 14 | --- 15 | 16 | ### Author Checklist 17 | 18 | *All items are required. Please add a note to the item if the item is not applicable and 19 | please add links to any relevant follow up issues.* 20 | 21 | I have... 22 | 23 | * [ ] Included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title 24 | * [ ] Added `!` to the type prefix if API, client, or state breaking change (i.e., requires minor or major version bump) 25 | * [ ] Targeted the correct branch (see [PR Targeting](https://github.com/atomone-hub/atomone/blob/main/CONTRIBUTING.md#pr-targeting)) 26 | * [ ] Provided a link to the relevant issue or specification 27 | * [ ] Followed the guidelines for [building SDK modules](https://github.com/atomone-hub/atomone/blob/main/docs/docs/building-modules) 28 | * [ ] Included the necessary unit and integration [tests](https://github.com/atomone-hub/atomone/blob/main/CONTRIBUTING.md#testing) 29 | * [ ] Added a changelog entry in `.changelog` (for details, see [contributing guidelines](../../CONTRIBUTING.md#changelog)) 30 | * [ ] Included comments for [documenting Go code](https://blog.golang.org/godoc) 31 | * [ ] Updated the relevant documentation or specification 32 | * [ ] Reviewed "Files changed" and left comments if necessary 33 | * [ ] Confirmed all CI checks have passed 34 | 35 | ### Reviewers Checklist 36 | 37 | *All items are required. Please add a note if the item is not applicable and please add 38 | your handle next to the items reviewed if you only reviewed selected items.* 39 | 40 | I have... 41 | 42 | * [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title 43 | * [ ] confirmed `!` in the type prefix if API or client breaking change 44 | * [ ] confirmed all author checklist items have been addressed 45 | * [ ] reviewed state machine logic 46 | * [ ] reviewed API design and naming 47 | * [ ] reviewed documentation is accurate 48 | * [ ] reviewed tests and test coverage 49 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | precision: 2 3 | round: down 4 | range: 70...100 5 | status: 6 | project: 7 | default: 8 | threshold: 1% # allow this much decrease on project 9 | app: 10 | target: 80% 11 | paths: # this must be a list type 12 | - "app/" 13 | changes: false 14 | 15 | comment: 16 | layout: "reach, diff, files" 17 | behavior: default # update if exists else create new 18 | require_changes: true 19 | 20 | ignore: 21 | - "*.pb.go" 22 | - "*.pb.gw.go" 23 | - "*.md" 24 | - "*.rst" 25 | - "cmd" 26 | - "client" 27 | - "contrib" 28 | - "docs" 29 | - "proto" 30 | - "tests/e2e" 31 | - "app/app_helpers.go" 32 | - "app/sim" 33 | - "app/upgrades" -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 10 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 8 | daysUntilClose: 4 9 | 10 | # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) 11 | onlyLabels: [] 12 | 13 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 14 | exemptLabels: 15 | - blocked 16 | - pinned 17 | - security 18 | 19 | # Set to true to ignore issues in a project (defaults to false) 20 | exemptProjects: true 21 | 22 | # Set to true to ignore issues in a milestone (defaults to false) 23 | exemptMilestones: true 24 | 25 | # Label to use when marking as stale 26 | staleLabel: stale 27 | 28 | # Comment to post when marking as stale. Set to `false` to disable 29 | markComment: > 30 | This issue has been automatically marked as stale because it has not had 31 | recent activity. It will be closed if no further activity occurs. Thank you 32 | for your contributions. 33 | # Limit the number of actions per hour, from 1-30. Default is 30 34 | limitPerRun: 30 35 | 36 | # Limit to only `issues` or `pulls` 37 | only: pulls 38 | 39 | # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': 40 | pulls: 41 | daysUntilStale: 30 42 | markComment: > 43 | This pull request has been automatically marked as stale because it has not had 44 | recent activity. It will be closed if no further activity occurs. Thank you 45 | for your contributions. 46 | -------------------------------------------------------------------------------- /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - "**.go" 7 | push: 8 | branches: 9 | - main 10 | - release/v* 11 | - feat/** 12 | paths: 13 | - "**.go" 14 | 15 | jobs: 16 | analyze: 17 | name: Analyze 18 | runs-on: ubuntu-latest 19 | permissions: 20 | actions: read 21 | contents: read 22 | security-events: write 23 | 24 | steps: 25 | - name: Checkout repository 26 | uses: actions/checkout@v4 27 | - uses: actions/setup-go@v5 28 | with: 29 | go-version-file: go.mod 30 | 31 | # Initializes the CodeQL tools for scanning. 32 | - name: Initialize CodeQL 33 | uses: github/codeql-action/init@v3 34 | with: 35 | languages: "go" 36 | # If you wish to specify custom queries, you can do so here or in a config file. 37 | # By default, queries listed here will override any specified in a config file. 38 | # Prefix the list here with "+" to use these queries and those in the config file. 39 | # queries: ./path/to/local/query, your-org/your-repo/queries@main 40 | queries: +security-and-quality,github/codeql/go/ql/src/experimental/InconsistentCode/DeferInLoop.ql@main,github/codeql/go/ql/src/experimental/Unsafe/WrongUsageOfUnsafe.ql@main,github/codeql/go/ql/src/experimental/CWE-369/DivideByZero.ql@main 41 | packs: +crypto-com/cosmos-sdk-codeql 42 | 43 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 44 | # If this step fails, then you should remove it and run the build manually (see below) 45 | - name: Autobuild 46 | uses: github/codeql-action/autobuild@v3 47 | 48 | # ℹ️ Command-line programs to run using the OS shell. 49 | # 📚 https://git.io/JvXDl 50 | 51 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 52 | # and modify them (or add more) to build your code if your project 53 | # uses a compiled language 54 | 55 | #- run: | 56 | # make bootstrap 57 | # make release 58 | 59 | - name: Perform CodeQL Analysis 60 | uses: github/codeql-action/analyze@v3 61 | -------------------------------------------------------------------------------- /.github/workflows/docker-push.yml: -------------------------------------------------------------------------------- 1 | # source: https://docs.github.com/en/enterprise-cloud@latest/actions/publishing-packages/publishing-docker-images 2 | name: Create and publish a Docker image 3 | 4 | on: 5 | push: 6 | branches: ['release'] 7 | 8 | env: 9 | REGISTRY: ghcr.io 10 | IMAGE_NAME: ${{ github.repository }} 11 | 12 | jobs: 13 | build-and-push-image: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: read 17 | packages: write 18 | 19 | steps: 20 | - name: Checkout repository 21 | uses: actions/checkout@v4 22 | 23 | - name: Log in to the Container registry 24 | uses: docker/login-action@v3.0.0 25 | with: 26 | registry: ${{ env.REGISTRY }} 27 | username: ${{ github.actor }} 28 | password: ${{ secrets.GITHUB_TOKEN }} 29 | 30 | - name: Extract metadata (tags, labels) for Docker 31 | id: meta 32 | uses: docker/metadata-action@v5.5.1 33 | with: 34 | images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} 35 | 36 | - name: Build and push Docker image 37 | uses: docker/build-push-action@v5.1.0 38 | with: 39 | context: . 40 | push: true 41 | tags: ${{ steps.meta.outputs.tags }} 42 | labels: ${{ steps.meta.outputs.labels }} 43 | 44 | - name: Build and push e2e docker image 45 | uses: docker/build-push-action@v5.1.0 46 | with: 47 | context: . 48 | file: Dockerfile.e2e 49 | push: true 50 | tags: ${{ steps.meta.outputs.tags }}-e2e 51 | labels: ${{ steps.meta.outputs.labels }} 52 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | on: 3 | push: 4 | branches: 5 | - main 6 | - release/** 7 | - feat/** 8 | pull_request: 9 | permissions: 10 | contents: read 11 | jobs: 12 | golangci: 13 | name: golangci-lint 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: actions/setup-go@v5 18 | with: 19 | go-version-file: go.mod 20 | - uses: technote-space/get-diff-action@v6.1.2 21 | id: git_diff 22 | with: 23 | PATTERNS: | 24 | **/*.go 25 | go.mod 26 | go.sum 27 | **/go.mod 28 | **/go.sum 29 | - name: run linting 30 | if: env.GIT_DIFF 31 | run: | 32 | make lint 33 | -------------------------------------------------------------------------------- /.github/workflows/md-link-checker.yml: -------------------------------------------------------------------------------- 1 | name: Check Markdown links 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: '* */24 * * *' 6 | jobs: 7 | markdown-link-check: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - uses: gaurav-nelson/github-action-markdown-link-check@1.0.15 12 | with: 13 | folder-path: "docs" -------------------------------------------------------------------------------- /.github/workflows/nightly-tests.yml: -------------------------------------------------------------------------------- 1 | name: "Nightly E2E run" 2 | on: 3 | workflow_call: 4 | workflow_dispatch: 5 | schedule: 6 | # run every day at 03:00 UTC 7 | - cron: "0 3 * * *" 8 | 9 | jobs: 10 | 11 | run-tests: 12 | uses: atomone-hub/atomone/.github/workflows/test.yml@main 13 | 14 | run-simulations: 15 | uses: atomone-hub/atomone/.github/workflows/sims.yml@main 16 | 17 | run-vulncheck: 18 | runs-on: ubuntu-latest 19 | timeout-minutes: 5 20 | steps: 21 | - uses: actions/checkout@v4 22 | - uses: actions/setup-go@v5 23 | with: 24 | go-version-file: go.mod 25 | - name: run-vulncheck 26 | id: vulncheck 27 | run: make vulncheck 28 | 29 | warn-if-failure: 30 | if: failure() 31 | needs: [ run-tests, run-vulncheck, run-simulations] 32 | runs-on: ubuntu-latest 33 | steps: 34 | - name: Notify Slack on failure 35 | uses: slackapi/slack-github-action@v1.25.0 36 | env: 37 | SLACK_WEBHOOK_URL: ${{ secrets.NIGHTLY_E2E_SLACK_WEBHOOK_URL }} 38 | SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK 39 | BRANCH: ${{ github.ref_name }} 40 | RUN_URL: "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" 41 | COMMITS_URL: "${{ github.server_url }}/${{ github.repository }}/commits/${{ github.ref_name }}" 42 | with: 43 | payload: | 44 | { 45 | "blocks": [ 46 | { 47 | "type": "header", 48 | "text": { 49 | "type": "plain_text", 50 | "text": "❗Nightly tests failed", 51 | "emoji": true 52 | } 53 | }, 54 | { 55 | "type": "section", 56 | "text": { 57 | "type": "mrkdwn", 58 | "text": "See the <${{ env.RUN_URL }}|run details>" 59 | } 60 | } 61 | ] 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: "Release" 2 | 3 | on: 4 | # can be used to re-release an existing tag 5 | workflow_dispatch: 6 | 7 | push: 8 | tags: 9 | - "v[0-9]+\\.[0-9]+\\.[0-9]+" 10 | - "v[0-9]+\\.[0-9]+\\.[0-9]+-rc[0-9]+" 11 | 12 | jobs: 13 | release: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | - run: git fetch --force --tags 21 | 22 | - uses: actions/setup-go@v5 23 | with: 24 | go-version-file: go.mod 25 | 26 | - name: Set Env 27 | run: echo "TM_VERSION=$(make print_tm_version)" >> $GITHUB_ENV 28 | 29 | - name: Release 30 | uses: goreleaser/goreleaser-action@v5 31 | with: 32 | version: v1.25.1 33 | args: release --clean 34 | env: 35 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 36 | -------------------------------------------------------------------------------- /.github/workflows/sim-label.yml: -------------------------------------------------------------------------------- 1 | name: SimLabeled 2 | on: 3 | pull_request: 4 | types: [ labeled ] 5 | 6 | jobs: 7 | cleanup-runs: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: rokroskar/workflow-run-cleanup-action@master 11 | env: 12 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 13 | # if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/main'" 14 | 15 | newbuild: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/setup-go@v5 19 | - name: Install runsim 20 | run: go install github.com/cosmos/tools/cmd/runsim@v1.0.0 21 | - uses: actions/cache@v4 22 | with: 23 | path: ~/go/bin 24 | key: ${{ runner.os }}-go-runsim-binary 25 | 26 | test-sim-nondeterminism-labeled: 27 | if: ${{ github.event.label.name == 'sim' }} 28 | runs-on: ubuntu-latest 29 | needs: newbuild 30 | steps: 31 | - uses: actions/checkout@v4 32 | - uses: actions/setup-go@v5 33 | with: 34 | go-version-file: go.mod 35 | - uses: actions/cache@v4 36 | with: 37 | path: ~/go/bin 38 | key: ${{ runner.os }}-go-runsim-binary 39 | - name: test nondeterminism 40 | run: | 41 | make test-sim-nondeterminism 42 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale pull requests" 2 | on: 3 | schedule: 4 | - cron: "0 0 * * 1-5" 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v9.0.0 11 | with: 12 | repo-token: ${{ secrets.GITHUB_TOKEN }} 13 | stale-pr-message: "This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions." 14 | days-before-stale: -1 15 | days-before-close: -1 16 | days-before-pr-stale: 45 17 | days-before-pr-close: 6 18 | exempt-pr-labels: "pinned, security, proposal, blocked, ADR" 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | .DS_Store 3 | *.swp 4 | *.swo 5 | *.swl 6 | *.swm 7 | *.swn 8 | .vscode 9 | .idea 10 | 11 | # Build 12 | artifacts 13 | vendor 14 | build 15 | tools/bin/* 16 | examples/build/* 17 | docs/_build 18 | docs/node_modules 19 | docs/tutorial 20 | dist 21 | tools-stamp 22 | docs/node_modules 23 | 24 | # Data - ideally these don't exist 25 | baseapp/data/* 26 | client/lcd/keys/* 27 | mytestnet 28 | 29 | # Testing 30 | coverage.txt 31 | profile.out 32 | 33 | # Vagrant 34 | .vagrant/ 35 | *.box 36 | *.log 37 | vagrant 38 | 39 | # IDE 40 | .idea/ 41 | *.iml 42 | 43 | # Graphviz 44 | dependency-graph.png 45 | 46 | # Latex 47 | *.aux 48 | *.out 49 | *.synctex.gz 50 | contract_tests/* 51 | 52 | go.work.sum 53 | 54 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | tests: false 3 | # timeout for analysis, e.g. 30s, 5m, default is 1m 4 | timeout: 5m 5 | skip-dirs: 6 | - tests/ 7 | - client/docs/statik 8 | 9 | linters: 10 | disable-all: true 11 | enable: 12 | - dogsled 13 | - errcheck 14 | - exportloopref 15 | - gci 16 | - goconst 17 | - gocritic 18 | - gofumpt 19 | - gosec 20 | - gosimple 21 | - govet 22 | - ineffassign 23 | - misspell 24 | - nakedret 25 | - nolintlint 26 | - staticcheck 27 | - stylecheck 28 | - typecheck 29 | - thelper 30 | - unconvert 31 | - unparam 32 | - unused 33 | 34 | issues: 35 | exclude-rules: 36 | - text: 'Use of weak random number generator' 37 | linters: 38 | - gosec 39 | - text: 'comment on exported var' 40 | linters: 41 | - golint 42 | - text: "don't use an underscore in package name" 43 | linters: 44 | - golint 45 | - text: 'ST1003:' 46 | linters: 47 | - stylecheck 48 | # FIXME: Disabled until golangci-lint updates stylecheck with this fix: 49 | # https://github.com/dominikh/go-tools/issues/389 50 | - text: 'ST1016:' 51 | linters: 52 | - stylecheck 53 | - path: 'migrations' 54 | text: 'SA1019:' 55 | linters: 56 | - staticcheck 57 | 58 | max-issues-per-linter: 10000 59 | max-same-issues: 10000 60 | 61 | linters-settings: 62 | gci: 63 | custom-order: true 64 | sections: 65 | - standard # Standard section: captures all standard packages. 66 | - default # Default section: contains all imports that could not be matched to another section type. 67 | - blank # blank imports 68 | - dot # dot imports 69 | - prefix(github.com/cometbft/cometbft) # comet 70 | - prefix(github.com/cosmos) # cosmos org 71 | - prefix(cosmossdk.io) # new modules 72 | - prefix(github.com/cosmos/cosmos-sdk) # cosmos sdk 73 | - prefix(github.com/atomone-hub/atomone) # AtomOne 74 | dogsled: 75 | max-blank-identifiers: 3 76 | maligned: 77 | # print struct with more effective memory layout or not, false by default 78 | suggest-new: true 79 | nolintlint: 80 | allow-unused: false 81 | allow-leading-space: true 82 | require-explanation: false 83 | require-specific: false 84 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | --- 2 | project_name: atomone 3 | 4 | builds: 5 | - main: ./cmd/atomoned 6 | id: "atomoned" 7 | binary: atomoned 8 | mod_timestamp: "{{ .CommitTimestamp }}" 9 | flags: 10 | - -tags=netgo 11 | - -trimpath 12 | env: 13 | - CGO_ENABLED=0 14 | ldflags: 15 | # .Env.TM_VERSION is provided in the workflow runner environment -> see .github/workflows/release.yml 16 | - -X github.com/cosmos/cosmos-sdk/version.Name=atomone -X github.com/cosmos/cosmos-sdk/version.AppName=atomoned -X github.com/cosmos/cosmos-sdk/version.Version=v{{ .Version }} -X github.com/cosmos/cosmos-sdk/version.Commit={{ .Commit }} -X github.com/cosmos/cosmos-sdk/version.BuildTags=netgo -X github.com/cometbft/cometbft/version.TMCoreSemVer={{ .Env.TM_VERSION }} -w -s 17 | goos: 18 | - darwin 19 | - linux 20 | - windows 21 | goarch: 22 | - amd64 23 | - arm64 24 | 25 | archives: 26 | # disables archiving; to enable use commented lines below 27 | - format: binary 28 | name_template: "{{ .Binary }}-v{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" 29 | 30 | # - format: tar.gz 31 | # wrap_in_directory: "true" 32 | # format_overrides: 33 | # - goos: windows 34 | # format: zip 35 | # name_template: "{{ .Binary }}-v{{ .Version }}-{{ .Os }}-{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}" 36 | # files: 37 | # - LICENSE 38 | # - README.md 39 | # rlcp: true 40 | 41 | release: 42 | prerelease: true 43 | name_template: "v{{.Version}}" 44 | 45 | checksum: 46 | name_template: SHA256SUMS-v{{.Version}}.txt 47 | algorithm: sha256 48 | 49 | snapshot: 50 | name_template: "{{ .Version }}-{{ .ShortCommit }}" 51 | 52 | changelog: 53 | disable: true 54 | 55 | git: 56 | # What should be used to sort tags when gathering the current and previous 57 | # tags if there are more than one tag in the same commit. 58 | # 59 | # source: https://goreleaser.com/customization/git/ 60 | tag_sort: -version:refname 61 | prerelease_suffix: "-rc" 62 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | defaults: 2 | actions: 3 | backport: 4 | assignees: 5 | - "{{ author }}" 6 | 7 | queue_rules: 8 | - name: default 9 | conditions: 10 | - "#approved-reviews-by>1" 11 | 12 | pull_request_rules: 13 | - name: Automatic merge on approval to the main branch 14 | conditions: 15 | - "#approved-reviews-by>=1" 16 | - base=main 17 | - label=A:automerge 18 | actions: 19 | queue: 20 | name: default 21 | merge: 22 | method: squash 23 | commit_message_template: | 24 | {{ title }} (#{{ number }}) 25 | {{ body }} 26 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG IMG_TAG=latest 2 | 3 | # Compile the atomoned binary 4 | FROM golang:1.22-alpine AS atomoned-builder 5 | WORKDIR /src/app/ 6 | COPY go.mod go.sum* ./ 7 | RUN go mod download 8 | COPY . . 9 | ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev python3 10 | RUN apk add --no-cache $PACKAGES 11 | RUN CGO_ENABLED=0 make install 12 | 13 | # Add to a distroless container 14 | FROM cgr.dev/chainguard/static:$IMG_TAG 15 | ARG IMG_TAG 16 | COPY --from=atomoned-builder /go/bin/atomoned /usr/local/bin/ 17 | EXPOSE 26656 26657 1317 9090 18 | USER 0 19 | 20 | ENTRYPOINT ["atomoned", "start"] 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AtomOne 2 | 3 | AtomOne is built using the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk) as a fork of the 4 | [Cosmos Hub](https://github.com/cosmos/gaia) at version [v15.2.0](https://github.com/cosmos/gaia/releases/tag/v15.2.0) (common commit hash 7281c9b). 5 | 6 | The following modifications have been made to the Cosmos Hub software to create AtomOne: 7 | 8 | 1. Removed x/globalfee module and revert to older and simpler fee decorator 9 | 2. Removed Packet Forwarding Middleware 10 | 3. Removed Interchain Security module 11 | 4. Reverted to standard Cosmos SDK v0.47.10 without the Liquid Staking Module (LSM) 12 | 5. Changed Bech32 prefixes to `atone` (see `cmd/atomoned/cmd/config.go`) 13 | 6. Removed ability for validators to vote on proposals with delegations, they can only use their own stake 14 | 15 | ## Reproducible builds 16 | 17 | An effort has been made to make it possible to build the exact same binary 18 | locally as the Github Release section. To do this: 19 | - Checkout to the expected released version 20 | - Run `make build` (which will output the binary to the `build` directory) or 21 | `make install`. Note that a fixed version of the `go` binary is required, 22 | follow the command instructions to install this specific version if needed. 23 | - The resulted binary should have the same sha256 hash than the one from the 24 | Github Release section. 25 | 26 | ## Ledger support 27 | 28 | Run `make build/install LEDGER_ENABLED=true` to have ledger support in 29 | `atomoned` binary. 30 | 31 | Note that this will disable reproducible builds, as it introduces OS 32 | dependencies. 33 | 34 | ## Genesis file 35 | 36 | The proposed genesis files for atomone can be found in the [genesis repo](https://github.com/atomone-hub/genesis). 37 | 38 | ## Public RPC and fullnode endpoints 39 | 40 | The public RPC and fullnode endpoints directory can be found in the [atom.one](https://atom.one) 41 | website. 42 | 43 | ## Acknowledgements 44 | 45 | Portions of this codebase are copied or adapted from [cosmos/gaia@v15](https://github.com/cosmos/gaia/tree/v15.0.0), [cosmos/cosmos-sdk@v47.10](https://github.com/cosmos/cosmos-sdk/tree/v0.47.10) and 46 | [skip-mev/feemarket@v1.1.1](https://github.com/skip-mev/feemarket/tree/v1.1.1). 47 | 48 | Their original licenses are both included in [ATTRIBUTION](ATTRIBUTION) 49 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | All in Bits strives to contribute toward the security of our ecosystem through 4 | internal security practices, and by working with external security researchers 5 | from the community. 6 | 7 | ## Reporting a Vulnerability 8 | 9 | If you've identified a vulnerability, please report it through one of the 10 | following venues: 11 | * Submit an advisory through GitHub: https://github.com/atomone-hub/atomone/security/advisories/new 12 | * Email security [at-symbol] tendermint [dot] com. If you are concerned about 13 | confidentiality e.g. because of a high-severity issue, you may email us for 14 | PGP or Signal contact details. 15 | * We provide bug bounty rewards through our program at 16 | [HackenProof](https://hackenproof.com/all-in-bits). You must report via 17 | HackenProof in order to be eligible for rewards. 18 | 19 | We will respond within 3 business days to all received reports. 20 | 21 | Thank you for helping to keep our ecosystem safe! 22 | 23 | ## Security Audits 24 | 25 | - March 2025: The security firm Zellic conducted a source code audit of the AtomOne daemon and 26 | published a [report](docs/AtomOne%20-%20Zellic%20Audit%20Report.pdf) on March 11, 2025. Zellic has 27 | independently published this report 28 | [here](https://github.com/Zellic/publications/blob/master/AtomOne%20-%20Zellic%20Audit%20Report.pdf) 29 | with a SHA-256 hash of 60625f148263829921f7b8cc4a065290b197ddb869ba821f7dc4cfe4a4f96ff1. 30 | The audit scope was the whole codebase with a specific focus on the new `x/photon` module and the [dynamic deposit proposal](https://github.com/atomone-hub/atomone/pull/69) from the `x/gov/` module. 31 | -------------------------------------------------------------------------------- /app/app_helpers.go: -------------------------------------------------------------------------------- 1 | package atomone 2 | 3 | import ( 4 | ibckeeper "github.com/cosmos/ibc-go/v7/modules/core/keeper" 5 | ibctestingtypes "github.com/cosmos/ibc-go/v7/testing/types" 6 | 7 | capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper" 8 | ) 9 | 10 | // GetStakingKeeper implements the TestingApp interface. Needed for ICS. 11 | func (app *AtomOneApp) GetStakingKeeper() ibctestingtypes.StakingKeeper { //nolint:nolintlint 12 | return app.StakingKeeper 13 | } 14 | 15 | // GetIBCKeeper implements the TestingApp interface. 16 | func (app *AtomOneApp) GetIBCKeeper() *ibckeeper.Keeper { //nolint:nolintlint 17 | return app.IBCKeeper 18 | } 19 | 20 | // GetScopedIBCKeeper implements the TestingApp interface. 21 | func (app *AtomOneApp) GetScopedIBCKeeper() capabilitykeeper.ScopedKeeper { //nolint:nolintlint 22 | return app.ScopedIBCKeeper 23 | } 24 | -------------------------------------------------------------------------------- /app/app_test.go: -------------------------------------------------------------------------------- 1 | package atomone_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | db "github.com/cometbft/cometbft-db" 9 | "github.com/cometbft/cometbft/libs/log" 10 | 11 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 12 | 13 | atomone "github.com/atomone-hub/atomone/app" 14 | atomonehelpers "github.com/atomone-hub/atomone/app/helpers" 15 | govtypes "github.com/atomone-hub/atomone/x/gov/types" 16 | ) 17 | 18 | type EmptyAppOptions struct{} 19 | 20 | func (ao EmptyAppOptions) Get(_ string) interface{} { 21 | return nil 22 | } 23 | 24 | func TestAtomOneApp_BlockedModuleAccountAddrs(t *testing.T) { 25 | encConfig := atomone.RegisterEncodingConfig() 26 | app := atomone.NewAtomOneApp( 27 | log.NewNopLogger(), 28 | db.NewMemDB(), 29 | nil, 30 | true, 31 | map[int64]bool{}, 32 | atomone.DefaultNodeHome, 33 | encConfig, 34 | EmptyAppOptions{}, 35 | ) 36 | 37 | moduleAccountAddresses := app.ModuleAccountAddrs() 38 | blockedAddrs := app.BlockedModuleAccountAddrs(moduleAccountAddresses) 39 | 40 | require.NotContains(t, blockedAddrs, authtypes.NewModuleAddress(govtypes.ModuleName).String()) 41 | } 42 | 43 | func TestAtomOneApp_Export(t *testing.T) { 44 | app := atomonehelpers.Setup(t) 45 | _, err := app.ExportAppStateAndValidators(true, []string{}, []string{}) 46 | require.NoError(t, err, "ExportAppStateAndValidators should not have an error") 47 | } 48 | -------------------------------------------------------------------------------- /app/const.go: -------------------------------------------------------------------------------- 1 | package atomone 2 | 3 | const ( 4 | appName = "AtomOneApp" 5 | ) 6 | -------------------------------------------------------------------------------- /app/encoding.go: -------------------------------------------------------------------------------- 1 | package atomone 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/std" 5 | 6 | "github.com/atomone-hub/atomone/app/params" 7 | ) 8 | 9 | func RegisterEncodingConfig() params.EncodingConfig { 10 | encConfig := params.MakeEncodingConfig() 11 | 12 | std.RegisterLegacyAminoCodec(encConfig.Amino) 13 | std.RegisterInterfaces(encConfig.InterfaceRegistry) 14 | ModuleBasics.RegisterLegacyAminoCodec(encConfig.Amino) 15 | ModuleBasics.RegisterInterfaces(encConfig.InterfaceRegistry) 16 | 17 | return encConfig 18 | } 19 | -------------------------------------------------------------------------------- /app/genesis.go: -------------------------------------------------------------------------------- 1 | package atomone 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/atomone-hub/atomone/app/params" 7 | ) 8 | 9 | // The genesis state of the blockchain is represented here as a map of raw json 10 | // messages key'd by a identifier string. 11 | // The identifier is used to determine which module genesis information belongs 12 | // to so it may be appropriately routed during init chain. 13 | // Within this application default genesis information is retrieved from 14 | // the ModuleBasicManager which populates json from each BasicModule 15 | // object provided to it during init. 16 | type GenesisState map[string]json.RawMessage 17 | 18 | // NewDefaultGenesisState generates the default state for the application. 19 | func NewDefaultGenesisState(encConfig params.EncodingConfig) GenesisState { 20 | return ModuleBasics.DefaultGenesis(encConfig.Marshaler) 21 | } 22 | -------------------------------------------------------------------------------- /app/genesis_account.go: -------------------------------------------------------------------------------- 1 | package atomone 2 | 3 | import ( 4 | "errors" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 8 | ) 9 | 10 | var _ authtypes.GenesisAccount = (*SimGenesisAccount)(nil) 11 | 12 | // SimGenesisAccount defines a type that implements the GenesisAccount interface 13 | // to be used for simulation accounts in the genesis state. 14 | type SimGenesisAccount struct { 15 | *authtypes.BaseAccount 16 | 17 | // vesting account fields 18 | OriginalVesting sdk.Coins `json:"original_vesting" yaml:"original_vesting"` // total vesting coins upon initialization 19 | DelegatedFree sdk.Coins `json:"delegated_free" yaml:"delegated_free"` // delegated vested coins at time of delegation 20 | DelegatedVesting sdk.Coins `json:"delegated_vesting" yaml:"delegated_vesting"` // delegated vesting coins at time of delegation 21 | StartTime int64 `json:"start_time" yaml:"start_time"` // vesting start time (UNIX Epoch time) 22 | EndTime int64 `json:"end_time" yaml:"end_time"` // vesting end time (UNIX Epoch time) 23 | 24 | // module account fields 25 | ModuleName string `json:"module_name" yaml:"module_name"` // name of the module account 26 | ModulePermissions []string `json:"module_permissions" yaml:"module_permissions"` // permissions of module account 27 | } 28 | 29 | // Validate checks for errors on the vesting and module account parameters 30 | func (sga SimGenesisAccount) Validate() error { 31 | if sga.OriginalVesting.IsAnyNil() { 32 | return errors.New("OriginalVesting amount must not be nil") 33 | } 34 | 35 | if !sga.OriginalVesting.IsZero() { 36 | if sga.StartTime >= sga.EndTime { 37 | return errors.New("vesting start-time cannot be before end-time") 38 | } 39 | } 40 | 41 | if sga.BaseAccount == nil { 42 | return errors.New("BaseAccount must not be nil") 43 | } 44 | 45 | if sga.ModuleName != "" { 46 | ma := authtypes.ModuleAccount{ 47 | BaseAccount: sga.BaseAccount, Name: sga.ModuleName, Permissions: sga.ModulePermissions, 48 | } 49 | 50 | if err := ma.Validate(); err != nil { 51 | return err 52 | } 53 | } 54 | 55 | return sga.BaseAccount.Validate() 56 | } 57 | -------------------------------------------------------------------------------- /app/genesis_account_fuzz_test.go: -------------------------------------------------------------------------------- 1 | package atomone 2 | 3 | import ( 4 | "runtime/debug" 5 | "testing" 6 | 7 | "github.com/google/gofuzz" 8 | ) 9 | 10 | func TestFuzzGenesisAccountValidate(t *testing.T) { 11 | if testing.Short() { 12 | t.Skip("running in -short mode") 13 | } 14 | 15 | t.Parallel() 16 | 17 | acct := new(SimGenesisAccount) 18 | i := 0 19 | defer func() { 20 | r := recover() 21 | if r == nil { 22 | return 23 | } 24 | 25 | // Otherwise report on the configuration and iteration. 26 | t.Fatalf("Failed SimGenesisAccount on iteration #%d: %#v\n\n%s\n\n%s", i, acct, r, debug.Stack()) 27 | }() 28 | 29 | f := fuzz.New() 30 | for i = 0; i < 1e5; i++ { 31 | acct = new(SimGenesisAccount) 32 | f.Fuzz(acct) 33 | acct.Validate() //nolint:errcheck 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/params/amino.go: -------------------------------------------------------------------------------- 1 | //go:build test_amino 2 | // +build test_amino 3 | 4 | package params 5 | 6 | import ( 7 | "github.com/cosmos/cosmos-sdk/codec" 8 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types" 9 | "github.com/cosmos/cosmos-sdk/x/auth/migrations/legacytx" 10 | ) 11 | 12 | func MakeTestEncodingConfig() EncodingConfig { 13 | cdc := codec.NewLegacyAmino() 14 | interfaceRegistry := cdctypes.NewInterfaceRegistry() 15 | codec := codec.NewProtoCodec(interfaceRegistry) 16 | 17 | return EncodingConfig{ 18 | InterfaceRegistry: interfaceRegistry, 19 | Marshaler: codec, 20 | TxConfig: legacytx.StdTxConfig{Cdc: cdc}, 21 | Amino: cdc, 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/params/config.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | const ( 4 | BondDenom = "uatone" 5 | 6 | Bech32PrefixAccAddr = "atone" 7 | ) 8 | 9 | var ( 10 | // Bech32PrefixAccPub defines the Bech32 prefix of an account's public key. 11 | Bech32PrefixAccPub = Bech32PrefixAccAddr + "pub" 12 | // Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address. 13 | Bech32PrefixValAddr = Bech32PrefixAccAddr + "valoper" 14 | // Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key. 15 | Bech32PrefixValPub = Bech32PrefixAccAddr + "valoperpub" 16 | // Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address. 17 | Bech32PrefixConsAddr = Bech32PrefixAccAddr + "valcons" 18 | // Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key. 19 | Bech32PrefixConsPub = Bech32PrefixAccAddr + "valconspub" 20 | ) 21 | -------------------------------------------------------------------------------- /app/params/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package params defines the simulation parameters in the atomone. 3 | 4 | It contains the default weights used for each transaction used on the module's 5 | simulation. These weights define the chance for a transaction to be simulated at 6 | any given operation. 7 | 8 | You can replace the default values for the weights by providing a params.json 9 | file with the weights defined for each of the transaction operations: 10 | 11 | { 12 | "op_weight_msg_send": 60, 13 | "op_weight_msg_delegate": 100, 14 | } 15 | 16 | In the example above, the `MsgSend` has 60% chance to be simulated, while the 17 | `MsgDelegate` will always be simulated. 18 | */ 19 | package params 20 | -------------------------------------------------------------------------------- /app/params/encoding.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/client" 5 | "github.com/cosmos/cosmos-sdk/codec" 6 | "github.com/cosmos/cosmos-sdk/codec/types" 7 | ) 8 | 9 | // EncodingConfig specifies the concrete encoding types to use for a given app. 10 | // This is provided for compatibility between protobuf and amino implementations. 11 | type EncodingConfig struct { 12 | InterfaceRegistry types.InterfaceRegistry 13 | Marshaler codec.Codec 14 | TxConfig client.TxConfig 15 | Amino *codec.LegacyAmino 16 | } 17 | -------------------------------------------------------------------------------- /app/params/proto.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | codectypes "github.com/cosmos/cosmos-sdk/codec/types" 6 | "github.com/cosmos/cosmos-sdk/x/auth/tx" 7 | ) 8 | 9 | // MakeEncodingConfig creates an EncodingConfig for an amino based test configuration. 10 | func MakeEncodingConfig() EncodingConfig { 11 | amino := codec.NewLegacyAmino() 12 | interfaceRegistry := codectypes.NewInterfaceRegistry() 13 | cdc := codec.NewProtoCodec(interfaceRegistry) 14 | txCfg := tx.NewTxConfig(cdc, tx.DefaultSignModes) 15 | return EncodingConfig{ 16 | InterfaceRegistry: interfaceRegistry, 17 | Marshaler: cdc, 18 | TxConfig: txCfg, 19 | Amino: amino, 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/params/weights.go: -------------------------------------------------------------------------------- 1 | package params 2 | 3 | // Default simulation operation weights for messages and gov proposals 4 | const ( 5 | DefaultWeightMsgSend int = 100 6 | DefaultWeightMsgMultiSend int = 10 7 | DefaultWeightMsgSetWithdrawAddress int = 50 8 | DefaultWeightMsgWithdrawDelegationReward int = 50 9 | DefaultWeightMsgWithdrawValidatorCommission int = 50 10 | DefaultWeightMsgFundCommunityPool int = 50 11 | DefaultWeightMsgDeposit int = 100 12 | DefaultWeightMsgVote int = 67 13 | DefaultWeightMsgUnjail int = 100 14 | DefaultWeightMsgCreateValidator int = 100 15 | DefaultWeightMsgEditValidator int = 5 16 | DefaultWeightMsgDelegate int = 100 17 | DefaultWeightMsgUndelegate int = 100 18 | DefaultWeightMsgBeginRedelegate int = 100 19 | DefaultWeightMsgCancelUnbondingDelegation int = 100 20 | 21 | DefaultWeightCommunitySpendProposal int = 5 22 | DefaultWeightTextProposal int = 5 23 | DefaultWeightParamChangeProposal int = 5 24 | ) 25 | -------------------------------------------------------------------------------- /app/upgrades/types.go: -------------------------------------------------------------------------------- 1 | package upgrades 2 | 3 | import ( 4 | store "github.com/cosmos/cosmos-sdk/store/types" 5 | sdk "github.com/cosmos/cosmos-sdk/types" 6 | "github.com/cosmos/cosmos-sdk/types/module" 7 | upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" 8 | 9 | "github.com/atomone-hub/atomone/app/keepers" 10 | ) 11 | 12 | // Upgrade defines a struct containing necessary fields that a SoftwareUpgradeProposal 13 | // must have written, in order for the state migration to go smoothly. 14 | // An upgrade must implement this struct, and then set it in the app.go. 15 | // The app.go will then define the handler. 16 | type Upgrade struct { 17 | // Upgrade version name, for the upgrade handler, e.g. `v7` 18 | UpgradeName string 19 | 20 | // CreateUpgradeHandler defines the function that creates an upgrade handler 21 | CreateUpgradeHandler func(*module.Manager, module.Configurator, *keepers.AppKeepers) upgradetypes.UpgradeHandler 22 | 23 | // Store upgrades, should be used for any new modules introduced, new modules deleted, or store names renamed. 24 | StoreUpgrades store.StoreUpgrades 25 | } 26 | 27 | // Fork defines a struct containing the requisite fields for a non-software upgrade proposal 28 | // Hard Fork at a given height to implement. 29 | // There is one time code that can be added for the start of the Fork, in `BeginForkLogic`. 30 | // Any other change in the code should be height-gated, if the goal is to have old and new binaries 31 | // to be compatible prior to the upgrade height. 32 | type Fork struct { 33 | // Upgrade version name, for the upgrade handler, e.g. `v7` 34 | UpgradeName string 35 | // height the upgrade occurs at 36 | UpgradeHeight int64 37 | 38 | // Function that runs some custom state transition code at the beginning of a fork. 39 | BeginForkLogic func(ctx sdk.Context, keepers *keepers.AppKeepers) 40 | } 41 | -------------------------------------------------------------------------------- /app/upgrades/v2/constants.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | store "github.com/cosmos/cosmos-sdk/store/types" 5 | crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" 6 | 7 | "github.com/atomone-hub/atomone/app/upgrades" 8 | photontypes "github.com/atomone-hub/atomone/x/photon/types" 9 | ) 10 | 11 | const ( 12 | UpgradeName = "v2" 13 | ) 14 | 15 | var Upgrade = upgrades.Upgrade{ 16 | UpgradeName: UpgradeName, 17 | CreateUpgradeHandler: CreateUpgradeHandler, 18 | StoreUpgrades: store.StoreUpgrades{ 19 | Added: []string{ 20 | // new module added in v2 21 | photontypes.ModuleName, 22 | }, 23 | Deleted: []string{ 24 | crisistypes.ModuleName, 25 | }, 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /app/upgrades/v3/constants.go: -------------------------------------------------------------------------------- 1 | package v3 2 | 3 | import ( 4 | store "github.com/cosmos/cosmos-sdk/store/types" 5 | 6 | "github.com/atomone-hub/atomone/app/upgrades" 7 | feemarkettypes "github.com/atomone-hub/atomone/x/feemarket/types" 8 | ) 9 | 10 | const ( 11 | UpgradeName = "v3" 12 | ) 13 | 14 | var Upgrade = upgrades.Upgrade{ 15 | UpgradeName: UpgradeName, 16 | CreateUpgradeHandler: CreateUpgradeHandler, 17 | StoreUpgrades: store.StoreUpgrades{ 18 | Added: []string{ 19 | // new module added in v3 20 | feemarkettypes.ModuleName, 21 | }, 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /app/upgrades/v3/upgrades.go: -------------------------------------------------------------------------------- 1 | package v3 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | "github.com/cosmos/cosmos-sdk/types/module" 6 | upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types" 7 | 8 | "github.com/atomone-hub/atomone/app/keepers" 9 | ) 10 | 11 | // CreateUpgradeHandler returns a upgrade handler for AtomOne v3 12 | func CreateUpgradeHandler( 13 | mm *module.Manager, 14 | configurator module.Configurator, 15 | keepers *keepers.AppKeepers, 16 | ) upgradetypes.UpgradeHandler { 17 | return func(ctx sdk.Context, plan upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { 18 | ctx.Logger().Info("Starting module migrations...") 19 | // RunMigrations will detect the add of the feemarket module, will initiate 20 | // its genesis and will fill the versionMap with its consensus version. 21 | vm, err := mm.RunMigrations(ctx, configurator, vm) 22 | if err != nil { 23 | return vm, err 24 | } 25 | ctx.Logger().Info("Upgrade complete") 26 | return vm, nil 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /buf.work.yaml: -------------------------------------------------------------------------------- 1 | # This workspace file points to the roots found in your 2 | # previous "buf.yaml" configuration. 3 | version: v1 4 | directories: 5 | - proto 6 | -------------------------------------------------------------------------------- /client/docs/swagger-ui/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomone-hub/atomone/f82dce0ebfcae9a297fdc5c79e2c234b86f23b8e/client/docs/swagger-ui/favicon-16x16.png -------------------------------------------------------------------------------- /client/docs/swagger-ui/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomone-hub/atomone/f82dce0ebfcae9a297fdc5c79e2c234b86f23b8e/client/docs/swagger-ui/favicon-32x32.png -------------------------------------------------------------------------------- /client/docs/swagger-ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Swagger UI 7 | 8 | 9 | 10 | 31 | 32 | 33 | 34 |
35 | 36 | 37 | 38 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /cmd/atomoned/cmd/bech32_convert.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | appparams "github.com/atomone-hub/atomone/app/params" 9 | addressutil "github.com/atomone-hub/atomone/pkg/address" 10 | ) 11 | 12 | var flagBech32Prefix = "prefix" 13 | 14 | // AddBech32ConvertCommand returns bech32-convert cobra Command. 15 | func AddBech32ConvertCommand() *cobra.Command { 16 | cmd := &cobra.Command{ 17 | Use: "bech32-convert [address]", 18 | Short: "Convert any bech32 string to the cosmos prefix", 19 | Long: `Convert any bech32 string to the cosmos prefix 20 | 21 | Example: 22 | atomoned debug bech32-convert akash1a6zlyvpnksx8wr6wz8wemur2xe8zyh0ytz6d88 23 | 24 | atomoned debug bech32-convert stride1673f0t8p893rqyqe420mgwwz92ac4qv6synvx2 --prefix osmo 25 | `, 26 | Args: cobra.ExactArgs(1), 27 | RunE: func(cmd *cobra.Command, args []string) error { 28 | bech32prefix, err := cmd.Flags().GetString(flagBech32Prefix) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | address := args[0] 34 | convertedAddress, err := addressutil.ConvertBech32Prefix(address, bech32prefix) 35 | if err != nil { 36 | return fmt.Errorf("convertation failed: %s", err) 37 | } 38 | 39 | cmd.Println(convertedAddress) 40 | 41 | return nil 42 | }, 43 | } 44 | 45 | cmd.Flags().StringP(flagBech32Prefix, "p", appparams.Bech32PrefixAccAddr, "Bech32 Prefix to encode to") 46 | 47 | return cmd 48 | } 49 | 50 | // addDebugCommands injects custom debug commands into another command as children. 51 | func addDebugCommands(cmd *cobra.Command) *cobra.Command { 52 | cmd.AddCommand(AddBech32ConvertCommand()) 53 | return cmd 54 | } 55 | -------------------------------------------------------------------------------- /cmd/atomoned/cmd/config.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | 6 | appparams "github.com/atomone-hub/atomone/app/params" 7 | ) 8 | 9 | func InitSDKConfig() { 10 | cfg := sdk.GetConfig() 11 | cfg.SetBech32PrefixForAccount(appparams.Bech32PrefixAccAddr, appparams.Bech32PrefixAccPub) 12 | cfg.SetBech32PrefixForValidator(appparams.Bech32PrefixValAddr, appparams.Bech32PrefixValPub) 13 | cfg.SetBech32PrefixForConsensusNode(appparams.Bech32PrefixConsAddr, appparams.Bech32PrefixConsPub) 14 | cfg.Seal() 15 | } 16 | -------------------------------------------------------------------------------- /cmd/atomoned/cmd/root_test.go: -------------------------------------------------------------------------------- 1 | package cmd_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" 9 | 10 | app "github.com/atomone-hub/atomone/app" 11 | "github.com/atomone-hub/atomone/cmd/atomoned/cmd" 12 | ) 13 | 14 | func TestRootCmdConfig(t *testing.T) { 15 | rootCmd, _ := cmd.NewRootCmd() 16 | rootCmd.SetArgs([]string{ 17 | "config", // Test the config cmd 18 | "keyring-backend", // key 19 | "test", // value 20 | }) 21 | 22 | require.NoError(t, svrcmd.Execute(rootCmd, "", app.DefaultNodeHome)) 23 | } 24 | -------------------------------------------------------------------------------- /cmd/atomoned/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "os" 5 | 6 | "github.com/cosmos/cosmos-sdk/server" 7 | svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" 8 | 9 | app "github.com/atomone-hub/atomone/app" 10 | "github.com/atomone-hub/atomone/cmd/atomoned/cmd" 11 | ) 12 | 13 | func main() { 14 | rootCmd, _ := cmd.NewRootCmd() 15 | 16 | if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil { 17 | switch e := err.(type) { 18 | case server.ErrorCode: 19 | os.Exit(e.Code) 20 | 21 | default: 22 | os.Exit(1) 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contrib/Dockerfile.test: -------------------------------------------------------------------------------- 1 | # Simple usage with a mounted data directory: 2 | # > docker build -t atomone . 3 | # > docker run -it -p 46657:46657 -p 46656:46656 -v ~/.atomone:/root/.atomone atomone atomoned init 4 | # > docker run -it -p 46657:46657 -p 46656:46656 -v ~/.atomone:/root/.atomone atomone atomoned start 5 | FROM golang:1.22-alpine AS build-env 6 | 7 | # Set up dependencies 8 | ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev python3 9 | 10 | # Set working directory for the build 11 | WORKDIR /go/src/github.com/atomone-hub/atomone 12 | 13 | # Add source files 14 | COPY . . 15 | 16 | # Install minimum necessary dependencies, build Cosmos SDK, remove packages 17 | RUN apk add --no-cache $PACKAGES && \ 18 | make install 19 | 20 | # Final image 21 | FROM alpine:edge 22 | 23 | # Install ca-certificates 24 | RUN apk add --update ca-certificates 25 | WORKDIR /root 26 | 27 | # Copy over binaries from the build-env 28 | COPY --from=build-env /go/bin/atomoned /usr/bin/atomoned 29 | 30 | COPY ./contrib/single-node.sh . 31 | 32 | EXPOSE 26657 33 | 34 | ENTRYPOINT [ "./single-node.sh" ] 35 | # NOTE: to run this image, docker run -d -p 26657:26657 ./single-node.sh {{chain_id}} {{genesis_account}} 36 | -------------------------------------------------------------------------------- /contrib/denom.json: -------------------------------------------------------------------------------- 1 | 2 | 3 | [{ 4 | "base": "uatone", 5 | "denom_units": [ 6 | { 7 | "aliases": [ 8 | "microatone" 9 | ], 10 | "denom": "uatone", 11 | "exponent": 0 12 | }, 13 | { 14 | "aliases": [ 15 | "milliatone" 16 | ], 17 | "denom": "matone", 18 | "exponent": 3 19 | }, 20 | { 21 | "aliases": [], 22 | "denom": "atone", 23 | "exponent": 6 24 | } 25 | ], 26 | "description": "The native staking token of AtomOne.", 27 | "display": "ATONE", 28 | "name": "ATONE", 29 | "symbol": "ATONE" 30 | }] 31 | -------------------------------------------------------------------------------- /contrib/devdeps/tools.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | 3 | package devdeps 4 | 5 | import ( 6 | // formatting 7 | _ "mvdan.cc/gofumpt" 8 | 9 | // linter 10 | _ "github.com/golangci/golangci-lint/cmd/golangci-lint" 11 | 12 | // mocks 13 | _ "github.com/golang/mock/mockgen" 14 | 15 | // for releases 16 | _ "github.com/goreleaser/goreleaser" 17 | 18 | _ "github.com/rakyll/statik" 19 | _ "golang.org/x/vuln/cmd/govulncheck" 20 | ) 21 | -------------------------------------------------------------------------------- /contrib/generate_release_note/main.go: -------------------------------------------------------------------------------- 1 | //go:build exclude 2 | // +build exclude 3 | 4 | package main 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "os" 10 | "strings" 11 | ) 12 | 13 | func main() { 14 | args := os.Args 15 | if len(args) != 4 { 16 | fmt.Println("please add os.Args release version, build_report path and CHANGELOG.md path, example: go run main.go v7.0.0 ../../artifacts/build_report ../../CHANGELOG.md") 17 | } 18 | 19 | buildReportPath := args[2] 20 | changelogPath := args[3] 21 | 22 | buildReport, err := os.ReadFile(buildReportPath) 23 | if err != nil { 24 | fmt.Printf("file error: %s\n", err) 25 | } 26 | 27 | changelog, err := FindChangelog(changelogPath, args[1]) 28 | if err != nil { 29 | fmt.Printf("cannot find changelog: %s\n", err) 30 | } 31 | 32 | note := strings.Builder{} 33 | note.WriteString(fmt.Sprintf("# AtomOne %s Release Notes\n", args[1])) 34 | note.WriteString(changelog) 35 | note.WriteString("```\n") 36 | note.Write(buildReport) 37 | note.WriteString("```\n") 38 | 39 | f, err := os.Create("./releasenote") 40 | if err != nil { 41 | fmt.Printf("cannot create a release note: %s\n", err) 42 | } 43 | defer f.Close() 44 | 45 | _, err = f.WriteString(note.String()) 46 | if err != nil { 47 | fmt.Printf("cannot write to releasenote: %s\n", err) 48 | } 49 | } 50 | 51 | func FindChangelog(file, version string) (string, error) { 52 | data, err := os.ReadFile(file) 53 | if err != nil { 54 | return "", errors.New("read changelog file failed") 55 | } 56 | 57 | changelogs := string(data) 58 | i := strings.Index(changelogs, "["+version) 59 | if i == -1 { 60 | // -1 means not found 61 | return "", fmt.Errorf("cannot find version %s", version) 62 | } 63 | j := strings.Index(changelogs[i:], "##") 64 | if j == -1 { 65 | // -1 means not found 66 | return "", fmt.Errorf("cannot find the end of %s's changelog", version) 67 | } 68 | 69 | return changelogs[i : i+j], nil 70 | } 71 | -------------------------------------------------------------------------------- /contrib/get_node.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | VERSION=v11.15.0 4 | NODE_FULL=node-${VERSION}-linux-x64 5 | 6 | mkdir -p ~/.local/bin 7 | mkdir -p ~/.local/node 8 | wget http://nodejs.org/dist/${VERSION}/${NODE_FULL}.tar.gz -O ~/.local/node/${NODE_FULL}.tar.gz 9 | tar -xzf ~/.local/node/${NODE_FULL}.tar.gz -C ~/.local/node/ 10 | ln -s ~/.local/node/${NODE_FULL}/bin/node ~/.local/bin/node 11 | ln -s ~/.local/node/${NODE_FULL}/bin/npm ~/.local/bin/npm 12 | export PATH=~/.local/bin:$PATH 13 | npm i -g dredd@11.0.1 14 | ln -s ~/.local/node/${NODE_FULL}/bin/dredd ~/.local/bin/dredd 15 | -------------------------------------------------------------------------------- /contrib/githooks/README.md: -------------------------------------------------------------------------------- 1 | # Git hooks 2 | 3 | Installation: 4 | 5 | ``` 6 | git config core.hooksPath contrib/githooks 7 | ``` 8 | 9 | ## pre-commit 10 | 11 | The hook automatically runs `gofmt`, `goimports`, and `misspell` 12 | to correctly format the `.go` files included in the commit, provided 13 | that all the aforementioned commands are installed and available 14 | in the user's search `$PATH` environment variable: 15 | 16 | ``` 17 | go install golang.org/x/tools/cmd/goimports 18 | go install github.com/golangci/misspell/cmd/misspell@master 19 | ``` 20 | 21 | It also runs `go mod tidy` and `golangci-lint` if available. 22 | -------------------------------------------------------------------------------- /contrib/githooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | CMDS='git go gofmt goimports misspell' 6 | STAGED_GO_FILES=$(git diff --cached --name-only -- '*.go') 7 | 8 | f_echo_stderr() { 9 | echo $@ >&2 10 | } 11 | 12 | f_exit_success() { 13 | [ x"$@" != "x" ] && f_echo_stderr $@ || exit 0 14 | } 15 | trap f_exit_success EXIT 16 | 17 | f_check_cmds() { 18 | for cmd in ${CMDS}; do 19 | which ${cmd} &>/dev/null || f_exit_success "couldn't find ${cmd}, skipping" 20 | done 21 | } 22 | 23 | f_check_cmds 24 | 25 | if [[ $STAGED_GO_FILES != "" ]]; then 26 | f_echo_stderr "[pre-commit] fmt'ing staged files..." 27 | for file in $STAGED_GO_FILES; do 28 | if [[ $file =~ vendor/ ]] || [[ $file =~ client/lcd/statik/ ]] || [[ $file =~ tests/mocks/ ]] || [[ $file =~ \.pb\.go ]]; then 29 | continue 30 | fi 31 | 32 | gofmt -w -s $file 33 | misspell -w $file 34 | goimports -w -local github.com/atomone-hub/atomone $file 35 | git add $file 36 | 37 | done 38 | fi 39 | 40 | # Run go mod tidy 41 | go mod tidy && git add go.mod go.sum 42 | -------------------------------------------------------------------------------- /contrib/githooks/precommit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | CMDS='git go gofmt goimports misspell' 6 | STAGED_GO_FILES=$(git diff --cached --name-only -- '*.go') 7 | 8 | f_echo_stderr() { 9 | echo $@ >&2 10 | } 11 | 12 | f_exit_success() { 13 | [ x"$@" != "x" ] && f_echo_stderr $@ || exit 0 14 | } 15 | trap f_exit_success EXIT 16 | 17 | f_check_cmds() { 18 | for cmd in ${CMDS}; do 19 | which ${cmd} &>/dev/null || f_exit_success "couldn't find ${cmd}, skipping" 20 | done 21 | } 22 | 23 | f_check_cmds 24 | 25 | if [[ $STAGED_GO_FILES != "" ]]; then 26 | f_echo_stderr "[pre-commit] fmt'ing staged files..." 27 | for file in $STAGED_GO_FILES; do 28 | if [[ $file =~ vendor/ ]] || [[ $file =~ client/lcd/statik/ ]] || [[ $file =~ tests/mocks/ ]] || [[ $file =~ \.pb\.go ]]; then 29 | continue 30 | fi 31 | 32 | gofmt -w -s $file 33 | misspell -w $file 34 | goimports -w -local github.com/atomone-hub/atomone $file 35 | git add $file 36 | 37 | done 38 | fi 39 | 40 | # Run go mod tidy 41 | go mod tidy && git add go.mod go.sum 42 | -------------------------------------------------------------------------------- /contrib/localnet/proposal_legacy_param_change.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "@type": "/atomone.gov.v1.MsgExecLegacyContent", 5 | "content": { 6 | "@type": "/cosmos.params.v1beta1.ParameterChangeProposal", 7 | "title": "IBC Transfers Enablement Proposal", 8 | "description": "This proposal would enable interchain functionality, allowing AtomOne to connect and interact seamlessly with the wider Cosmos ecosystem.", 9 | "changes": [ 10 | { 11 | "subspace": "transfer", 12 | "key": "SendEnabled", 13 | "value": "true" 14 | }, 15 | { 16 | "subspace": "transfer", 17 | "key": "ReceiveEnabled", 18 | "value": "true" 19 | } 20 | ] 21 | }, 22 | "authority": "atone10d07y265gmmuvt4z0w9aw880jnsr700j5z0zqt" 23 | } 24 | ], 25 | "metadata": "ipfs://CID", 26 | "deposit": "200000000uatone", 27 | "title": "IBC Enablement", 28 | "summary": "summary" 29 | } 30 | -------------------------------------------------------------------------------- /contrib/localnet/proposal_text.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": "ipfs://CID", 3 | "deposit": "512000000uatone", 4 | "title": "text proposal", 5 | "summary": "my summary" 6 | } 7 | -------------------------------------------------------------------------------- /contrib/localnet/proposal_upgrade.json: -------------------------------------------------------------------------------- 1 | { 2 | "messages": [ 3 | { 4 | "@type": "/cosmos.upgrade.v1beta1.MsgSoftwareUpgrade", 5 | "authority": "atone10d07y265gmmuvt4z0w9aw880jnsr700j5z0zqt", 6 | "plan": { 7 | "name": "v2", 8 | "time": "0001-01-01T00:00:00Z", 9 | "height": "80", 10 | "info": "", 11 | "upgraded_client_state": null 12 | } 13 | } 14 | ], 15 | "metadata": "ipfs://CID", 16 | "deposit": "512000000uatone", 17 | "title": "v2", 18 | "summary": "v2" 19 | } 20 | -------------------------------------------------------------------------------- /contrib/scripts/local-atomone.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eux 3 | 4 | # User balance of stake tokens 5 | USER_COINS="100000000000stake" 6 | # Amount of stake tokens staked 7 | STAKE="100000000stake" 8 | # Node IP address 9 | NODE_IP="127.0.0.1" 10 | 11 | # Home directory 12 | HOME_DIR="/Users/msalopek" 13 | 14 | # Validator moniker 15 | MONIKER="coordinator" 16 | 17 | # Validator directory 18 | PROV_NODE_DIR=${HOME_DIR}/provider-${MONIKER} 19 | 20 | # Coordinator key 21 | PROV_KEY=${MONIKER}-key 22 | 23 | 24 | # Clean start 25 | pkill -f atomoned &> /dev/null || true 26 | rm -rf ${PROV_NODE_DIR} 27 | 28 | # Build file and node directory structure 29 | atomoned init $MONIKER --chain-id provider --home ${PROV_NODE_DIR} 30 | jq ".app_state.gov.voting_params.voting_period = \"20s\" | .app_state.staking.params.unbonding_time = \"86400s\"" \ 31 | ${PROV_NODE_DIR}/config/genesis.json > \ 32 | ${PROV_NODE_DIR}/edited_genesis.json && mv ${PROV_NODE_DIR}/edited_genesis.json ${PROV_NODE_DIR}/config/genesis.json 33 | 34 | sleep 1 35 | 36 | # Create account keypair 37 | atomoned keys add $PROV_KEY --home ${PROV_NODE_DIR} --keyring-backend test --output json > ${PROV_NODE_DIR}/${PROV_KEY}.json 2>&1 38 | sleep 1 39 | 40 | # Add stake to user 41 | PROV_ACCOUNT_ADDR=$(jq -r '.address' ${PROV_NODE_DIR}/${PROV_KEY}.json) 42 | atomoned add-genesis-account $PROV_ACCOUNT_ADDR $USER_COINS --home ${PROV_NODE_DIR} --keyring-backend test 43 | sleep 1 44 | 45 | 46 | # Stake 1/1000 user's coins 47 | atomoned gentx $PROV_KEY $STAKE --chain-id provider --home ${PROV_NODE_DIR} --keyring-backend test --moniker $MONIKER 48 | sleep 1 49 | 50 | atomoned collect-gentxs --home ${PROV_NODE_DIR} --gentx-dir ${PROV_NODE_DIR}/config/gentx/ 51 | sleep 1 52 | 53 | sed -i -r "/node =/ s/= .*/= \"tcp:\/\/${NODE_IP}:26658\"/" ${PROV_NODE_DIR}/config/client.toml 54 | sed -i -r 's/timeout_commit = "5s"/timeout_commit = "3s"/g' ${PROV_NODE_DIR}/config/config.toml 55 | sed -i -r 's/timeout_propose = "3s"/timeout_propose = "1s"/g' ${PROV_NODE_DIR}/config/config.toml 56 | 57 | 58 | # Start atomone 59 | atomoned start \ 60 | --home ${PROV_NODE_DIR} \ 61 | --rpc.laddr tcp://${NODE_IP}:26658 \ 62 | --grpc.address ${NODE_IP}:9091 \ 63 | --address tcp://${NODE_IP}:26655 \ 64 | --p2p.laddr tcp://${NODE_IP}:26656 \ 65 | --grpc-web.enable=false &> ${PROV_NODE_DIR}/logs 66 | -------------------------------------------------------------------------------- /contrib/scripts/test_localnet_liveness.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CNT=0 4 | ITER=$1 5 | SLEEP=$2 6 | NUMBLOCKS=$3 7 | NODEADDR=$4 8 | 9 | if [ -z "$1" ]; then 10 | echo "Invalid argument: missing number of iterations" 11 | echo "sh test_localnet_liveness.sh " 12 | exit 1 13 | fi 14 | 15 | if [ -z "$2" ]; then 16 | echo "Invalid argument: missing sleep duration" 17 | echo "sh test_localnet_liveness.sh " 18 | exit 1 19 | fi 20 | 21 | if [ -z "$3" ]; then 22 | echo "Invalid argument: missing number of blocks" 23 | echo "sh test_localnet_liveness.sh " 24 | exit 1 25 | fi 26 | 27 | if [ -z "$4" ]; then 28 | echo "Invalid argument: missing node address" 29 | echo "sh test_localnet_liveness.sh " 30 | exit 1 31 | fi 32 | 33 | echo "running 'sh test_localnet_liveness.sh iterations=$ITER sleep=$SLEEP num-blocks=$NUMBLOCKS node-address=$NODEADDR'" 34 | 35 | while [ ${CNT} -lt $ITER ]; do 36 | curr_block=$(curl -s $NODEADDR:26657/status | jq -r '.result.sync_info.latest_block_height') 37 | 38 | tail liveness.out 39 | 40 | if [ ! -z ${curr_block} ]; then 41 | echo "Current block: ${curr_block}" 42 | fi 43 | 44 | if [ ! -z ${curr_block} ] && [ ${curr_block} -gt ${NUMBLOCKS} ]; then 45 | echo "Success: number of blocks reached" 46 | exit 0 47 | fi 48 | 49 | sleep $SLEEP 50 | done 51 | 52 | echo "Failed: timeout reached" 53 | exit 1 54 | -------------------------------------------------------------------------------- /contrib/single-node.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o errexit -o nounset 4 | 5 | CHAINID=$1 6 | GENACCT=$2 7 | 8 | if [ -z "$1" ]; then 9 | echo "Need to input chain id..." 10 | exit 1 11 | fi 12 | 13 | if [ -z "$2" ]; then 14 | echo "Need to input genesis account address..." 15 | exit 1 16 | fi 17 | 18 | # Build genesis file incl account for passed address 19 | coins="10000000000stake,100000000000samoleans" 20 | atomoned init --chain-id $CHAINID $CHAINID 21 | atomoned keys add validator --keyring-backend="test" 22 | atomoned add-genesis-account $(atomoned keys show validator -a --keyring-backend="test") $coins 23 | atomoned add-genesis-account $GENACCT $coins 24 | atomoned gentx validator 5000000000stake --keyring-backend="test" --chain-id $CHAINID 25 | atomoned collect-gentxs 26 | 27 | # Set proper defaults and change ports 28 | echo "Setting rpc listen address" 29 | sed -i '' 's#"tcp://127.0.0.1:26657"#"tcp://0.0.0.0:26657"#g' ~/.atomone/config/config.toml 30 | echo 2 31 | sed -i '' 's/timeout_commit = "5s"/timeout_commit = "1s"/g' ~/.atomone/config/config.toml 32 | sed -i '' 's/timeout_propose = "3s"/timeout_propose = "1s"/g' ~/.atomone/config/config.toml 33 | sed -i '' 's/index_all_keys = false/index_all_keys = true/g' ~/.atomone/config/config.toml 34 | 35 | # Start the atomone 36 | atomoned start --pruning=nothing 37 | -------------------------------------------------------------------------------- /docs/AtomOne - Zellic Audit Report.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomone-hub/atomone/f82dce0ebfcae9a297fdc5c79e2c234b86f23b8e/docs/AtomOne - Zellic Audit Report.pdf -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # AtomOne Documentation 8 | 9 | Welcome to the documentation of the **AtomOne application: `atomone`**. 10 | 11 | ## Testing Chain Upgrade 12 | 13 | Chain upgrade is an important procedure that should be tested carefully. This 14 | section aims to provide a guide for testing chain upgrades in AtomOne using a 15 | localnet. 16 | 17 | 1. Git checkout the version of AtomOne you want to upgrade from. 18 | 2. Update `contrib/localnet/upgrade_proposal.json` with the correct plan name, 19 | which means the exact `UpgradeName` used to qualify the upgrade in the 20 | next version. For instance for the v2 upgrade, the plan name is `v2` (see 21 | the `app/upgrade` folder). 22 | 3. Run `make localnet-start` to start a new localnet. 23 | 4. Run `make localnet-submit-upgrade-proposal` to submit the upgrade proposal 24 | and give it enough yes votes for passing the tally. 25 | 5. Wait for 5 minutes (the voting period) and run `atomoned --home ~/.atomone-localnet q gov proposals` 26 | to check that the proposal has passed. 27 | 6. Wait for the block height that was registered in the upgrade proposal. Once 28 | reached the localnet should stop producing blocks, and return an error like: 29 | ``` 30 | ERR UPGRADE "v2" NEEDED (...) 31 | ERR CONSENSUS FAILURE!!! 32 | ``` 33 | This means it is time to upgrade the binary. 34 | 7. Stop the `make localnet-start` 35 | 8. Git checkout the version of AtomOne you want to upgrade to. 36 | 9. Run `make localnet-restart` (/!\ not `localnet-start` which would delete all 37 | the chain data). Block production should restart. 38 | 10. Check that the upgrade procedure has been executed properly. 39 | 11. Restart the node to ensure it continues producing blocks after the upgrade. 40 | -------------------------------------------------------------------------------- /docs/architecture/adr-template.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # ADR {ADR-NUMBER}: {TITLE} 6 | 7 | ## Changelog 8 | 9 | - {date}: {changelog} 10 | 11 | ## Status 12 | 13 | {DRAFT | PROPOSED} Not Implemented 14 | 15 | > Please have a look at the [PROCESS](./PROCESS.md#adr-status) page. 16 | > Use DRAFT if the ADR is in a draft stage (draft PR) or PROPOSED if it's in review. 17 | 18 | ## Abstract 19 | 20 | > "If you can't explain it simply, you don't understand it well enough." Provide 21 | > a simplified and layman-accessible explanation of the ADR. 22 | > A short (~200 word) description of the issue being addressed. 23 | 24 | ## Context 25 | 26 | > This section contains all the context one needs to understand the current state, and why there is a problem. 27 | > It should be as succinct as possible and introduce the high level idea behind the solution. 28 | > The language in this section is value-neutral. It is simply describing facts. 29 | 30 | ## Decision 31 | 32 | > This section explains all of the details of the proposed solution, including implementation details. 33 | It should also describe affects / corollary items that may need to be changed as a part of this. 34 | If the proposed change will be large, please also indicate a way to do the change to maximize ease of review. 35 | (e.g. the optimal split of things to do between separate PR's) 36 | 37 | ## Consequences 38 | 39 | > This section describes the consequences, after applying the decision. 40 | > All consequences should be summarized here, not just the "positive" ones. 41 | 42 | ### Positive 43 | 44 | > {positive consequences} 45 | 46 | ### Negative 47 | 48 | > {negative consequences} 49 | 50 | ### Neutral 51 | 52 | > {neutral consequences} 53 | 54 | ## References 55 | 56 | > Are there any relevant PR comments, issues that led up to this, or articles referrenced for why we made the given design choice? If so link them here! 57 | 58 | * {reference link} 59 | -------------------------------------------------------------------------------- /docs/architecture/demo/README_NakamotoBonus.md: -------------------------------------------------------------------------------- 1 | # Nakamoto Bonus Demo 2 | 3 | The `NakamotoBonus.ipynb` demo showcases the effect of assigning the block reward in a proportional and in a uniform way. The user is able to change using a widget the state of the delegations on the network and amount of the reward that is assigned proportionally vs uniformly and see how this affects the way reward is split across validators. 4 | It can be seen that, increasing the amount of reward that is assigned uniformly - i.e. the Nakamoto Bonus - increases the incentive of delegators to delegate on validators having less voting power. 5 | 6 | ## Requirements 7 | 8 | The file `NakamotoBonus.ipynb` is a Jupyter notebook file and it requires python and jupyterlab to be installed in the system. There are several ways to install Jupyter lab, for more information refer to this [link](https://jupyterlab.readthedocs.io/en/stable/getting_started/installation.html). 9 | 10 | ## Usage 11 | 12 | Jupyter should be launched using the terminal from the folder containing the file `NakamotoBonus.ipynb`. 13 | 14 | ```bash 15 | jupyter lab 16 | ``` 17 | 18 | This will launch the jupyter webui. The user can then select the `NakamotoBonus.ipynb` using the file browser on the left side of the UI, and execute the code in the cells to generate the charts. 19 | -------------------------------------------------------------------------------- /docs/architecture/img/adr-001-voting-period-extension-late-quorum-case1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomone-hub/atomone/f82dce0ebfcae9a297fdc5c79e2c234b86f23b8e/docs/architecture/img/adr-001-voting-period-extension-late-quorum-case1.png -------------------------------------------------------------------------------- /docs/architecture/img/adr-001-voting-period-extension-late-quorum-case2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/atomone-hub/atomone/f82dce0ebfcae9a297fdc5c79e2c234b86f23b8e/docs/architecture/img/adr-001-voting-period-extension-late-quorum-case2.png -------------------------------------------------------------------------------- /e2e.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG GO_VERSION 2 | ARG IMG_TAG=latest 3 | 4 | # Compile the atomoned binary 5 | FROM golang:$GO_VERSION-alpine AS atomoned-builder 6 | WORKDIR /src/app/ 7 | COPY go.mod go.sum* ./ 8 | RUN go mod download 9 | COPY . . 10 | ENV PACKAGES curl make git libc-dev bash gcc linux-headers eudev-dev python3 11 | RUN apk add --no-cache $PACKAGES 12 | RUN CGO_ENABLED=0 make install 13 | 14 | # Add to a distroless container 15 | FROM alpine:$IMG_TAG 16 | RUN adduser -D nonroot 17 | ARG IMG_TAG 18 | COPY --from=atomoned-builder /go/bin/atomoned /usr/local/bin/ 19 | EXPOSE 26656 26657 1317 9090 20 | USER nonroot 21 | 22 | ENTRYPOINT ["atomoned", "start"] 23 | -------------------------------------------------------------------------------- /mlc_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignorePatterns": [ { "pattern": "^https://ipfs.io/ipfs/" } ], 3 | "replacementPatterns": [ ], 4 | "httpHeaders": [ ], 5 | "timeout": "20s", 6 | "retryOn429": true, 7 | "retryCount": 5, 8 | "fallbackRetryDelay": "30s", 9 | "aliveStatusCodes": [0, 200, 206, 403, 429] 10 | } 11 | -------------------------------------------------------------------------------- /pkg/address/address.go: -------------------------------------------------------------------------------- 1 | package address 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/cosmos/cosmos-sdk/types/bech32" 7 | ) 8 | 9 | // ConvertBech32Prefix convert bech32 address to specified prefix. 10 | func ConvertBech32Prefix(address, prefix string) (string, error) { 11 | _, bz, err := bech32.DecodeAndConvert(address) 12 | if err != nil { 13 | return "", fmt.Errorf("cannot decode %s address: %s", address, err) 14 | } 15 | 16 | convertedAddress, err := bech32.ConvertAndEncode(prefix, bz) 17 | if err != nil { 18 | return "", fmt.Errorf("cannot convert %s address: %s", address, err) 19 | } 20 | 21 | return convertedAddress, nil 22 | } 23 | -------------------------------------------------------------------------------- /pkg/address/address_test.go: -------------------------------------------------------------------------------- 1 | package address 2 | 3 | import ( 4 | "errors" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestConvertBech32Prefix(t *testing.T) { 11 | cases := []struct { 12 | name string 13 | address string 14 | prefix string 15 | converted string 16 | err error 17 | }{ 18 | { 19 | name: "Convert valid bech 32 address", 20 | address: "akash1a6zlyvpnksx8wr6wz8wemur2xe8zyh0ytz6d88", 21 | converted: "cosmos1a6zlyvpnksx8wr6wz8wemur2xe8zyh0yxeh27a", 22 | prefix: "cosmos", 23 | }, 24 | { 25 | name: "Convert invalid address", 26 | address: "invalidaddress", 27 | prefix: "cosmos", 28 | err: errors.New("cannot decode invalidaddress address: decoding bech32 failed: invalid separator index -1"), 29 | }, 30 | } 31 | 32 | for _, tt := range cases { 33 | t.Run(tt.name, func(t *testing.T) { 34 | convertedAddress, err := ConvertBech32Prefix(tt.address, tt.prefix) 35 | if tt.err != nil { 36 | require.ErrorContains(t, err, tt.err.Error()) 37 | } else { 38 | require.NoError(t, err) 39 | } 40 | require.Equal(t, tt.converted, convertedAddress) 41 | }) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /post/post.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | errorsmod "cosmossdk.io/errors" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 8 | 9 | feemarketpost "github.com/atomone-hub/atomone/x/feemarket/post" 10 | ) 11 | 12 | // PostHandlerOptions are the options required for constructing a FeeMarket PostHandler. 13 | type HandlerOptions struct { 14 | FeemarketKeeper feemarketpost.FeeMarketKeeper 15 | ConsensusParamsKeeper feemarketpost.ConsensusParamsKeeper 16 | } 17 | 18 | // NewPostHandler returns a PostHandler chain with the fee deduct decorator. 19 | func NewPostHandler(options HandlerOptions) (sdk.PostHandler, error) { 20 | if options.FeemarketKeeper == nil { 21 | return nil, errorsmod.Wrap(sdkerrors.ErrLogic, "feemarket keeper is required for post builder") 22 | } 23 | 24 | postDecorators := []sdk.PostDecorator{ 25 | feemarketpost.NewFeemarketStateUpdateDecorator( 26 | options.FeemarketKeeper, 27 | options.ConsensusParamsKeeper, 28 | ), 29 | } 30 | 31 | return sdk.ChainPostDecorators(postDecorators...), nil 32 | } 33 | -------------------------------------------------------------------------------- /proto/atomone/feemarket/module/v1/module.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package atomone.feemarket.module.v1; 4 | 5 | import "cosmos/app/v1alpha1/module.proto"; 6 | 7 | // Module is the config object of the builder module. 8 | message Module { 9 | option (cosmos.app.v1alpha1.module) = { 10 | go_import : "github.com/atomone-hub/atomone/x/feemarket" 11 | }; 12 | 13 | // Authority defines the custom module authority. If not set, defaults to the 14 | // governance module. 15 | string authority = 1; 16 | } 17 | -------------------------------------------------------------------------------- /proto/atomone/feemarket/v1/genesis.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package atomone.feemarket.v1; 3 | 4 | option go_package = "github.com/atomone-hub/atomone/x/feemarket/types"; 5 | 6 | import "gogoproto/gogo.proto"; 7 | import "cosmos_proto/cosmos.proto"; 8 | import "atomone/feemarket/v1/params.proto"; 9 | 10 | // GenesisState defines the feemarket module's genesis state. 11 | message GenesisState { 12 | // Params are the parameters for the feemarket module. These parameters 13 | // can be utilized to implement both the base EIP-1559 fee market and 14 | // and the AIMD EIP-1559 fee market. 15 | Params params = 1 [ (gogoproto.nullable) = false ]; 16 | 17 | // State contains the current state of the AIMD fee market. 18 | State state = 2 [ (gogoproto.nullable) = false ]; 19 | } 20 | 21 | // State is utilized to track the current state of the fee market. This includes 22 | // the current base fee, learning rate, and block gas within the 23 | // specified AIMD window. 24 | message State { 25 | // BaseGasPrice is the current base fee. This is denominated in the fee per 26 | // gas unit. 27 | string base_gas_price = 1 [ 28 | (cosmos_proto.scalar) = "cosmos.Dec", 29 | (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", 30 | (gogoproto.nullable) = false 31 | ]; 32 | 33 | // LearningRate is the current learning rate. 34 | string learning_rate = 2 [ 35 | (cosmos_proto.scalar) = "cosmos.Dec", 36 | (gogoproto.customtype) = "cosmossdk.io/math.LegacyDec", 37 | (gogoproto.nullable) = false 38 | ]; 39 | 40 | // Window contains a list of the last blocks' gas values. This is used 41 | // to calculate the next base fee. This stores the number of units of gas 42 | // consumed per block. 43 | repeated uint64 window = 3; 44 | 45 | // Index is the index of the current block in the block gas window. 46 | uint64 index = 4; 47 | } 48 | -------------------------------------------------------------------------------- /proto/atomone/feemarket/v1/tx.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package atomone.feemarket.v1; 3 | 4 | import "atomone/feemarket/v1/params.proto"; 5 | import "cosmos_proto/cosmos.proto"; 6 | import "cosmos/msg/v1/msg.proto"; 7 | import "gogoproto/gogo.proto"; 8 | import "amino/amino.proto"; 9 | 10 | option go_package = "github.com/atomone-hub/atomone/x/feemarket/types"; 11 | 12 | // Message service defines the types of messages supported by the feemarket 13 | // module. 14 | service Msg { 15 | option (cosmos.msg.v1.service) = true; 16 | 17 | // UpdateParams defines a method for updating the feemarket module parameters. 18 | rpc UpdateParams(MsgUpdateParams) returns (MsgUpdateParamsResponse); 19 | } 20 | 21 | // MsgUpdateParams defines the sdk.Msg/UpdateParams request type. It contains 22 | // the new parameters for the feemarket module. 23 | message MsgUpdateParams { 24 | option (cosmos.msg.v1.signer) = "authority"; 25 | option (amino.name) = "atomone/x/feemarket/v1/MsgUpdateParams"; 26 | 27 | // Authority defines the authority that is updating the feemarket module 28 | // parameters. 29 | string authority = 1 [ (cosmos_proto.scalar) = "cosmos.AddressString" ]; 30 | // Params defines the new parameters for the feemarket module. 31 | Params params = 2 [ (gogoproto.nullable) = false ]; 32 | } 33 | 34 | // MsgUpdateParamsResponse defines the response structure for executing a 35 | // MsgUpdateParams message. 36 | message MsgUpdateParamsResponse {} 37 | -------------------------------------------------------------------------------- /proto/atomone/gov/module/v1/module.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package atomone.gov.module.v1; 4 | 5 | import "cosmos/app/v1alpha1/module.proto"; 6 | 7 | // Module is the config object of the gov module. 8 | message Module { 9 | option (cosmos.app.v1alpha1.module) = { 10 | go_import : "github.com/atomone-hub/atomone/x/gov" 11 | }; 12 | 13 | // max_metadata_len defines the maximum proposal metadata length. 14 | // Defaults to 255 if not explicitly set. 15 | uint64 max_metadata_len = 1; 16 | 17 | // authority defines the custom module authority. If not set, defaults to the 18 | // governance module. 19 | string authority = 2; 20 | } 21 | -------------------------------------------------------------------------------- /proto/atomone/gov/v1/genesis.proto: -------------------------------------------------------------------------------- 1 | // Since: cosmos-sdk 0.46 2 | syntax = "proto3"; 3 | 4 | package atomone.gov.v1; 5 | 6 | import "atomone/gov/v1/gov.proto"; 7 | 8 | option go_package = "github.com/atomone-hub/atomone/x/gov/types/v1"; 9 | 10 | // GenesisState defines the gov module's genesis state. 11 | message GenesisState { 12 | // starting_proposal_id is the ID of the starting proposal. 13 | uint64 starting_proposal_id = 1; 14 | // deposits defines all the deposits present at genesis. 15 | repeated Deposit deposits = 2; 16 | // votes defines all the votes present at genesis. 17 | repeated Vote votes = 3; 18 | // proposals defines all the proposals present at genesis. 19 | repeated Proposal proposals = 4; 20 | // Deprecated: Prefer to use `params` instead. 21 | // deposit_params defines all the paramaters of related to deposit. 22 | DepositParams deposit_params = 5 [ deprecated = true ]; 23 | // Deprecated: Prefer to use `params` instead. 24 | // voting_params defines all the paramaters of related to voting. 25 | VotingParams voting_params = 6 [ deprecated = true ]; 26 | // Deprecated: Prefer to use `params` instead. 27 | // tally_params defines all the paramaters of related to tally. 28 | TallyParams tally_params = 7 [ deprecated = true ]; 29 | // params defines all the paramaters of x/gov module. 30 | // 31 | // Since: cosmos-sdk 0.47 32 | Params params = 8; 33 | // The constitution allows builders to lay a foundation and define purpose. 34 | // 35 | // Since: cosmos-sdk 0.48 36 | string constitution = 9; 37 | 38 | // last updated value for the dynamic min deposit 39 | LastMinDeposit last_min_deposit = 10; 40 | 41 | // last updated value for the dynamic min initial deposit 42 | LastMinDeposit last_min_initial_deposit = 11; 43 | } 44 | -------------------------------------------------------------------------------- /proto/atomone/gov/v1beta1/genesis.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package atomone.gov.v1beta1; 4 | 5 | import "gogoproto/gogo.proto"; 6 | import "atomone/gov/v1beta1/gov.proto"; 7 | import "amino/amino.proto"; 8 | 9 | option go_package = "github.com/atomone-hub/atomone/x/gov/types/v1beta1"; 10 | 11 | // GenesisState defines the gov module's genesis state. 12 | message GenesisState { 13 | // starting_proposal_id is the ID of the starting proposal. 14 | uint64 starting_proposal_id = 1; 15 | // deposits defines all the deposits present at genesis. 16 | repeated Deposit deposits = 2 [ 17 | (gogoproto.castrepeated) = "Deposits", 18 | (gogoproto.nullable) = false, 19 | (amino.dont_omitempty) = true 20 | ]; 21 | // votes defines all the votes present at genesis. 22 | repeated Vote votes = 3 [ 23 | (gogoproto.castrepeated) = "Votes", 24 | (gogoproto.nullable) = false, 25 | (amino.dont_omitempty) = true 26 | ]; 27 | // proposals defines all the proposals present at genesis. 28 | repeated Proposal proposals = 4 [ 29 | (gogoproto.castrepeated) = "Proposals", 30 | (gogoproto.nullable) = false, 31 | (amino.dont_omitempty) = true 32 | ]; 33 | // params defines all the parameters of related to deposit. 34 | DepositParams deposit_params = 5 35 | [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; 36 | // params defines all the parameters of related to voting. 37 | VotingParams voting_params = 6 38 | [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; 39 | // params defines all the parameters of related to tally. 40 | TallyParams tally_params = 7 41 | [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; 42 | } 43 | -------------------------------------------------------------------------------- /proto/atomone/photon/v1/genesis.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package atomone.photon.v1; 3 | 4 | import "gogoproto/gogo.proto"; 5 | import "atomone/photon/v1/photon.proto"; 6 | import "amino/amino.proto"; 7 | 8 | option go_package = "github.com/atomone-hub/atomone/x/photon/types"; 9 | 10 | // GenesisState defines the x/photon module's genesis state. 11 | message GenesisState { 12 | Params params = 1 [ (gogoproto.nullable) = false, (amino.dont_omitempty) = true ]; 13 | } 14 | -------------------------------------------------------------------------------- /proto/atomone/photon/v1/photon.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package atomone.photon.v1; 3 | 4 | import "gogoproto/gogo.proto"; 5 | 6 | option go_package = "github.com/atomone-hub/atomone/x/photon/types"; 7 | 8 | // Params defines the parameters for the x/photon module. 9 | message Params { 10 | // Allow to mint photon or not 11 | bool mint_disabled = 1; 12 | // tx_fee_exceptions holds the msg type urls that are allowed to use some 13 | // different tx fee coins than photon. 14 | // A wildcard "*" can be used to allow all transactions to use any fee denom. 15 | repeated string tx_fee_exceptions = 2; 16 | } 17 | -------------------------------------------------------------------------------- /proto/atomone/photon/v1/query.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package atomone.photon.v1; 3 | 4 | import "gogoproto/gogo.proto"; 5 | import "google/api/annotations.proto"; 6 | import "cosmos/base/query/v1beta1/pagination.proto"; 7 | import "atomone/photon/v1/photon.proto"; 8 | import "cosmos/base/v1beta1/coin.proto"; 9 | import "amino/amino.proto"; 10 | import "cosmos_proto/cosmos.proto"; 11 | 12 | option go_package = "github.com/atomone-hub/atomone/x/photon/types"; 13 | 14 | // Query defines the gRPC querier service. 15 | service Query { 16 | // Parameters queries the parameters of the module. 17 | rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { 18 | option (google.api.http).get = "/atomone/photon/v1/params"; 19 | } 20 | // ConversionRate queries the photon's conversion rate 21 | rpc ConversionRate(QueryConversionRateRequest) returns (QueryConversionRateResponse) { 22 | option (google.api.http).get = "/atomone/photon/v1/conversion_rate"; 23 | } 24 | } 25 | 26 | // QueryParamsRequest is request type for the Query/Params RPC method. 27 | message QueryParamsRequest {} 28 | 29 | // QueryParamsResponse is response type for the Query/Params RPC method. 30 | message QueryParamsResponse { 31 | // params holds all the parameters of this module. 32 | Params params = 1 [ (gogoproto.nullable) = false ]; 33 | } 34 | 35 | // QueryConversionRateRequest is request type for the Query/ConversionRate RPC method. 36 | message QueryConversionRateRequest {} 37 | 38 | // QueryConversionRateResponse is response type for the Query/ConversionRate RPC method. 39 | message QueryConversionRateResponse { 40 | // conversion_rate represents the factor used to convert atone to photon. 41 | string conversion_rate = 1 [ (cosmos_proto.scalar) = "cosmos.Dec" ]; 42 | } 43 | -------------------------------------------------------------------------------- /proto/buf.gen.gogo.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | plugins: 3 | - name: gocosmos 4 | out: .. 5 | opt: plugins=grpc,Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types 6 | - name: grpc-gateway 7 | out: .. 8 | opt: logtostderr=true,allow_colon_final_segments=true -------------------------------------------------------------------------------- /proto/buf.gen.pulsar.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | 3 | managed: 4 | enabled: true 5 | go_package_prefix: 6 | default: cosmossdk.io/api 7 | except: 8 | - buf.build/googleapis/googleapis 9 | - buf.build/cosmos/gogo-proto 10 | - buf.build/cosmos/cosmos-proto 11 | plugins: 12 | - name: go-pulsar 13 | out: ../api 14 | opt: paths=source_relative 15 | - name: go-grpc 16 | out: ../api 17 | opt: paths=source_relative -------------------------------------------------------------------------------- /proto/buf.gen.swagger.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | plugins: 3 | - name: swagger 4 | out: ./tmp-swagger-gen 5 | opt: 6 | - logtostderr=true 7 | - fqn_for_swagger_name=true 8 | - simple_operation_ids=true 9 | -------------------------------------------------------------------------------- /proto/buf.lock: -------------------------------------------------------------------------------- 1 | # Generated by buf. DO NOT EDIT. 2 | version: v1 3 | deps: 4 | - remote: buf.build 5 | owner: cosmos 6 | repository: cosmos-proto 7 | commit: 1935555c206d4afb9e94615dfd0fad31 8 | digest: shake256:c74d91a3ac7ae07d579e90eee33abf9b29664047ac8816500cf22c081fec0d72d62c89ce0bebafc1f6fec7aa5315be72606717740ca95007248425102c365377 9 | - remote: buf.build 10 | owner: cosmos 11 | repository: cosmos-sdk 12 | commit: 954f7b05f38440fc8250134b15adec47 13 | digest: shake256:2ab4404fd04a7d1d52df0e2d0f2d477a3d83ffd88d876957bf3fedfd702c8e52833d65b3ce1d89a3c5adf2aab512616b0e4f51d8463f07eda9a8a3317ee3ac54 14 | - remote: buf.build 15 | owner: cosmos 16 | repository: gogo-proto 17 | commit: 5e5b9fdd01804356895f8f79a6f1ddc1 18 | digest: shake256:0b85da49e2e5f9ebc4806eae058e2f56096ff3b1c59d1fb7c190413dd15f45dd456f0b69ced9059341c80795d2b6c943de15b120a9e0308b499e43e4b5fc2952 19 | - remote: buf.build 20 | owner: cosmos 21 | repository: ibc 22 | commit: fbb44f5ad3194450af479a615fa715d9 23 | digest: shake256:3fbf41c96089017ebf3b5143f78de0d531f604cb11da1bc98b2104eb6dd295b8a49f5f35c60b8389ba50bfa08959da905109324099e75ece9afd8e4087b14019 24 | - remote: buf.build 25 | owner: cosmos 26 | repository: ics23 27 | commit: 55085f7c710a45f58fa09947208eb70b 28 | digest: shake256:9bf0bc495b5a11c88d163d39ef521bc4b00bc1374a05758c91d82821bdc61f09e8c2c51dda8452529bf80137f34d852561eacbe9550a59015d51cecb0dacb628 29 | - remote: buf.build 30 | owner: cosmos 31 | repository: interchain-security 32 | commit: 4fcaba68958648a180fdc72c487018b5 33 | digest: shake256:a699ee174513d757cbd2f7a5827f9622b15c0204fe850bdeedbeb4e141f6c33a0e3e341e7aaf6cdd2e05f3deaf6932282805a090b0624759a2418ed7aceb3bc7 34 | - remote: buf.build 35 | owner: googleapis 36 | repository: googleapis 37 | commit: cc916c31859748a68fd229a3c8d7a2e8 38 | digest: shake256:469b049d0eb04203d5272062636c078decefc96fec69739159c25d85349c50c34c7706918a8b216c5c27f76939df48452148cff8c5c3ae77fa6ba5c25c1b8bf8 39 | -------------------------------------------------------------------------------- /proto/buf.yaml: -------------------------------------------------------------------------------- 1 | # Generated by "buf config migrate-v1beta1". Edit as necessary, and 2 | # remove this comment when you're finished. 3 | # 4 | # This module represents the "proto" root found in 5 | # the previous configuration. 6 | version: v1 7 | name: buf.build/atomone-hub/atomone 8 | deps: 9 | - buf.build/cosmos/gogo-proto 10 | - buf.build/cosmos/cosmos-sdk:v0.47.0 11 | - buf.build/googleapis/googleapis 12 | 13 | breaking: 14 | use: 15 | - FILE 16 | lint: 17 | use: 18 | - DEFAULT 19 | - COMMENTS 20 | - FILE_LOWER_SNAKE_CASE 21 | except: 22 | - UNARY_RPC 23 | - COMMENT_FIELD 24 | - SERVICE_SUFFIX 25 | - PACKAGE_VERSION_SUFFIX 26 | - RPC_REQUEST_STANDARD_NAME 27 | ignore: 28 | - tendermint 29 | -------------------------------------------------------------------------------- /proto/scripts/protoc-swagger-gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | cd proto 6 | # Generate atomone & cosmos-sdj swagger files 7 | # We don't care about filtering query.proto or service.proto files, because 8 | # client/docs/config.json has a white list of the required files. 9 | buf generate --template buf.gen.swagger.yaml 10 | buf generate --template buf.gen.swagger.yaml buf.build/cosmos/cosmos-sdk 11 | 12 | # combine swagger files 13 | # uses nodejs package `swagger-combine`. 14 | # all the individual swagger files need to be configured in `config.json` for merging 15 | swagger-combine ../client/docs/config.json -o ../client/docs/swagger-ui/swagger.yaml -f yaml --continueOnConflictingPaths true --includeDefinitions true 16 | 17 | # clean swagger files 18 | rm -rf ./tmp-swagger-gen 19 | -------------------------------------------------------------------------------- /proto/scripts/protocgen-pulsar.sh: -------------------------------------------------------------------------------- 1 | 2 | # this script is for generating protobuf files for the new google.golang.org/protobuf API 3 | 4 | set -eo pipefail 5 | 6 | protoc_install_gopulsar() { 7 | go install github.com/cosmos/cosmos-proto/cmd/protoc-gen-go-pulsar@latest 8 | go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest 9 | } 10 | 11 | protoc_install_gopulsar 12 | 13 | echo "Cleaning API directory" 14 | (cd api; find ./ -type f \( -iname \*.pulsar.go -o -iname \*.pb.go -o -iname \*.cosmos_orm.go -o -iname \*.pb.gw.go \) -delete; find . -empty -type d -delete; cd ..) 15 | 16 | echo "Generating API module" 17 | (cd proto; buf generate --template buf.gen.pulsar.yaml) 18 | -------------------------------------------------------------------------------- /proto/scripts/protocgen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | echo "Generating gogo proto code" 5 | cd proto 6 | proto_dirs=$(find ./ -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) 7 | for dir in $proto_dirs; do 8 | for file in $(find "${dir}" -maxdepth 1 -name '*.proto'); do 9 | if grep "option go_package" $file &> /dev/null ; then 10 | buf generate --template buf.gen.gogo.yaml $file 11 | fi 12 | done 13 | done 14 | cd .. 15 | # move proto files to the right places 16 | cp -r github.com/atomone-hub/atomone/* ./ 17 | rm -rf github.com 18 | -------------------------------------------------------------------------------- /tests/e2e/address.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "strconv" 7 | 8 | "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" 9 | crypto "github.com/cosmos/cosmos-sdk/crypto/types" 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | ) 12 | 13 | // HDPath generates an HD path based on the wallet index 14 | func HDPath(index int) string { 15 | return fmt.Sprintf("m/44'/118'/0'/0/%d", index) 16 | } 17 | 18 | // PubKey returns a sample account PubKey 19 | func PubKey() crypto.PubKey { 20 | seed := []byte(strconv.Itoa(rand.Int())) 21 | return ed25519.GenPrivKeyFromSecret(seed).PubKey() 22 | } 23 | 24 | // AccAddress returns a sample account address 25 | func AccAddress() sdk.AccAddress { 26 | addr := PubKey().Address() 27 | return sdk.AccAddress(addr) 28 | } 29 | 30 | // Address returns a sample string account address 31 | func Address() string { 32 | return AccAddress().String() 33 | } 34 | -------------------------------------------------------------------------------- /tests/e2e/doc.go: -------------------------------------------------------------------------------- 1 | // package e2e defines an integration testing suite used for full end-to-end 2 | // testing functionality. 3 | // 4 | // The file e2e_suite_test.go defines the testing suite and contains the core 5 | // bootrapping logic that creates a testing environment via Docker containers. 6 | // A testing network is created dynamically and contains multiple Docker 7 | // containers: 8 | // 9 | // 1. Two independent AtomOne networks 10 | // 3. A hermes relayer connecting the two AtomOne networks over IBC 11 | // 12 | // The file e2e_test.go contains the actual end-to-end integration tests that 13 | // utilize the testing suite. 14 | package e2e 15 | -------------------------------------------------------------------------------- /tests/e2e/docker/hermes.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM --platform=linux/amd64 informalsystems/hermes:1.10.0 AS hermes-builder 2 | 3 | FROM --platform=linux/amd64 debian:buster-slim 4 | USER root 5 | 6 | COPY --from=hermes-builder /usr/bin/hermes /usr/local/bin/ 7 | RUN chmod +x /usr/local/bin/hermes 8 | 9 | EXPOSE 3031 -------------------------------------------------------------------------------- /tests/e2e/e2e_encode_test.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "encoding/base64" 5 | "path/filepath" 6 | 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | ) 9 | 10 | const ( 11 | rawTxFile = "tx_raw.json" 12 | ) 13 | 14 | func (s *IntegrationTestSuite) testEncode() { 15 | chain := s.chainA 16 | _, encoded, err := buildRawTx() 17 | s.Require().NoError(err) 18 | 19 | got := s.execEncode(chain, filepath.Join(atomoneHomePath, rawTxFile)) 20 | s.T().Logf("encoded tx: %s", got) 21 | s.Require().Equal(encoded, got) 22 | } 23 | 24 | func (s *IntegrationTestSuite) testDecode() { 25 | chain := s.chainA 26 | rawTx, encoded, err := buildRawTx() 27 | s.Require().NoError(err) 28 | 29 | got := s.execDecode(chain, encoded) 30 | s.T().Logf("raw tx: %s", got) 31 | s.Require().Equal(string(rawTx), got) 32 | } 33 | 34 | // buildRawTx build a dummy tx using the TxBuilder and 35 | // return the JSON and encoded tx's 36 | func buildRawTx() ([]byte, string, error) { 37 | builder := txConfig.NewTxBuilder() 38 | builder.SetGasLimit(gas) 39 | builder.SetFeeAmount(sdk.NewCoins(standardFees)) 40 | builder.SetMemo("foomemo") 41 | tx, err := txConfig.TxJSONEncoder()(builder.GetTx()) 42 | if err != nil { 43 | return nil, "", err 44 | } 45 | txBytes, err := txConfig.TxEncoder()(builder.GetTx()) 46 | if err != nil { 47 | return nil, "", err 48 | } 49 | return tx, base64.StdEncoding.EncodeToString(txBytes), err 50 | } 51 | -------------------------------------------------------------------------------- /tests/e2e/e2e_evidence_test.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "time" 7 | 8 | "github.com/cosmos/cosmos-sdk/x/evidence/exported" 9 | evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types" 10 | ) 11 | 12 | func (s *IntegrationTestSuite) testEvidenceQueries() { 13 | s.Run("queries", func() { 14 | var ( 15 | valIdx = 0 16 | chain = s.chainA 17 | chainAPI = fmt.Sprintf("http://%s", s.valResources[chain.id][valIdx].GetHostPort("1317/tcp")) 18 | ) 19 | res, err := queryAllEvidence(chainAPI) 20 | s.Require().NoError(err) 21 | s.Require().Equal(numberOfEvidences, len(res.Evidence)) 22 | for _, evidence := range res.Evidence { 23 | var exportedEvidence exported.Evidence 24 | err := cdc.UnpackAny(evidence, &exportedEvidence) 25 | s.Require().NoError(err) 26 | eq, ok := exportedEvidence.(*evidencetypes.Equivocation) 27 | s.Require().True(ok) 28 | s.execQueryEvidence(chain, valIdx, eq.Hash().String()) 29 | } 30 | }) 31 | } 32 | 33 | func (s *IntegrationTestSuite) execQueryEvidence(c *chain, valIdx int, hash string) (res evidencetypes.Equivocation) { 34 | ctx, cancel := context.WithTimeout(context.Background(), time.Minute) 35 | defer cancel() 36 | 37 | s.T().Logf("querying evidence %s on chain %s", hash, c.id) 38 | 39 | atomoneCommand := []string{ 40 | atomonedBinary, 41 | queryCommand, 42 | evidencetypes.ModuleName, 43 | hash, 44 | } 45 | 46 | s.executeAtomoneTxCommand(ctx, c, atomoneCommand, valIdx, func(stdOut []byte, stdErr []byte) bool { 47 | // TODO parse evidence after fix the SDK 48 | // https://github.com/cosmos/cosmos-sdk/issues/13444 49 | // s.Require().NoError(yaml.Unmarshal(stdOut, &res)) 50 | return true 51 | }) 52 | return res 53 | } 54 | -------------------------------------------------------------------------------- /tests/e2e/e2e_slashing_test.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | const jailedValidatorKey = "jailed" 4 | 5 | func (s *IntegrationTestSuite) testSlashing(chainEndpoint string) { 6 | s.Run("unjail validator", func() { 7 | validators, err := queryValidators(chainEndpoint) 8 | s.Require().NoError(err) 9 | 10 | for _, val := range validators { 11 | if val.Jailed { 12 | s.execUnjail( 13 | s.chainA, 14 | withKeyValue(flagFrom, jailedValidatorKey), 15 | ) 16 | 17 | valQ, err := queryValidator(chainEndpoint, val.OperatorAddress) 18 | s.Require().NoError(err) 19 | s.Require().False(valQ.Jailed) 20 | } 21 | } 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /tests/e2e/io.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | // copyFile copy file from src to dst 10 | func copyFile(src, dst string) (int64, error) { 11 | sourceFileStat, err := os.Stat(src) 12 | if err != nil { 13 | return 0, err 14 | } 15 | 16 | if !sourceFileStat.Mode().IsRegular() { 17 | return 0, fmt.Errorf("%s is not a regular file", src) 18 | } 19 | 20 | source, err := os.Open(src) 21 | if err != nil { 22 | return 0, err 23 | } 24 | defer source.Close() 25 | 26 | destination, err := os.Create(dst) 27 | if err != nil { 28 | return 0, err 29 | } 30 | defer destination.Close() 31 | 32 | nBytes, err := io.Copy(destination, source) 33 | return nBytes, err 34 | } 35 | 36 | // writeFile write a byte slice into a file path 37 | // create the file if it doesn't exist 38 | // NOTE: this file can be write and read by everyone 39 | func writeFile(path string, body []byte) error { 40 | return os.WriteFile(path, body, 0o666) //nolint:gosec 41 | } 42 | -------------------------------------------------------------------------------- /tests/e2e/keys.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "github.com/cosmos/go-bip39" 5 | ) 6 | 7 | // createMnemonic creates a random string mnemonic 8 | func createMnemonic() (string, error) { 9 | entropySeed, err := bip39.NewEntropy(256) 10 | if err != nil { 11 | return "", err 12 | } 13 | 14 | mnemonic, err := bip39.NewMnemonic(entropySeed) 15 | if err != nil { 16 | return "", err 17 | } 18 | 19 | return mnemonic, nil 20 | } 21 | -------------------------------------------------------------------------------- /tests/e2e/util_test.go: -------------------------------------------------------------------------------- 1 | package e2e 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/cosmos/cosmos-sdk/codec/unknownproto" 7 | sdktx "github.com/cosmos/cosmos-sdk/types/tx" 8 | ) 9 | 10 | func decodeTx(txBytes []byte) (*sdktx.Tx, error) { 11 | var raw sdktx.TxRaw 12 | 13 | // reject all unknown proto fields in the root TxRaw 14 | err := unknownproto.RejectUnknownFieldsStrict(txBytes, &raw, encodingConfig.InterfaceRegistry) 15 | if err != nil { 16 | return nil, fmt.Errorf("failed to reject unknown fields: %w", err) 17 | } 18 | 19 | if err := cdc.Unmarshal(txBytes, &raw); err != nil { 20 | return nil, err 21 | } 22 | 23 | var body sdktx.TxBody 24 | if err := cdc.Unmarshal(raw.BodyBytes, &body); err != nil { 25 | return nil, fmt.Errorf("failed to decode tx: %w", err) 26 | } 27 | 28 | var authInfo sdktx.AuthInfo 29 | 30 | // reject all unknown proto fields in AuthInfo 31 | err = unknownproto.RejectUnknownFieldsStrict(raw.AuthInfoBytes, &authInfo, encodingConfig.InterfaceRegistry) 32 | if err != nil { 33 | return nil, fmt.Errorf("failed to reject unknown fields: %w", err) 34 | } 35 | 36 | if err := cdc.Unmarshal(raw.AuthInfoBytes, &authInfo); err != nil { 37 | return nil, fmt.Errorf("failed to decode auth info: %w", err) 38 | } 39 | 40 | return &sdktx.Tx{ 41 | Body: &body, 42 | AuthInfo: &authInfo, 43 | Signatures: raw.Signatures, 44 | }, nil 45 | } 46 | 47 | func concatFlags(originalCollection []string, commandFlags []string, generalFlags []string) []string { 48 | originalCollection = append(originalCollection, commandFlags...) 49 | originalCollection = append(originalCollection, generalFlags...) 50 | 51 | return originalCollection 52 | } 53 | -------------------------------------------------------------------------------- /types/errors/errors.go: -------------------------------------------------------------------------------- 1 | package errors 2 | 3 | import ( 4 | errorsmod "cosmossdk.io/errors" 5 | ) 6 | 7 | const codespace = "atomone" 8 | 9 | var ( 10 | // ErrTxDecode is returned if we cannot parse a transaction 11 | ErrTxDecode = errorsmod.Register(codespace, 1, "tx parse error") 12 | // ErrUnauthorized is used whenever a request without sufficient 13 | // authorization is handled. 14 | ErrUnauthorized = errorsmod.Register(codespace, 2, "unauthorized") 15 | 16 | // ErrInsufficientFunds is used when the account cannot pay requested amount. 17 | ErrInsufficientFunds = errorsmod.Register(codespace, 3, "insufficient funds") 18 | 19 | // ErrInsufficientFunds is used when the account cannot pay requested amount. 20 | ErrInsufficientFee = errorsmod.Register(codespace, 4, "insufficient fee") 21 | 22 | // ErrInvalidCoins is used when sdk.Coins are invalid. 23 | ErrInvalidCoins = errorsmod.Register(codespace, 5, "invalid coins") 24 | 25 | // ErrInvalidType defines an error an invalid type. 26 | ErrInvalidType = errorsmod.Register(codespace, 6, "invalid type") 27 | 28 | // ErrLogic defines an internal logic error, e.g. an invariant or assertion 29 | // that is violated. It is a programmer error, not a user-facing error. 30 | ErrLogic = errorsmod.Register(codespace, 7, "internal logic error") 31 | 32 | // ErrNotFound defines an error when requested entity doesn't exist in the state. 33 | ErrNotFound = errorsmod.Register(codespace, 8, "not found") 34 | 35 | // ErrInsufficientStake is used when the account has insufficient staked tokens. 36 | ErrInsufficientStake = errorsmod.Register(codespace, 9, "insufficient stake") 37 | ) 38 | -------------------------------------------------------------------------------- /x/feemarket/ante/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package ante 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 6 | 7 | "github.com/atomone-hub/atomone/x/feemarket/types" 8 | ) 9 | 10 | type FeeMarketKeeper interface { 11 | GetMinGasPrice(sdk.Context, string) (sdk.DecCoin, error) 12 | GetParams(sdk.Context) (types.Params, error) 13 | } 14 | 15 | // AccountKeeper defines the contract needed for AccountKeeper related APIs. 16 | // Interface provides support to use non-sdk AccountKeeper for AnteHandler's decorators. 17 | type AccountKeeper interface { 18 | GetAccount(ctx sdk.Context, addr sdk.AccAddress) authtypes.AccountI 19 | } 20 | 21 | // FeeGrantKeeper defines the expected feegrant keeper. 22 | type FeeGrantKeeper interface { 23 | UseGrantedFees(ctx sdk.Context, granter, grantee sdk.AccAddress, fee sdk.Coins, msgs []sdk.Msg) error 24 | } 25 | 26 | // BankKeeper defines the contract needed for supply related APIs. 27 | type BankKeeper interface { 28 | SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error 29 | } 30 | -------------------------------------------------------------------------------- /x/feemarket/fuzz/tx_priority_test.go: -------------------------------------------------------------------------------- 1 | package fuzz_test 2 | 3 | import ( 4 | "math" 5 | "testing" 6 | 7 | sdkmath "cosmossdk.io/math" 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | "github.com/stretchr/testify/require" 10 | "pgregory.net/rapid" 11 | 12 | "github.com/atomone-hub/atomone/x/feemarket/ante" 13 | ) 14 | 15 | type input struct { 16 | payFee sdk.Coin 17 | gasLimit int64 18 | currentGasPrice sdk.DecCoin 19 | } 20 | 21 | // TestGetTxPriority ensures that tx priority is properly bounded 22 | func TestGetTxPriority(t *testing.T) { 23 | rapid.Check(t, func(t *rapid.T) { 24 | inputs := createRandomInput(t) 25 | 26 | priority := ante.GetTxPriority(inputs.payFee, inputs.gasLimit, inputs.currentGasPrice) 27 | require.GreaterOrEqual(t, priority, int64(0)) 28 | require.LessOrEqual(t, priority, int64(math.MaxInt64)) 29 | }) 30 | } 31 | 32 | // CreateRandomInput returns a random inputs to the priority function. 33 | func createRandomInput(t *rapid.T) input { 34 | denom := "skip" 35 | 36 | price := rapid.Int64Range(1, 1_000_000_000).Draw(t, "gas price") 37 | priceDec := sdkmath.LegacyNewDecWithPrec(price, 6) 38 | 39 | gasLimit := rapid.Int64Range(1_000_000, 1_000_000_000_000).Draw(t, "gas limit") 40 | 41 | if priceDec.MulInt64(gasLimit).GTE(sdkmath.LegacyNewDec(math.MaxInt64)) { 42 | t.Fatalf("not supposed to happen") 43 | } 44 | 45 | payFeeAmt := rapid.Int64Range(priceDec.MulInt64(gasLimit).TruncateInt64(), math.MaxInt64).Draw(t, "fee amount") 46 | 47 | return input{ 48 | payFee: sdk.NewCoin(denom, sdkmath.NewInt(payFeeAmt)), 49 | gasLimit: gasLimit, 50 | currentGasPrice: sdk.NewDecCoinFromDec(denom, priceDec), 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /x/feemarket/keeper/abci.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | abci "github.com/cometbft/cometbft/abci/types" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | // EndBlock returns an endblocker for the x/feemarket module. The endblocker 10 | // is responsible for updating the state of the fee market based on the 11 | // AIMD learning rate adjustment algorithm. 12 | func (k *Keeper) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { 13 | if err := k.UpdateFeeMarket(ctx); err != nil { 14 | panic(err) 15 | } 16 | 17 | return []abci.ValidatorUpdate{} 18 | } 19 | -------------------------------------------------------------------------------- /x/feemarket/keeper/genesis.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | 6 | "github.com/atomone-hub/atomone/x/feemarket/types" 7 | ) 8 | 9 | // InitGenesis initializes the feemarket module's state from a given genesis state. 10 | func (k *Keeper) InitGenesis(ctx sdk.Context, gs types.GenesisState) { 11 | if err := gs.ValidateBasic(); err != nil { 12 | panic(err) 13 | } 14 | 15 | if gs.Params.Window != uint64(len(gs.State.Window)) { 16 | panic("genesis state and parameters do not match for window") 17 | } 18 | 19 | // Initialize the fee market state and parameters. 20 | if err := k.SetParams(ctx, gs.Params); err != nil { 21 | panic(err) 22 | } 23 | 24 | if err := k.SetState(ctx, gs.State); err != nil { 25 | panic(err) 26 | } 27 | 28 | // always init enabled height to -1 until it is explicitly set later in the application 29 | k.SetEnabledHeight(ctx, -1) 30 | } 31 | 32 | // ExportGenesis returns a GenesisState for a given context. 33 | func (k *Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { 34 | // Get the feemarket module's parameters. 35 | params, err := k.GetParams(ctx) 36 | if err != nil { 37 | panic(err) 38 | } 39 | 40 | // Get the feemarket module's state. 41 | state, err := k.GetState(ctx) 42 | if err != nil { 43 | panic(err) 44 | } 45 | 46 | return types.NewGenesisState(params, state) 47 | } 48 | -------------------------------------------------------------------------------- /x/feemarket/keeper/genesis_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/atomone-hub/atomone/x/feemarket/testutil" 7 | "github.com/atomone-hub/atomone/x/feemarket/types" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestInitGenesis(t *testing.T) { 12 | k, _, ctx := testutil.SetupKeeper(t, 0) 13 | t.Run("default genesis should not panic", func(t *testing.T) { 14 | require.NotPanics(t, func() { 15 | k.InitGenesis(ctx, *types.DefaultGenesisState()) 16 | }) 17 | }) 18 | 19 | t.Run("default AIMD genesis should not panic", func(t *testing.T) { 20 | require.NotPanics(t, func() { 21 | k.InitGenesis(ctx, *types.DefaultAIMDGenesisState()) 22 | }) 23 | }) 24 | 25 | t.Run("bad genesis state should panic", func(t *testing.T) { 26 | gs := types.DefaultGenesisState() 27 | gs.Params.Window = 0 28 | require.Panics(t, func() { 29 | k.InitGenesis(ctx, *gs) 30 | }) 31 | }) 32 | 33 | t.Run("mismatch in params and state for window should panic", func(t *testing.T) { 34 | gs := types.DefaultAIMDGenesisState() 35 | gs.Params.Window = 1 36 | 37 | require.Panics(t, func() { 38 | k.InitGenesis(ctx, *gs) 39 | }) 40 | }) 41 | } 42 | 43 | func TestExportGenesis(t *testing.T) { 44 | k, _, ctx := testutil.SetupKeeper(t, 0) 45 | t.Run("export genesis should not panic for default eip-1559", func(t *testing.T) { 46 | gs := types.DefaultGenesisState() 47 | k.InitGenesis(ctx, *gs) 48 | 49 | var exportedGenesis *types.GenesisState 50 | require.NotPanics(t, func() { 51 | exportedGenesis = k.ExportGenesis(ctx) 52 | }) 53 | 54 | require.Equal(t, gs, exportedGenesis) 55 | }) 56 | 57 | t.Run("export genesis should not panic for default AIMD eip-1559", func(t *testing.T) { 58 | gs := types.DefaultAIMDGenesisState() 59 | k.InitGenesis(ctx, *gs) 60 | 61 | var exportedGenesis *types.GenesisState 62 | require.NotPanics(t, func() { 63 | exportedGenesis = k.ExportGenesis(ctx) 64 | }) 65 | 66 | require.Equal(t, gs, exportedGenesis) 67 | }) 68 | } 69 | -------------------------------------------------------------------------------- /x/feemarket/keeper/msg_server.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | 9 | "github.com/atomone-hub/atomone/x/feemarket/types" 10 | ) 11 | 12 | var _ types.MsgServer = (*MsgServer)(nil) 13 | 14 | // MsgServer is the server API for x/feemarket Msg service. 15 | type MsgServer struct { 16 | k *Keeper 17 | } 18 | 19 | // NewMsgServer returns the MsgServer implementation. 20 | func NewMsgServer(k *Keeper) types.MsgServer { 21 | return &MsgServer{k} 22 | } 23 | 24 | // Params defines a method that updates the module's parameters. The signer of the message must 25 | // be the module authority. 26 | func (ms MsgServer) UpdateParams(goCtx context.Context, msg *types.MsgUpdateParams) (*types.MsgUpdateParamsResponse, error) { 27 | ctx := sdk.UnwrapSDKContext(goCtx) 28 | 29 | if msg.Authority != ms.k.GetAuthority() { 30 | return nil, fmt.Errorf("invalid authority to execute message") 31 | } 32 | 33 | gotParams, err := ms.k.GetParams(ctx) 34 | if err != nil { 35 | return nil, fmt.Errorf("error getting params: %w", err) 36 | } 37 | 38 | // if going from disabled -> enabled, set enabled height 39 | if !gotParams.Enabled && msg.Params.Enabled { 40 | ms.k.SetEnabledHeight(ctx, ctx.BlockHeight()) 41 | } 42 | 43 | params := msg.Params 44 | if err := ms.k.SetParams(ctx, params); err != nil { 45 | return nil, fmt.Errorf("error setting params: %w", err) 46 | } 47 | 48 | newState := types.NewState(params.Window, params.MinBaseGasPrice, params.MinLearningRate) 49 | if err := ms.k.SetState(ctx, newState); err != nil { 50 | return nil, fmt.Errorf("error setting state: %w", err) 51 | } 52 | 53 | return &types.MsgUpdateParamsResponse{}, nil 54 | } 55 | -------------------------------------------------------------------------------- /x/feemarket/keeper/query_server.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | 8 | "github.com/atomone-hub/atomone/x/feemarket/types" 9 | ) 10 | 11 | var _ types.QueryServer = (*QueryServer)(nil) 12 | 13 | // QueryServer defines the gRPC server for the x/feemarket module. 14 | type QueryServer struct { 15 | k Keeper 16 | } 17 | 18 | // NewQueryServer creates a new instance of the x/feemarket QueryServer type. 19 | func NewQueryServer(keeper Keeper) types.QueryServer { 20 | return &QueryServer{k: keeper} 21 | } 22 | 23 | // Params defines a method that returns the current feemarket parameters. 24 | func (q QueryServer) Params(goCtx context.Context, _ *types.ParamsRequest) (*types.ParamsResponse, error) { 25 | ctx := sdk.UnwrapSDKContext(goCtx) 26 | 27 | params, err := q.k.GetParams(ctx) 28 | return &types.ParamsResponse{Params: params}, err 29 | } 30 | 31 | // State defines a method that returns the current feemarket state. 32 | func (q QueryServer) State(goCtx context.Context, _ *types.StateRequest) (*types.StateResponse, error) { 33 | ctx := sdk.UnwrapSDKContext(goCtx) 34 | 35 | state, err := q.k.GetState(ctx) 36 | return &types.StateResponse{State: state}, err 37 | } 38 | 39 | // GasPrice defines a method that returns the current feemarket base gas price. 40 | func (q QueryServer) GasPrice(goCtx context.Context, req *types.GasPriceRequest) (*types.GasPriceResponse, error) { 41 | ctx := sdk.UnwrapSDKContext(goCtx) 42 | 43 | gasPrice, err := q.k.GetMinGasPrice(ctx, req.GetDenom()) 44 | return &types.GasPriceResponse{Price: gasPrice}, err 45 | } 46 | 47 | // GasPrices defines a method that returns the current feemarket list of gas prices. 48 | func (q QueryServer) GasPrices(goCtx context.Context, _ *types.GasPricesRequest) (*types.GasPricesResponse, error) { 49 | ctx := sdk.UnwrapSDKContext(goCtx) 50 | 51 | gasPrices, err := q.k.GetMinGasPrices(ctx) 52 | return &types.GasPricesResponse{Prices: gasPrices}, err 53 | } 54 | -------------------------------------------------------------------------------- /x/feemarket/module_simulation.go: -------------------------------------------------------------------------------- 1 | package feemarket 2 | 3 | import ( 4 | "fmt" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/cosmos/cosmos-sdk/types/module" 8 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 9 | 10 | "github.com/atomone-hub/atomone/x/feemarket/types" 11 | "github.com/atomone-hub/atomone/x/gov/simulation" 12 | ) 13 | 14 | // GenerateGenesisState returns a disabled feemarket module because the module 15 | // does not work well with simulations. Especially the feemarket ante handler 16 | // does not accept 0 fee coins which is quite common during simulation's 17 | // operations. 18 | func (AppModule) GenerateGenesisState(simState *module.SimulationState) { 19 | params := types.DefaultParams() 20 | params.Enabled = false 21 | genesis := types.NewGenesisState(params, types.DefaultState()) 22 | simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(genesis) 23 | fmt.Println("Feemarket module is disabled") 24 | } 25 | 26 | // RegisterStoreDecoder registers a decoder. 27 | func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { 28 | sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc) 29 | } 30 | 31 | // WeightedOperations returns the all the module operations with their respective weights. 32 | func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /x/feemarket/post/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package post 2 | 3 | import ( 4 | tmproto "github.com/cometbft/cometbft/proto/tendermint/types" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | 8 | "github.com/atomone-hub/atomone/x/feemarket/types" 9 | ) 10 | 11 | type FeeMarketKeeper interface { 12 | GetState(ctx sdk.Context) (types.State, error) 13 | GetParams(ctx sdk.Context) (types.Params, error) 14 | SetState(ctx sdk.Context, state types.State) error 15 | GetEnabledHeight(ctx sdk.Context) (int64, error) 16 | } 17 | 18 | type ConsensusParamsKeeper interface { 19 | Get(sdk.Context) (*tmproto.ConsensusParams, error) 20 | } 21 | -------------------------------------------------------------------------------- /x/feemarket/testutil/expected_keepers_mocks.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: x/feemarket/types/expected_keepers.go 3 | 4 | // Package testutil is a generated GoMock package. 5 | package testutil 6 | 7 | import ( 8 | reflect "reflect" 9 | 10 | types "github.com/cometbft/cometbft/proto/tendermint/types" 11 | types0 "github.com/cosmos/cosmos-sdk/types" 12 | gomock "github.com/golang/mock/gomock" 13 | ) 14 | 15 | // MockConsensusParamsKeeper is a mock of ConsensusParamsKeeper interface. 16 | type MockConsensusParamsKeeper struct { 17 | ctrl *gomock.Controller 18 | recorder *MockConsensusParamsKeeperMockRecorder 19 | } 20 | 21 | // MockConsensusParamsKeeperMockRecorder is the mock recorder for MockConsensusParamsKeeper. 22 | type MockConsensusParamsKeeperMockRecorder struct { 23 | mock *MockConsensusParamsKeeper 24 | } 25 | 26 | // NewMockConsensusParamsKeeper creates a new mock instance. 27 | func NewMockConsensusParamsKeeper(ctrl *gomock.Controller) *MockConsensusParamsKeeper { 28 | mock := &MockConsensusParamsKeeper{ctrl: ctrl} 29 | mock.recorder = &MockConsensusParamsKeeperMockRecorder{mock} 30 | return mock 31 | } 32 | 33 | // EXPECT returns an object that allows the caller to indicate expected use. 34 | func (m *MockConsensusParamsKeeper) EXPECT() *MockConsensusParamsKeeperMockRecorder { 35 | return m.recorder 36 | } 37 | 38 | // Get mocks base method. 39 | func (m *MockConsensusParamsKeeper) Get(arg0 types0.Context) (*types.ConsensusParams, error) { 40 | m.ctrl.T.Helper() 41 | ret := m.ctrl.Call(m, "Get", arg0) 42 | ret0, _ := ret[0].(*types.ConsensusParams) 43 | ret1, _ := ret[1].(error) 44 | return ret0, ret1 45 | } 46 | 47 | // Get indicates an expected call of Get. 48 | func (mr *MockConsensusParamsKeeperMockRecorder) Get(arg0 interface{}) *gomock.Call { 49 | mr.mock.ctrl.T.Helper() 50 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockConsensusParamsKeeper)(nil).Get), arg0) 51 | } 52 | -------------------------------------------------------------------------------- /x/feemarket/testutil/keeper.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golang/mock/gomock" 7 | 8 | tmproto "github.com/cometbft/cometbft/proto/tendermint/types" 9 | tmtime "github.com/cometbft/cometbft/types/time" 10 | 11 | "github.com/cosmos/cosmos-sdk/testutil" 12 | sdk "github.com/cosmos/cosmos-sdk/types" 13 | moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" 14 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 15 | 16 | "github.com/atomone-hub/atomone/x/feemarket/keeper" 17 | "github.com/atomone-hub/atomone/x/feemarket/types" 18 | govtypes "github.com/atomone-hub/atomone/x/gov/types" 19 | ) 20 | 21 | type Mocks struct { 22 | ConsensusParamsKeeper *MockConsensusParamsKeeper 23 | } 24 | 25 | func SetupMsgServer(t *testing.T, maxBlockGas uint64) (types.MsgServer, *keeper.Keeper, Mocks, sdk.Context) { 26 | t.Helper() 27 | k, m, ctx := SetupKeeper(t, maxBlockGas) 28 | return keeper.NewMsgServer(k), k, m, ctx 29 | } 30 | 31 | func SetupQueryServer(t *testing.T, maxBlockGas uint64) (types.QueryServer, *keeper.Keeper, Mocks, sdk.Context) { 32 | t.Helper() 33 | k, m, ctx := SetupKeeper(t, maxBlockGas) 34 | return keeper.NewQueryServer(*k), k, m, ctx 35 | } 36 | 37 | const MaxBlockGas = 30_000_000 38 | 39 | func SetupKeeper(t *testing.T, maxBlockGas uint64) (*keeper.Keeper, Mocks, sdk.Context) { 40 | t.Helper() 41 | ctrl := gomock.NewController(t) 42 | m := Mocks{ 43 | ConsensusParamsKeeper: NewMockConsensusParamsKeeper(ctrl), 44 | } 45 | 46 | key := sdk.NewKVStoreKey(types.StoreKey) 47 | testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) 48 | ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: tmtime.Now()}) 49 | encCfg := moduletestutil.MakeTestEncodingConfig() 50 | types.RegisterInterfaces(encCfg.InterfaceRegistry) 51 | // banktypes.RegisterInterfaces(encCfg.InterfaceRegistry) 52 | authority := authtypes.NewModuleAddress(govtypes.ModuleName).String() 53 | 54 | // setup block max gas 55 | if maxBlockGas == 0 { 56 | maxBlockGas = MaxBlockGas 57 | } 58 | m.ConsensusParamsKeeper.EXPECT().Get(ctx). 59 | Return(&tmproto.ConsensusParams{ 60 | Block: &tmproto.BlockParams{MaxGas: int64(maxBlockGas)}, 61 | }, nil).MaxTimes(1) 62 | 63 | return keeper.NewKeeper(encCfg.Codec, key, &types.ErrorDenomResolver{}, m.ConsensusParamsKeeper, authority), m, ctx 64 | } 65 | -------------------------------------------------------------------------------- /x/feemarket/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | "github.com/cosmos/cosmos-sdk/codec/legacy" 6 | "github.com/cosmos/cosmos-sdk/codec/types" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | "github.com/cosmos/cosmos-sdk/types/msgservice" 9 | ) 10 | 11 | // RegisterLegacyAminoCodec registers the necessary x/feemarket interfaces (messages) on the 12 | // provided LegacyAmino codec. 13 | func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { 14 | legacy.RegisterAminoMsg(cdc, &MsgUpdateParams{}, "atomone/x/feemarket/v1/MsgUpdateParams") 15 | } 16 | 17 | // RegisterInterfaces registers the x/feemarket interfaces (messages + msg server) on the 18 | // provided InterfaceRegistry. 19 | func RegisterInterfaces(registry types.InterfaceRegistry) { 20 | registry.RegisterImplementations((*sdk.Msg)(nil), 21 | &MsgUpdateParams{}, 22 | ) 23 | 24 | msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) 25 | } 26 | -------------------------------------------------------------------------------- /x/feemarket/types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdkerrors "cosmossdk.io/errors" 5 | ) 6 | 7 | var ( 8 | ErrNoFeeCoins = sdkerrors.New(ModuleName, 1, "no fee coin provided. Must provide one.") 9 | ErrTooManyFeeCoins = sdkerrors.New(ModuleName, 2, "too many fee coins provided. Only one fee coin may be provided") 10 | ErrResolverNotSet = sdkerrors.New(ModuleName, 3, "denom resolver interface not set. Only the feemarket base fee denomination can be used") 11 | ErrMaxGasExceeded = sdkerrors.New(ModuleName, 4, "block gas cannot exceed max block gas") 12 | ) 13 | -------------------------------------------------------------------------------- /x/feemarket/types/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | tmproto "github.com/cometbft/cometbft/proto/tendermint/types" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | type ConsensusParamsKeeper interface { 10 | Get(sdk.Context) (*tmproto.ConsensusParams, error) 11 | } 12 | -------------------------------------------------------------------------------- /x/feemarket/types/genesis.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/cosmos/cosmos-sdk/codec" 7 | ) 8 | 9 | // NewGenesisState returns a new genesis state for the module. 10 | func NewGenesisState( 11 | params Params, 12 | state State, 13 | ) *GenesisState { 14 | return &GenesisState{ 15 | Params: params, 16 | State: state, 17 | } 18 | } 19 | 20 | // ValidateBasic performs basic validation of the genesis state data returning an 21 | // error for any failed validation criteria. 22 | func (gs *GenesisState) ValidateBasic() error { 23 | if err := gs.Params.ValidateBasic(); err != nil { 24 | return err 25 | } 26 | return gs.State.ValidateBasic() 27 | } 28 | 29 | // GetGenesisStateFromAppState returns x/feemarket GenesisState given raw application 30 | // genesis state. 31 | func GetGenesisStateFromAppState(cdc codec.Codec, appState map[string]json.RawMessage) GenesisState { 32 | var gs GenesisState 33 | cdc.MustUnmarshalJSON(appState[ModuleName], &gs) 34 | return gs 35 | } 36 | -------------------------------------------------------------------------------- /x/feemarket/types/genesis_test.go: -------------------------------------------------------------------------------- 1 | package types_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | "github.com/atomone-hub/atomone/x/feemarket/types" 9 | ) 10 | 11 | func TestGenesis(t *testing.T) { 12 | t.Run("can create a new default genesis state", func(t *testing.T) { 13 | gs := types.DefaultGenesisState() 14 | require.NoError(t, gs.ValidateBasic()) 15 | }) 16 | 17 | t.Run("can accept a valid genesis state for AIMD eip-1559", func(t *testing.T) { 18 | gs := types.DefaultAIMDGenesisState() 19 | require.NoError(t, gs.ValidateBasic()) 20 | }) 21 | } 22 | -------------------------------------------------------------------------------- /x/feemarket/types/keys.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // ModuleName is the name of the feemarket module. 5 | ModuleName = "feemarket" 6 | // StoreKey is the store key string for the feemarket module. 7 | StoreKey = ModuleName 8 | ) 9 | 10 | const ( 11 | prefixParams = iota + 1 12 | prefixState 13 | prefixEnableHeight = 3 14 | ) 15 | 16 | var ( 17 | // KeyParams is the store key for the feemarket module's parameters. 18 | KeyParams = []byte{prefixParams} 19 | 20 | // KeyState is the store key for the feemarket module's data. 21 | KeyState = []byte{prefixState} 22 | 23 | // KeyEnabledHeight is the store key for the feemarket module's enabled height. 24 | KeyEnabledHeight = []byte{prefixEnableHeight} 25 | ) 26 | -------------------------------------------------------------------------------- /x/feemarket/types/msgs.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | ) 6 | 7 | var _ sdk.Msg = &MsgUpdateParams{} 8 | 9 | // NewMsgUpdateParams returns a new message to update the x/feemarket module's parameters. 10 | func NewMsgUpdateParams(authority string, params Params) MsgUpdateParams { 11 | return MsgUpdateParams{ 12 | Authority: authority, 13 | Params: params, 14 | } 15 | } 16 | 17 | // GetSigners implements GetSigners for the msg. 18 | func (m *MsgUpdateParams) GetSigners() []sdk.AccAddress { 19 | addr, _ := sdk.AccAddressFromBech32(m.Authority) 20 | return []sdk.AccAddress{addr} 21 | } 22 | 23 | // ValidateBasic determines whether the information in the message is formatted correctly, specifically 24 | // whether the authority is a valid acc-address. 25 | func (m *MsgUpdateParams) ValidateBasic() error { 26 | // validate authority address 27 | _, err := sdk.AccAddressFromBech32(m.Authority) 28 | if err != nil { 29 | return err 30 | } 31 | 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /x/feemarket/types/msgs_test.go: -------------------------------------------------------------------------------- 1 | package types_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | 10 | "github.com/atomone-hub/atomone/x/feemarket/types" 11 | ) 12 | 13 | func TestMsgUpdateParams(t *testing.T) { 14 | t.Run("should reject a message with an invalid authority address", func(t *testing.T) { 15 | msg := types.NewMsgUpdateParams("invalid", types.DefaultParams()) 16 | err := msg.ValidateBasic() 17 | require.Error(t, err) 18 | }) 19 | 20 | t.Run("should accept an empty message with a valid authority address", func(t *testing.T) { 21 | msg := types.NewMsgUpdateParams(sdk.AccAddress("test").String(), types.DefaultParams()) 22 | err := msg.ValidateBasic() 23 | require.NoError(t, err) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /x/feemarket/types/resolver.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | // DenomResolver is an interface to convert a given token to the feemarket's base token. 10 | type DenomResolver interface { 11 | // ConvertToDenom converts deccoin into the equivalent amount of the token denominated in denom. 12 | ConvertToDenom(ctx sdk.Context, coin sdk.DecCoin, denom string) (sdk.DecCoin, error) 13 | // ExtraDenoms returns a list of denoms in addition of `Params.base_denom` it's possible to pay fees with 14 | ExtraDenoms(ctx sdk.Context) ([]string, error) 15 | } 16 | 17 | // TestDenomResolver is a test implementation of the DenomResolver interface. It returns "feeCoin.Amount baseDenom" for all coins that are not the baseDenom. 18 | // NOTE: DO NOT USE THIS IN PRODUCTION 19 | type TestDenomResolver struct{} 20 | 21 | // ConvertToDenom returns "coin.Amount denom" for all coins that are not the denom. 22 | func (r *TestDenomResolver) ConvertToDenom(_ sdk.Context, coin sdk.DecCoin, denom string) (sdk.DecCoin, error) { 23 | if coin.Denom == denom { 24 | return coin, nil 25 | } 26 | 27 | return sdk.NewDecCoinFromDec(denom, coin.Amount), nil 28 | } 29 | 30 | func (r *TestDenomResolver) ExtraDenoms(_ sdk.Context) ([]string, error) { 31 | return []string{}, nil 32 | } 33 | 34 | // ErrorDenomResolver is a test implementation of the DenomResolver interface. It returns an error for all coins that are not the baseDenom. 35 | // NOTE: DO NOT USE THIS IN PRODUCTION 36 | type ErrorDenomResolver struct{} 37 | 38 | // ConvertToDenom returns an error for all coins that are not the denom. 39 | func (r *ErrorDenomResolver) ConvertToDenom(_ sdk.Context, coin sdk.DecCoin, denom string) (sdk.DecCoin, error) { 40 | if coin.Denom == denom { 41 | return coin, nil 42 | } 43 | 44 | return sdk.DecCoin{}, fmt.Errorf("error resolving denom") 45 | } 46 | 47 | func (r *ErrorDenomResolver) ExtraDenoms(_ sdk.Context) ([]string, error) { 48 | return []string{}, nil 49 | } 50 | -------------------------------------------------------------------------------- /x/gov/abci_internal_test.go: -------------------------------------------------------------------------------- 1 | package gov 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | ) 10 | 11 | func failingHandler(_ sdk.Context, _ sdk.Msg) (*sdk.Result, error) { 12 | panic("test-fail") 13 | } 14 | 15 | func okHandler(_ sdk.Context, _ sdk.Msg) (*sdk.Result, error) { 16 | return new(sdk.Result), nil 17 | } 18 | 19 | func TestSafeExecuteHandler(t *testing.T) { 20 | t.Parallel() 21 | 22 | require := require.New(t) 23 | var ctx sdk.Context 24 | 25 | r, err := safeExecuteHandler(ctx, nil, failingHandler) 26 | require.ErrorContains(err, "test-fail") 27 | require.Nil(r) 28 | 29 | r, err = safeExecuteHandler(ctx, nil, okHandler) 30 | require.Nil(err) 31 | require.NotNil(r) 32 | } 33 | -------------------------------------------------------------------------------- /x/gov/autocli.go: -------------------------------------------------------------------------------- 1 | package gov 2 | 3 | import ( 4 | autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" 5 | govv1 "cosmossdk.io/api/cosmos/gov/v1" 6 | govv1beta1 "cosmossdk.io/api/cosmos/gov/v1beta1" 7 | ) 8 | 9 | // AutoCLIOptions implements the autocli.HasAutoCLIConfig interface. 10 | func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { 11 | return &autocliv1.ModuleOptions{ 12 | Tx: &autocliv1.ServiceCommandDescriptor{ 13 | Service: govv1.Msg_ServiceDesc.ServiceName, 14 | // map v1beta1 as a sub-command 15 | SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{ 16 | "v1beta1": {Service: govv1beta1.Msg_ServiceDesc.ServiceName}, 17 | }, 18 | }, 19 | Query: &autocliv1.ServiceCommandDescriptor{ 20 | Service: govv1.Query_ServiceDesc.ServiceName, 21 | // map v1beta1 as a sub-command 22 | SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{ 23 | "v1beta1": {Service: govv1beta1.Query_ServiceDesc.ServiceName}, 24 | }, 25 | }, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /x/gov/client/proposal_handler.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/spf13/cobra" 5 | ) 6 | 7 | // function to create the cli handler 8 | type CLIHandlerFn func() *cobra.Command 9 | 10 | // ProposalHandler wraps CLIHandlerFn 11 | type ProposalHandler struct { 12 | CLIHandler CLIHandlerFn 13 | } 14 | 15 | // NewProposalHandler creates a new ProposalHandler object 16 | func NewProposalHandler(cliHandler CLIHandlerFn) ProposalHandler { 17 | return ProposalHandler{ 18 | CLIHandler: cliHandler, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /x/gov/client/utils/unified_diff.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hexops/gotextdiff" 7 | "github.com/hexops/gotextdiff/myers" 8 | "github.com/hexops/gotextdiff/span" 9 | ) 10 | 11 | // GenerateUnifiedDiff generates a unified diff from src and dst strings using gotextdiff. 12 | // This is the only function that uses the gotextdiff library as its primary use is for 13 | // clients. 14 | func GenerateUnifiedDiff(src, dst string) (string, error) { 15 | // Create spans for the source and destination texts 16 | srcURI := span.URIFromPath("src") 17 | 18 | if src == "" || src[len(src)-1] != '\n' { 19 | src += "\n" // Add an EOL to src if it's empty or newline is missing 20 | } 21 | if dst == "" || dst[len(dst)-1] != '\n' { 22 | dst += "\n" // Add an EOL to dst if it's empty or newline is missing 23 | } 24 | 25 | // Compute the edits using the Myers diff algorithm 26 | eds := myers.ComputeEdits(srcURI, src, dst) 27 | 28 | // Generate the unified diff string 29 | diff := gotextdiff.ToUnified("src", "dst", src, eds) 30 | 31 | // Convert the diff to a string 32 | diffStr := fmt.Sprintf("%v", diff) 33 | 34 | return diffStr, nil 35 | } 36 | -------------------------------------------------------------------------------- /x/gov/client/utils/unified_diff_test.go: -------------------------------------------------------------------------------- 1 | package utils_test 2 | 3 | import ( 4 | "strings" 5 | "testing" 6 | 7 | "github.com/atomone-hub/atomone/x/gov/client/utils" 8 | "github.com/atomone-hub/atomone/x/gov/types" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestGenerateUnifiedDiff(t *testing.T) { 13 | tests := []struct { 14 | name string 15 | src string 16 | dst string 17 | expected string 18 | }{ 19 | { 20 | name: "No changes", 21 | src: "Line one\nLine two\nLine three", 22 | dst: "Line one\nLine two\nLine three", 23 | expected: ``, 24 | }, 25 | { 26 | name: "Line added", 27 | src: "Line one\nLine two", 28 | dst: "Line one\nLine two\nLine three", 29 | expected: `@@ -1,2 +1,3 @@ 30 | Line one 31 | Line two 32 | +Line three 33 | `, 34 | }, 35 | { 36 | name: "Line deleted", 37 | src: "Line one\nLine two\nLine three", 38 | dst: "Line one\nLine three", 39 | expected: `@@ -1,3 +1,2 @@ 40 | Line one 41 | -Line two 42 | Line three 43 | `, 44 | }, 45 | { 46 | name: "Line modified", 47 | src: "Line one\nLine two\nLine three", 48 | dst: "Line one\nLine two modified\nLine three", 49 | expected: `@@ -1,3 +1,3 @@ 50 | Line one 51 | -Line two 52 | +Line two modified 53 | Line three 54 | `, 55 | }, 56 | { 57 | name: "Multiple changes", 58 | src: "Line one\nLine two\nLine three", 59 | dst: "Line zero\nLine one\nLine three\nLine four", 60 | expected: `@@ -1,3 +1,4 @@ 61 | +Line zero 62 | Line one 63 | -Line two 64 | Line three 65 | +Line four 66 | `, 67 | }, 68 | } 69 | 70 | for _, tt := range tests { 71 | t.Run(tt.name, func(t *testing.T) { 72 | diff, err := utils.GenerateUnifiedDiff(tt.src, tt.dst) 73 | require.NoError(t, err) 74 | 75 | diffContent := strings.TrimPrefix(diff, "--- src\n+++ dst\n") 76 | expectedContent := strings.TrimPrefix(tt.expected, "--- src\n+++ dst\n") 77 | 78 | require.Equal(t, expectedContent, diffContent) 79 | }) 80 | } 81 | } 82 | 83 | func TestUnifiedDiffIntegration(t *testing.T) { 84 | src := "Line one\nLine two\nLine three" 85 | dst := "Line zero\nLine one\nLine three\nLine four" 86 | 87 | diffStr, err := utils.GenerateUnifiedDiff(src, dst) 88 | require.NoError(t, err) 89 | 90 | result, err := types.ApplyUnifiedDiff(src, diffStr) 91 | require.NoError(t, err) 92 | require.Equal(t, dst, result) 93 | } 94 | -------------------------------------------------------------------------------- /x/gov/client/utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/atomone-hub/atomone/x/gov/types/v1beta1" 7 | ) 8 | 9 | // NormalizeVoteOption - normalize user specified vote option 10 | func NormalizeVoteOption(option string) string { 11 | switch option { 12 | case "Yes", "yes": 13 | return v1beta1.OptionYes.String() 14 | 15 | case "Abstain", "abstain": 16 | return v1beta1.OptionAbstain.String() 17 | 18 | case "No", "no": 19 | return v1beta1.OptionNo.String() 20 | 21 | default: 22 | return option 23 | } 24 | } 25 | 26 | // NormalizeWeightedVoteOptions - normalize vote options param string 27 | func NormalizeWeightedVoteOptions(options string) string { 28 | newOptions := []string{} 29 | for _, option := range strings.Split(options, ",") { 30 | fields := strings.Split(option, "=") 31 | fields[0] = NormalizeVoteOption(fields[0]) 32 | if len(fields) < 2 { 33 | fields = append(fields, "1") 34 | } 35 | newOptions = append(newOptions, strings.Join(fields, "=")) 36 | } 37 | return strings.Join(newOptions, ",") 38 | } 39 | 40 | // NormalizeProposalType - normalize user specified proposal type. 41 | func NormalizeProposalType(proposalType string) string { 42 | switch proposalType { 43 | case "Text", "text": 44 | return v1beta1.ProposalTypeText 45 | 46 | default: 47 | return "" 48 | } 49 | } 50 | 51 | // NormalizeProposalStatus - normalize user specified proposal status. 52 | func NormalizeProposalStatus(status string) string { 53 | switch status { 54 | case "DepositPeriod", "deposit_period": 55 | return v1beta1.StatusDepositPeriod.String() 56 | case "VotingPeriod", "voting_period": 57 | return v1beta1.StatusVotingPeriod.String() 58 | case "Passed", "passed": 59 | return v1beta1.StatusPassed.String() 60 | case "Rejected", "rejected": 61 | return v1beta1.StatusRejected.String() 62 | default: 63 | return status 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /x/gov/codec/cdc.go: -------------------------------------------------------------------------------- 1 | package codec 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | var ( 10 | Amino = codec.NewLegacyAmino() 11 | ModuleCdc = codec.NewAminoCodec(Amino) 12 | ) 13 | 14 | func init() { 15 | cryptocodec.RegisterCrypto(Amino) 16 | codec.RegisterEvidences(Amino) 17 | sdk.RegisterLegacyAminoCodec(Amino) 18 | } 19 | -------------------------------------------------------------------------------- /x/gov/codec/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Package codec provides a singleton instance of Amino codec that should be used to register 3 | any concrete type that can later be referenced inside a MsgSubmitProposal instance so that they 4 | can be (de)serialized properly. 5 | 6 | Amino types should be ideally registered inside this codec within the init function of each module's 7 | codec.go file as follows: 8 | 9 | func init() { 10 | // ... 11 | 12 | RegisterLegacyAminoCodec(govcodec.Amino) 13 | RegisterLegacyAminoCodec(groupcodec.Amino) 14 | 15 | } 16 | 17 | The codec instance is put inside this package and not the x/gov/types package in order to avoid any dependency cycle. 18 | */ 19 | package codec 20 | -------------------------------------------------------------------------------- /x/gov/exported/exported.go: -------------------------------------------------------------------------------- 1 | package exported 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" 6 | ) 7 | 8 | type ( 9 | ParamSet = paramtypes.ParamSet 10 | 11 | // Subspace defines an interface that implements the legacy x/params Subspace 12 | // type. 13 | // 14 | // NOTE: This is used solely for migration of x/params managed parameters. 15 | ParamSubspace interface { 16 | Get(ctx sdk.Context, key []byte, ptr interface{}) 17 | } 18 | ) 19 | -------------------------------------------------------------------------------- /x/gov/keeper/constitution.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | 6 | "github.com/atomone-hub/atomone/x/gov/types" 7 | ) 8 | 9 | func (keeper Keeper) GetConstitution(ctx sdk.Context) (constitution string) { 10 | store := ctx.KVStore(keeper.storeKey) 11 | bz := store.Get(types.KeyConstitution) 12 | 13 | return string(bz) 14 | } 15 | 16 | func (keeper Keeper) SetConstitution(ctx sdk.Context, constitution string) { 17 | store := ctx.KVStore(keeper.storeKey) 18 | store.Set(types.KeyConstitution, []byte(constitution)) 19 | } 20 | 21 | // ApplyConstitutionAmendment applies the amendment as a patch against the current constitution 22 | // and returns the updated constitution. If the amendment cannot be applied cleanly, an error is returned. 23 | func (k Keeper) ApplyConstitutionAmendment(ctx sdk.Context, amendment string) (updatedConstitution string, err error) { 24 | if amendment == "" { 25 | return "", types.ErrInvalidConstitutionAmendment.Wrap("amendment cannot be empty") 26 | } 27 | 28 | currentConstitution := k.GetConstitution(ctx) 29 | updatedConstitution, err = types.ApplyUnifiedDiff(currentConstitution, amendment) 30 | if err != nil { 31 | return "", types.ErrInvalidConstitutionAmendment.Wrapf("failed to apply amendment: %v", err) 32 | } 33 | 34 | return updatedConstitution, nil 35 | } 36 | -------------------------------------------------------------------------------- /x/gov/keeper/constitution_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestApplyConstitutionAmendment(t *testing.T) { 10 | govKeeper, _, _, ctx := setupGovKeeper(t) 11 | 12 | tests := []struct { 13 | name string 14 | initialConstitution string 15 | amendment string 16 | expectedResult string 17 | expectError bool 18 | }{ 19 | { 20 | name: "failed patch application", 21 | initialConstitution: "Hello World", 22 | amendment: "Hi World", 23 | expectError: true, 24 | }, 25 | { 26 | name: "successful patch application", 27 | initialConstitution: "Hello\nWorld", 28 | amendment: "@@ -1,2 +1,2 @@\n-Hello\n+Hi\n World", 29 | expectError: false, 30 | expectedResult: "Hi\nWorld", 31 | }, 32 | { 33 | name: "successful patch application with multiple hunks", 34 | initialConstitution: "Line one\nLine two\nLine three\nLine four\nLine five\nLine six\nLine seven\nLine eight\nLine nine", 35 | amendment: "--- src\n+++ dst\n@@ -1,2 +1,2 @@\n-Line one\n+Line one modified\n Line two\n@@ -8,2 +8,2 @@\n Line eight\n-Line nine\n+Line nine modified", 36 | expectError: false, 37 | expectedResult: "Line one modified\nLine two\nLine three\nLine four\nLine five\nLine six\nLine seven\nLine eight\nLine nine modified", 38 | }, 39 | } 40 | 41 | for _, tt := range tests { 42 | t.Run(tt.name, func(t *testing.T) { 43 | govKeeper.SetConstitution(ctx, tt.initialConstitution) 44 | updatedConstitution, err := govKeeper.ApplyConstitutionAmendment(ctx, tt.amendment) 45 | if tt.expectError { 46 | require.Error(t, err) 47 | } else { 48 | require.NoError(t, err) 49 | require.Equal(t, tt.expectedResult, updatedConstitution) 50 | } 51 | }) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /x/gov/keeper/export_test.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import sdk "github.com/cosmos/cosmos-sdk/types" 4 | 5 | // ValidateInitialDeposit is a helper function used only in deposit tests which returns the same 6 | // functionality of validateInitialDeposit private function. 7 | func (k Keeper) ValidateInitialDeposit(ctx sdk.Context, initialDeposit sdk.Coins) error { 8 | return k.validateInitialDeposit(ctx, initialDeposit) 9 | } 10 | -------------------------------------------------------------------------------- /x/gov/keeper/internal_test.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import "github.com/atomone-hub/atomone/x/gov/types" 4 | 5 | // UnsafeSetHooks updates the gov keeper's hooks, overriding any potential 6 | // pre-existing hooks. 7 | // WARNING: this function should only be used in tests. 8 | func UnsafeSetHooks(k *Keeper, h types.GovHooks) { 9 | k.hooks = h 10 | } 11 | -------------------------------------------------------------------------------- /x/gov/keeper/invariants.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | // DONTCOVER 4 | 5 | import ( 6 | "fmt" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | 10 | "github.com/atomone-hub/atomone/x/gov/types" 11 | v1 "github.com/atomone-hub/atomone/x/gov/types/v1" 12 | ) 13 | 14 | // RegisterInvariants registers all governance invariants 15 | func RegisterInvariants(ir sdk.InvariantRegistry, keeper *Keeper, bk types.BankKeeper) { 16 | ir.RegisterRoute(types.ModuleName, "module-account", ModuleAccountInvariant(keeper, bk)) 17 | } 18 | 19 | // AllInvariants runs all invariants of the governance module 20 | func AllInvariants(keeper *Keeper, bk types.BankKeeper) sdk.Invariant { 21 | return func(ctx sdk.Context) (string, bool) { 22 | return ModuleAccountInvariant(keeper, bk)(ctx) 23 | } 24 | } 25 | 26 | // ModuleAccountInvariant checks that the module account coins reflects the sum of 27 | // deposit amounts held on store. 28 | func ModuleAccountInvariant(keeper *Keeper, bk types.BankKeeper) sdk.Invariant { 29 | return func(ctx sdk.Context) (string, bool) { 30 | var expectedDeposits sdk.Coins 31 | 32 | keeper.IterateAllDeposits(ctx, func(deposit v1.Deposit) bool { 33 | expectedDeposits = expectedDeposits.Add(deposit.Amount...) 34 | return false 35 | }) 36 | 37 | macc := keeper.GetGovernanceAccount(ctx) 38 | balances := bk.GetAllBalances(ctx, macc.GetAddress()) 39 | 40 | // Require that the deposit balances are <= than the x/gov module's total 41 | // balances. We use the <= operator since external funds can be sent to x/gov 42 | // module's account and so the balance can be larger. 43 | broken := !balances.IsAllGTE(expectedDeposits) 44 | 45 | return sdk.FormatInvariant(types.ModuleName, "deposits", 46 | fmt.Sprintf("\tgov ModuleAccount coins: %s\n\tsum of deposit amounts: %s\n", 47 | balances, expectedDeposits)), broken 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /x/gov/keeper/migrations.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | 6 | "github.com/atomone-hub/atomone/x/gov/exported" 7 | v5 "github.com/atomone-hub/atomone/x/gov/migrations/v5" 8 | ) 9 | 10 | // Migrator is a struct for handling in-place store migrations. 11 | type Migrator struct { 12 | keeper *Keeper 13 | legacySubspace exported.ParamSubspace 14 | } 15 | 16 | // NewMigrator returns a new Migrator. 17 | func NewMigrator(keeper *Keeper, legacySubspace exported.ParamSubspace) Migrator { 18 | return Migrator{ 19 | keeper: keeper, 20 | legacySubspace: legacySubspace, 21 | } 22 | } 23 | 24 | // Migrate4to5 migrates the store from version 4 to 5. 25 | func (m Migrator) Migrate4to5(ctx sdk.Context) error { 26 | return v5.MigrateStore(ctx, m.keeper.storeKey, m.keeper.cdc) 27 | } 28 | -------------------------------------------------------------------------------- /x/gov/keeper/params.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | 6 | "github.com/atomone-hub/atomone/x/gov/types" 7 | v1 "github.com/atomone-hub/atomone/x/gov/types/v1" 8 | ) 9 | 10 | // SetParams sets the gov module's parameters. 11 | func (k Keeper) SetParams(ctx sdk.Context, params v1.Params) error { 12 | store := ctx.KVStore(k.storeKey) 13 | bz, err := k.cdc.Marshal(¶ms) 14 | if err != nil { 15 | return err 16 | } 17 | store.Set(types.ParamsKey, bz) 18 | 19 | return nil 20 | } 21 | 22 | // GetParams gets the gov module's parameters. 23 | func (k Keeper) GetParams(clientCtx sdk.Context) (params v1.Params) { 24 | store := clientCtx.KVStore(k.storeKey) 25 | bz := store.Get(types.ParamsKey) 26 | if bz == nil { 27 | return params 28 | } 29 | 30 | k.cdc.MustUnmarshal(bz, ¶ms) 31 | return params 32 | } 33 | -------------------------------------------------------------------------------- /x/gov/migrations/v3/keys.go: -------------------------------------------------------------------------------- 1 | package v3 2 | 3 | const ( 4 | // ModuleName is the name of the module 5 | ModuleName = "gov" 6 | ) 7 | -------------------------------------------------------------------------------- /x/gov/migrations/v5/store.go: -------------------------------------------------------------------------------- 1 | package v5 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | storetypes "github.com/cosmos/cosmos-sdk/store/types" 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | 8 | govv1 "github.com/atomone-hub/atomone/x/gov/types/v1" 9 | ) 10 | 11 | var ParamsKey = []byte{0x30} 12 | 13 | // Addition of the dynamic-deposit parameters. 14 | // Addition of the burnDepositNoThreshold parameter. 15 | func MigrateStore(ctx sdk.Context, storeKey storetypes.StoreKey, cdc codec.BinaryCodec) error { 16 | store := ctx.KVStore(storeKey) 17 | paramsBz := store.Get(ParamsKey) 18 | 19 | var params govv1.Params 20 | cdc.MustUnmarshal(paramsBz, ¶ms) 21 | 22 | defaultParams := govv1.DefaultParams() 23 | params.MinDepositThrottler = defaultParams.MinDepositThrottler 24 | params.MinInitialDepositThrottler = defaultParams.MinInitialDepositThrottler 25 | params.BurnDepositNoThreshold = defaultParams.BurnDepositNoThreshold 26 | 27 | bz, err := cdc.Marshal(¶ms) 28 | if err != nil { 29 | return err 30 | } 31 | store.Set(ParamsKey, bz) 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /x/gov/migrations/v5/store_test.go: -------------------------------------------------------------------------------- 1 | package v5_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | storetypes "github.com/cosmos/cosmos-sdk/store/types" 9 | "github.com/cosmos/cosmos-sdk/testutil" 10 | moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" 11 | "github.com/cosmos/cosmos-sdk/x/bank" 12 | 13 | "github.com/atomone-hub/atomone/x/gov" 14 | v5 "github.com/atomone-hub/atomone/x/gov/migrations/v5" 15 | govv1 "github.com/atomone-hub/atomone/x/gov/types/v1" 16 | ) 17 | 18 | func TestMigrateStore(t *testing.T) { 19 | cdc := moduletestutil.MakeTestEncodingConfig(gov.AppModuleBasic{}, bank.AppModuleBasic{}).Codec 20 | govKey := storetypes.NewKVStoreKey("gov") 21 | ctx := testutil.DefaultContext(govKey, storetypes.NewTransientStoreKey("transient_test")) 22 | store := ctx.KVStore(govKey) 23 | 24 | var params govv1.Params 25 | bz := store.Get(v5.ParamsKey) 26 | require.NoError(t, cdc.Unmarshal(bz, ¶ms)) 27 | require.NotNil(t, params) 28 | require.Nil(t, params.MinDepositThrottler) 29 | require.Nil(t, params.MinInitialDepositThrottler) 30 | require.Equal(t, "", params.BurnDepositNoThreshold) 31 | 32 | // Run migrations. 33 | err := v5.MigrateStore(ctx, govKey, cdc) 34 | require.NoError(t, err) 35 | 36 | // Check params 37 | bz = store.Get(v5.ParamsKey) 38 | require.NoError(t, cdc.Unmarshal(bz, ¶ms)) 39 | require.NotNil(t, params) 40 | require.Equal(t, govv1.DefaultParams().MinDepositThrottler, params.MinDepositThrottler) 41 | require.Equal(t, govv1.DefaultParams().MinInitialDepositThrottler, params.MinInitialDepositThrottler) 42 | require.Equal(t, govv1.DefaultParams().BurnDepositNoThreshold, params.BurnDepositNoThreshold) 43 | } 44 | -------------------------------------------------------------------------------- /x/gov/simulation/decoder.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "fmt" 7 | 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | "github.com/cosmos/cosmos-sdk/types/kv" 10 | 11 | "github.com/atomone-hub/atomone/x/gov/types" 12 | "github.com/atomone-hub/atomone/x/gov/types/v1beta1" 13 | ) 14 | 15 | // NewDecodeStore returns a decoder function closure that unmarshals the KVPair's 16 | // Value to the corresponding gov type. 17 | func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string { 18 | return func(kvA, kvB kv.Pair) string { 19 | switch { 20 | case bytes.Equal(kvA.Key[:1], types.ProposalsKeyPrefix): 21 | var proposalA v1beta1.Proposal 22 | err := cdc.Unmarshal(kvA.Value, &proposalA) 23 | if err != nil { 24 | panic(err) 25 | } 26 | var proposalB v1beta1.Proposal 27 | err = cdc.Unmarshal(kvB.Value, &proposalB) 28 | if err != nil { 29 | panic(err) 30 | } 31 | return fmt.Sprintf("%v\n%v", proposalA, proposalB) 32 | 33 | case bytes.Equal(kvA.Key[:1], types.ActiveProposalQueuePrefix), 34 | bytes.Equal(kvA.Key[:1], types.InactiveProposalQueuePrefix), 35 | bytes.Equal(kvA.Key[:1], types.ProposalIDKey): 36 | proposalIDA := binary.LittleEndian.Uint64(kvA.Value) 37 | proposalIDB := binary.LittleEndian.Uint64(kvB.Value) 38 | return fmt.Sprintf("proposalIDA: %d\nProposalIDB: %d", proposalIDA, proposalIDB) 39 | 40 | case bytes.Equal(kvA.Key[:1], types.DepositsKeyPrefix): 41 | var depositA, depositB v1beta1.Deposit 42 | cdc.MustUnmarshal(kvA.Value, &depositA) 43 | cdc.MustUnmarshal(kvB.Value, &depositB) 44 | return fmt.Sprintf("%v\n%v", depositA, depositB) 45 | 46 | case bytes.Equal(kvA.Key[:1], types.VotesKeyPrefix): 47 | var voteA, voteB v1beta1.Vote 48 | cdc.MustUnmarshal(kvA.Value, &voteA) 49 | cdc.MustUnmarshal(kvB.Value, &voteB) 50 | return fmt.Sprintf("%v\n%v", voteA, voteB) 51 | 52 | case bytes.Equal(kvA.Key[:1], types.VotingPeriodProposalKeyPrefix): 53 | return fmt.Sprintf("%v\n%v", kvA.Value, kvB.Value) 54 | 55 | default: 56 | panic(fmt.Sprintf("invalid governance key prefix %X", kvA.Key[:1])) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /x/gov/testutil/configurator.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | appv1alpha1 "cosmossdk.io/api/cosmos/app/v1alpha1" 5 | "cosmossdk.io/core/appconfig" 6 | 7 | "github.com/cosmos/cosmos-sdk/testutil/configurator" 8 | 9 | govmodulev1 "github.com/atomone-hub/atomone/api/atomone/gov/module/v1" 10 | ) 11 | 12 | func GovModule() configurator.ModuleOption { 13 | return func(config *configurator.Config) { 14 | config.ModuleConfigs["gov"] = &appv1alpha1.ModuleConfig{ 15 | Name: "gov", 16 | Config: appconfig.WrapAny(&govmodulev1.Module{}), 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /x/gov/testutil/expected_keepers.go: -------------------------------------------------------------------------------- 1 | // This file only used to generate mocks 2 | 3 | package testutil 4 | 5 | import ( 6 | math "cosmossdk.io/math" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 10 | bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" 11 | 12 | "github.com/atomone-hub/atomone/x/gov/types" 13 | ) 14 | 15 | // AccountKeeper extends gov's actual expected AccountKeeper with additional 16 | // methods used in tests. 17 | type AccountKeeper interface { 18 | types.AccountKeeper 19 | 20 | IterateAccounts(ctx sdk.Context, cb func(account authtypes.AccountI) (stop bool)) 21 | } 22 | 23 | // BankKeeper extends gov's actual expected BankKeeper with additional 24 | // methods used in tests. 25 | type BankKeeper interface { 26 | bankkeeper.Keeper 27 | } 28 | 29 | // StakingKeeper extends gov's actual expected StakingKeeper with additional 30 | // methods used in tests. 31 | type StakingKeeper interface { 32 | types.StakingKeeper 33 | 34 | BondDenom(ctx sdk.Context) string 35 | TokensFromConsensusPower(ctx sdk.Context, power int64) math.Int 36 | } 37 | -------------------------------------------------------------------------------- /x/gov/types/config.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // Config is a config struct used for intialising the gov module to avoid using globals. 4 | type Config struct { 5 | // MaxMetadataLen defines the maximum proposal metadata length. 6 | MaxMetadataLen uint64 7 | } 8 | 9 | // DefaultConfig returns the default config for gov. 10 | func DefaultConfig() Config { 11 | return Config{ 12 | MaxMetadataLen: 255, 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /x/gov/types/events.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // Governance module event types 4 | const ( 5 | EventTypeSubmitProposal = "submit_proposal" 6 | EventTypeProposalDeposit = "proposal_deposit" 7 | EventTypeProposalVote = "proposal_vote" 8 | EventTypeInactiveProposal = "inactive_proposal" 9 | EventTypeActiveProposal = "active_proposal" 10 | EventTypeSignalProposal = "signal_proposal" 11 | EventTypeQuorumCheck = "quorum_check" 12 | EventTypeMinDepositChange = "min_deposit_change" 13 | EventTypeMinInitialDepositChange = "min_initial_deposit_change" 14 | 15 | AttributeKeyVoter = "voter" 16 | AttributeKeyProposalResult = "proposal_result" 17 | AttributeKeyOption = "option" 18 | AttributeKeyProposalID = "proposal_id" 19 | AttributeKeyProposalMessages = "proposal_messages" // Msg type_urls in the proposal 20 | AttributeKeyVotingPeriodStart = "voting_period_start" 21 | AttributeValueProposalDropped = "proposal_dropped" // didn't meet min deposit 22 | AttributeValueProposalPassed = "proposal_passed" // met vote quorum 23 | AttributeValueProposalRejected = "proposal_rejected" // didn't meet vote quorum 24 | AttributeValueProposalFailed = "proposal_failed" // error on proposal handler 25 | AttributeKeyProposalType = "proposal_type" 26 | AttributeSignalTitle = "signal_title" 27 | AttributeSignalDescription = "signal_description" 28 | AttributeValueProposalQuorumMet = "proposal_quorum_met" // met quorum 29 | AttributeValueProposalQuorumNotMet = "proposal_quorum_not_met" // didn't meet quorum 30 | AttributeValueProposalQuorumCheckSkipped = "proposal_quorum_check_skipped" // skipped quorum check 31 | AttributeKeyNewMinDeposit = "new_min_deposit" // new min deposit value 32 | AttributeKeyLastMinDeposit = "last_min_deposit" // last min deposit value 33 | AttributeKeyNewMinInitialDeposit = "new_min_initial_deposit" // new min initial deposit value 34 | AttributeKeyLastMinInitialDeposit = "last_min_initial_deposit" // last min initial deposit value 35 | ) 36 | -------------------------------------------------------------------------------- /x/gov/types/hooks.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | ) 6 | 7 | var _ GovHooks = MultiGovHooks{} 8 | 9 | // combine multiple governance hooks, all hook functions are run in array sequence 10 | type MultiGovHooks []GovHooks 11 | 12 | func NewMultiGovHooks(hooks ...GovHooks) MultiGovHooks { 13 | return hooks 14 | } 15 | 16 | func (h MultiGovHooks) AfterProposalSubmission(ctx sdk.Context, proposalID uint64) { 17 | for i := range h { 18 | h[i].AfterProposalSubmission(ctx, proposalID) 19 | } 20 | } 21 | 22 | func (h MultiGovHooks) AfterProposalDeposit(ctx sdk.Context, proposalID uint64, depositorAddr sdk.AccAddress) { 23 | for i := range h { 24 | h[i].AfterProposalDeposit(ctx, proposalID, depositorAddr) 25 | } 26 | } 27 | 28 | func (h MultiGovHooks) AfterProposalVote(ctx sdk.Context, proposalID uint64, voterAddr sdk.AccAddress) { 29 | for i := range h { 30 | h[i].AfterProposalVote(ctx, proposalID, voterAddr) 31 | } 32 | } 33 | 34 | func (h MultiGovHooks) AfterProposalFailedMinDeposit(ctx sdk.Context, proposalID uint64) { 35 | for i := range h { 36 | h[i].AfterProposalFailedMinDeposit(ctx, proposalID) 37 | } 38 | } 39 | 40 | func (h MultiGovHooks) AfterProposalVotingPeriodEnded(ctx sdk.Context, proposalID uint64) { 41 | for i := range h { 42 | h[i].AfterProposalVotingPeriodEnded(ctx, proposalID) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /x/gov/types/keys_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | ) 12 | 13 | var addr = sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) 14 | 15 | func TestProposalKeys(t *testing.T) { 16 | // key proposal 17 | key := ProposalKey(1) 18 | proposalID := SplitProposalKey(key) 19 | require.Equal(t, int(proposalID), 1) 20 | 21 | // key active proposal queue 22 | now := time.Now() 23 | key = ActiveProposalQueueKey(3, now) 24 | proposalID, expTime := SplitActiveProposalQueueKey(key) 25 | require.Equal(t, int(proposalID), 3) 26 | require.True(t, now.Equal(expTime)) 27 | 28 | // key inactive proposal queue 29 | key = InactiveProposalQueueKey(3, now) 30 | proposalID, expTime = SplitInactiveProposalQueueKey(key) 31 | require.Equal(t, int(proposalID), 3) 32 | require.True(t, now.Equal(expTime)) 33 | 34 | // invalid key 35 | require.Panics(t, func() { SplitProposalKey([]byte("test")) }) 36 | require.Panics(t, func() { SplitInactiveProposalQueueKey([]byte("test")) }) 37 | } 38 | 39 | func TestDepositKeys(t *testing.T) { 40 | key := DepositsKey(2) 41 | proposalID := SplitProposalKey(key) 42 | require.Equal(t, int(proposalID), 2) 43 | 44 | key = DepositKey(2, addr) 45 | proposalID, depositorAddr := SplitKeyDeposit(key) 46 | require.Equal(t, int(proposalID), 2) 47 | require.Equal(t, addr, depositorAddr) 48 | } 49 | 50 | func TestVoteKeys(t *testing.T) { 51 | key := VotesKey(2) 52 | proposalID := SplitProposalKey(key) 53 | require.Equal(t, int(proposalID), 2) 54 | 55 | key = VoteKey(2, addr) 56 | proposalID, voterAddr := SplitKeyDeposit(key) 57 | require.Equal(t, int(proposalID), 2) 58 | require.Equal(t, addr, voterAddr) 59 | } 60 | -------------------------------------------------------------------------------- /x/gov/types/metadata.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // ProposalMetadata is the metadata of a proposal 4 | // This metadata is supposed to live off-chain when submitted in a proposal 5 | type ProposalMetadata struct { 6 | Title string `json:"title"` 7 | Authors []string `json:"authors"` 8 | Summary string `json:"summary"` 9 | Details string `json:"details"` 10 | ProposalForumUrl string `json:"proposal_forum_url"` //nolint:revive // named 'Url' instead of 'URL' for avoiding the camel case split 11 | VoteOptionContext string `json:"vote_option_context"` 12 | } 13 | -------------------------------------------------------------------------------- /x/gov/types/v1/content.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/cosmos/gogoproto/proto" 7 | 8 | codectypes "github.com/cosmos/cosmos-sdk/codec/types" 9 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 10 | 11 | "github.com/atomone-hub/atomone/x/gov/types/v1beta1" 12 | ) 13 | 14 | // NewLegacyContent creates a new MsgExecLegacyContent from a legacy Content 15 | // interface. 16 | func NewLegacyContent(content v1beta1.Content, authority string) (*MsgExecLegacyContent, error) { 17 | msg, ok := content.(proto.Message) 18 | if !ok { 19 | return nil, fmt.Errorf("%T does not implement proto.Message", content) 20 | } 21 | 22 | any, err := codectypes.NewAnyWithValue(msg) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return NewMsgExecLegacyContent(any, authority), nil 28 | } 29 | 30 | // LegacyContentFromMessage extracts the legacy Content interface from a 31 | // MsgExecLegacyContent. 32 | func LegacyContentFromMessage(msg *MsgExecLegacyContent) (v1beta1.Content, error) { 33 | content, ok := msg.Content.GetCachedValue().(v1beta1.Content) 34 | if !ok { 35 | return nil, sdkerrors.ErrInvalidType.Wrapf("expected %T, got %T", (*v1beta1.Content)(nil), msg.Content.GetCachedValue()) 36 | } 37 | 38 | return content, nil 39 | } 40 | -------------------------------------------------------------------------------- /x/gov/types/v1/deposit.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "fmt" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | // NewDeposit creates a new Deposit instance 10 | // 11 | //nolint:interfacer 12 | func NewDeposit(proposalID uint64, depositor sdk.AccAddress, amount sdk.Coins) Deposit { 13 | return Deposit{proposalID, depositor.String(), amount} 14 | } 15 | 16 | // Deposits is a collection of Deposit objects 17 | type Deposits []*Deposit 18 | 19 | // Equal returns true if two slices (order-dependant) of deposits are equal. 20 | func (d Deposits) Equal(other Deposits) bool { 21 | if len(d) != len(other) { 22 | return false 23 | } 24 | 25 | for i, deposit := range d { 26 | if deposit.String() != other[i].String() { 27 | return false 28 | } 29 | } 30 | 31 | return true 32 | } 33 | 34 | // String implements stringer interface 35 | func (d Deposits) String() string { 36 | if len(d) == 0 { 37 | return "[]" 38 | } 39 | out := fmt.Sprintf("Deposits for Proposal %d:", d[0].ProposalId) 40 | for _, dep := range d { 41 | out += fmt.Sprintf("\n %s: %s", dep.Depositor, dep.Amount) 42 | } 43 | return out 44 | } 45 | -------------------------------------------------------------------------------- /x/gov/types/v1/min_deposit.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import sdk "github.com/cosmos/cosmos-sdk/types" 4 | 5 | func GetNewMinDeposit(minDepositFloor, lastMinDeposit sdk.Coins, percChange sdk.Dec) sdk.Coins { 6 | newMinDeposit := sdk.Coins{} 7 | minDepositFloorDenomsSeen := make(map[string]bool) 8 | for _, lastMinDepositCoin := range lastMinDeposit { 9 | minDepositFloorCoinAmt := minDepositFloor.AmountOf(lastMinDepositCoin.Denom) 10 | if minDepositFloorCoinAmt.IsZero() { 11 | // minDepositFloor was changed since last update, 12 | // and this coin was removed. 13 | // reflect this also in the current min initial deposit, 14 | // i.e. remove this coin 15 | continue 16 | } 17 | minDepositFloorDenomsSeen[lastMinDepositCoin.Denom] = true 18 | minDepositCoinAmt := lastMinDepositCoin.Amount.ToLegacyDec().Mul(percChange).TruncateInt() 19 | if minDepositCoinAmt.LT(minDepositFloorCoinAmt) { 20 | newMinDeposit = append(newMinDeposit, sdk.NewCoin(lastMinDepositCoin.Denom, minDepositFloorCoinAmt)) 21 | } else { 22 | newMinDeposit = append(newMinDeposit, sdk.NewCoin(lastMinDepositCoin.Denom, minDepositCoinAmt)) 23 | } 24 | } 25 | 26 | // make sure any new denoms in minDepositFloor are added to minDeposit 27 | for _, minDepositFloorCoin := range minDepositFloor { 28 | if _, seen := minDepositFloorDenomsSeen[minDepositFloorCoin.Denom]; !seen { 29 | minDepositCoinAmt := minDepositFloorCoin.Amount.ToLegacyDec().Mul(percChange).TruncateInt() 30 | if minDepositCoinAmt.LT(minDepositFloorCoin.Amount) { 31 | newMinDeposit = append(newMinDeposit, minDepositFloorCoin) 32 | } else { 33 | newMinDeposit = append(newMinDeposit, sdk.NewCoin(minDepositFloorCoin.Denom, minDepositCoinAmt)) 34 | } 35 | } 36 | } 37 | 38 | return newMinDeposit 39 | } 40 | -------------------------------------------------------------------------------- /x/gov/types/v1/proposals_test.go: -------------------------------------------------------------------------------- 1 | package v1_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/require" 9 | 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | 12 | v1 "github.com/atomone-hub/atomone/x/gov/types/v1" 13 | "github.com/atomone-hub/atomone/x/gov/types/v1beta1" 14 | ) 15 | 16 | func TestProposalStatus_Format(t *testing.T) { 17 | statusDepositPeriod, _ := v1.ProposalStatusFromString("PROPOSAL_STATUS_DEPOSIT_PERIOD") 18 | tests := []struct { 19 | pt v1.ProposalStatus 20 | sprintFArgs string 21 | expectedStringOutput string 22 | }{ 23 | {statusDepositPeriod, "%s", "PROPOSAL_STATUS_DEPOSIT_PERIOD"}, 24 | {statusDepositPeriod, "%v", "1"}, 25 | } 26 | for _, tt := range tests { 27 | got := fmt.Sprintf(tt.sprintFArgs, tt.pt) 28 | require.Equal(t, tt.expectedStringOutput, got) 29 | } 30 | } 31 | 32 | // TestNestedAnys tests that we can call .String() on a struct with nested Anys. 33 | // Here, we're creating a proposal which has a Msg (1st any) with a legacy 34 | // content (2nd any). 35 | func TestNestedAnys(t *testing.T) { 36 | // TODO https://github.com/cosmos/cosmos-sdk/issues/10965 37 | t.Skip() 38 | testProposal := v1beta1.NewTextProposal("Proposal", "testing proposal") 39 | msgContent, err := v1.NewLegacyContent(testProposal, "cosmos1govacct") 40 | require.NoError(t, err) 41 | proposal, err := v1.NewProposal([]sdk.Msg{msgContent}, 1, time.Now(), time.Now(), "", "title", "summary", sdk.AccAddress("cosmos1ghekyjucln7y67ntx7cf27m9dpuxxemn4c8g4r")) 42 | require.NoError(t, err) 43 | 44 | require.Equal(t, "TODO Fix panic here", proposal.String()) 45 | } 46 | -------------------------------------------------------------------------------- /x/gov/types/v1/quorum_check.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | time "time" 5 | ) 6 | 7 | func NewQuorumCheckQueueEntry(quorumTimeoutTime time.Time, quorumCheckCount uint64) QuorumCheckQueueEntry { 8 | return QuorumCheckQueueEntry{ 9 | QuorumTimeoutTime: &quorumTimeoutTime, 10 | QuorumCheckCount: quorumCheckCount, 11 | QuorumChecksDone: 0, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /x/gov/types/v1/tally.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | "cosmossdk.io/math" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | // NewTallyResult creates a new TallyResult instance 10 | func NewTallyResult(yes, abstain, no math.Int) TallyResult { 11 | return TallyResult{ 12 | YesCount: yes.String(), 13 | AbstainCount: abstain.String(), 14 | NoCount: no.String(), 15 | } 16 | } 17 | 18 | // NewTallyResultFromMap creates a new TallyResult instance from a Option -> Dec map 19 | func NewTallyResultFromMap(results map[VoteOption]sdk.Dec) TallyResult { 20 | return NewTallyResult( 21 | results[OptionYes].TruncateInt(), 22 | results[OptionAbstain].TruncateInt(), 23 | results[OptionNo].TruncateInt(), 24 | ) 25 | } 26 | 27 | // EmptyTallyResult returns an empty TallyResult. 28 | func EmptyTallyResult() TallyResult { 29 | return NewTallyResult(math.ZeroInt(), math.ZeroInt(), math.ZeroInt()) 30 | } 31 | 32 | // Equals returns if two tally results are equal. 33 | func (tr TallyResult) Equals(comp TallyResult) bool { 34 | return tr.YesCount == comp.YesCount && 35 | tr.AbstainCount == comp.AbstainCount && 36 | tr.NoCount == comp.NoCount 37 | } 38 | -------------------------------------------------------------------------------- /x/gov/types/v1beta1/content.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | sdkgovtypes "github.com/cosmos/cosmos-sdk/x/gov/types/v1beta1" 6 | ) 7 | 8 | // Content defines an interface that a proposal must implement. It contains 9 | // information such as the title and description along with the type and routing 10 | // information for the appropriate handler to process the proposal. Content can 11 | // have additional fields, which will handled by a proposal's Handler. 12 | type Content interface { 13 | GetTitle() string 14 | GetDescription() string 15 | ProposalRoute() string 16 | ProposalType() string 17 | ValidateBasic() error 18 | String() string 19 | } 20 | 21 | // Handler defines a function that handles a proposal after it has passed the 22 | // governance process. 23 | type Handler func(ctx sdk.Context, content Content) error 24 | 25 | // WrapSDKHandler converts a Cosmos SDK gov Handler to GovGen gov Handler 26 | func WrapSDKHandler(sdkHandler sdkgovtypes.Handler) Handler { 27 | return func(ctx sdk.Context, content Content) error { 28 | return sdkHandler(ctx, content) 29 | } 30 | } 31 | 32 | type HandlerRoute struct { 33 | Handler Handler 34 | RouteKey string 35 | } 36 | 37 | // IsManyPerContainerType implements the depinject.ManyPerContainerType interface. 38 | func (HandlerRoute) IsManyPerContainerType() {} 39 | -------------------------------------------------------------------------------- /x/gov/types/v1beta1/deposit.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | "fmt" 5 | 6 | "sigs.k8s.io/yaml" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | ) 10 | 11 | // NewDeposit creates a new Deposit instance 12 | // 13 | //nolint:interfacer 14 | func NewDeposit(proposalID uint64, depositor sdk.AccAddress, amount sdk.Coins) Deposit { 15 | return Deposit{proposalID, depositor.String(), amount} 16 | } 17 | 18 | func (d Deposit) String() string { 19 | out, _ := yaml.Marshal(d) 20 | return string(out) 21 | } 22 | 23 | // Empty returns whether a deposit is empty. 24 | func (d Deposit) Empty() bool { 25 | return d.String() == Deposit{}.String() 26 | } 27 | 28 | // Deposits is a collection of Deposit objects 29 | type Deposits []Deposit 30 | 31 | // Equal returns true if two slices (order-dependant) of deposits are equal. 32 | func (d Deposits) Equal(other Deposits) bool { 33 | if len(d) != len(other) { 34 | return false 35 | } 36 | 37 | for i, deposit := range d { 38 | if deposit.String() != other[i].String() { 39 | return false 40 | } 41 | } 42 | 43 | return true 44 | } 45 | 46 | // String implements stringer interface 47 | func (d Deposits) String() string { 48 | if len(d) == 0 { 49 | return "[]" 50 | } 51 | out := fmt.Sprintf("Deposits for Proposal %d:", d[0].ProposalId) 52 | for _, dep := range d { 53 | out += fmt.Sprintf("\n %s: %s", dep.Depositor, dep.Amount) 54 | } 55 | return out 56 | } 57 | -------------------------------------------------------------------------------- /x/gov/types/v1beta1/proposals_test.go: -------------------------------------------------------------------------------- 1 | package v1beta1_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "github.com/atomone-hub/atomone/x/gov/types/v1beta1" 10 | ) 11 | 12 | func TestProposalStatus_Format(t *testing.T) { 13 | statusDepositPeriod, _ := v1beta1.ProposalStatusFromString("PROPOSAL_STATUS_DEPOSIT_PERIOD") 14 | tests := []struct { 15 | pt v1beta1.ProposalStatus 16 | sprintFArgs string 17 | expectedStringOutput string 18 | }{ 19 | {statusDepositPeriod, "%s", "PROPOSAL_STATUS_DEPOSIT_PERIOD"}, 20 | {statusDepositPeriod, "%v", "1"}, 21 | } 22 | for _, tt := range tests { 23 | got := fmt.Sprintf(tt.sprintFArgs, tt.pt) 24 | require.Equal(t, tt.expectedStringOutput, got) 25 | } 26 | } 27 | 28 | func TestContentFromProposalType(t *testing.T) { 29 | tests := []struct { 30 | proposalType string 31 | expectedType string 32 | }{ 33 | { 34 | proposalType: "TextProposal", 35 | expectedType: "", 36 | }, 37 | { 38 | proposalType: "text", 39 | expectedType: v1beta1.ProposalTypeText, 40 | }, 41 | { 42 | proposalType: "Text", 43 | expectedType: v1beta1.ProposalTypeText, 44 | }, 45 | } 46 | 47 | for _, test := range tests { 48 | content, ok := v1beta1.ContentFromProposalType("title", "foo", test.proposalType) 49 | if test.expectedType == "" { 50 | require.False(t, ok) 51 | continue 52 | } 53 | 54 | require.True(t, ok) 55 | require.NotNil(t, content) 56 | require.Equal(t, test.expectedType, content.ProposalType()) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /x/gov/types/v1beta1/router.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | "fmt" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | var _ Router = (*router)(nil) 10 | 11 | // Router implements a governance Handler router. 12 | // 13 | // TODO: Use generic router (ref #3976). 14 | type Router interface { 15 | AddRoute(r string, h Handler) (rtr Router) 16 | HasRoute(r string) bool 17 | GetRoute(path string) (h Handler) 18 | Seal() 19 | } 20 | 21 | type router struct { 22 | routes map[string]Handler 23 | sealed bool 24 | } 25 | 26 | // NewRouter creates a new Router interface instance 27 | func NewRouter() Router { 28 | return &router{ 29 | routes: make(map[string]Handler), 30 | } 31 | } 32 | 33 | // Seal seals the router which prohibits any subsequent route handlers to be 34 | // added. Seal will panic if called more than once. 35 | func (rtr *router) Seal() { 36 | if rtr.sealed { 37 | panic("router already sealed") 38 | } 39 | rtr.sealed = true 40 | } 41 | 42 | // AddRoute adds a governance handler for a given path. It returns the Router 43 | // so AddRoute calls can be linked. It will panic if the router is sealed. 44 | func (rtr *router) AddRoute(path string, h Handler) Router { 45 | if rtr.sealed { 46 | panic("router sealed; cannot add route handler") 47 | } 48 | 49 | if !sdk.IsAlphaNumeric(path) { 50 | panic("route expressions can only contain alphanumeric characters") 51 | } 52 | if rtr.HasRoute(path) { 53 | panic(fmt.Sprintf("route %s has already been initialized", path)) 54 | } 55 | 56 | rtr.routes[path] = h 57 | return rtr 58 | } 59 | 60 | // HasRoute returns true if the router has a path registered or false otherwise. 61 | func (rtr *router) HasRoute(path string) bool { 62 | return rtr.routes[path] != nil 63 | } 64 | 65 | // GetRoute returns a Handler for a given path. 66 | func (rtr *router) GetRoute(path string) Handler { 67 | if !rtr.HasRoute(path) { 68 | panic(fmt.Sprintf("route \"%s\" does not exist", path)) 69 | } 70 | 71 | return rtr.routes[path] 72 | } 73 | -------------------------------------------------------------------------------- /x/gov/types/v1beta1/tally.go: -------------------------------------------------------------------------------- 1 | package v1beta1 2 | 3 | import ( 4 | "sigs.k8s.io/yaml" 5 | 6 | "cosmossdk.io/math" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | ) 10 | 11 | // ValidatorGovInfo used for tallying 12 | type ValidatorGovInfo struct { 13 | Address sdk.ValAddress // address of the validator operator 14 | BondedTokens math.Int // Power of a Validator 15 | DelegatorShares math.LegacyDec // Total outstanding delegator shares 16 | DelegatorDeductions math.LegacyDec // Delegator deductions from validator's delegators voting independently 17 | Vote WeightedVoteOptions // Vote of the validator 18 | } 19 | 20 | // NewValidatorGovInfo creates a ValidatorGovInfo instance 21 | func NewValidatorGovInfo(address sdk.ValAddress, bondedTokens math.Int, delegatorShares, 22 | delegatorDeductions sdk.Dec, options WeightedVoteOptions, 23 | ) ValidatorGovInfo { 24 | return ValidatorGovInfo{ 25 | Address: address, 26 | BondedTokens: bondedTokens, 27 | DelegatorShares: delegatorShares, 28 | DelegatorDeductions: delegatorDeductions, 29 | Vote: options, 30 | } 31 | } 32 | 33 | // NewTallyResult creates a new TallyResult instance 34 | func NewTallyResult(yes, abstain, no, noWithVeto math.Int) TallyResult { 35 | return TallyResult{ 36 | Yes: yes, 37 | Abstain: abstain, 38 | No: no, 39 | NoWithVeto: noWithVeto, 40 | } 41 | } 42 | 43 | // NewTallyResultFromMap creates a new TallyResult instance from a Option -> Dec map 44 | func NewTallyResultFromMap(results map[VoteOption]sdk.Dec) TallyResult { 45 | return NewTallyResult( 46 | results[OptionYes].TruncateInt(), 47 | results[OptionAbstain].TruncateInt(), 48 | results[OptionNo].TruncateInt(), 49 | results[OptionNoWithVeto].TruncateInt(), 50 | ) 51 | } 52 | 53 | // EmptyTallyResult returns an empty TallyResult. 54 | func EmptyTallyResult() TallyResult { 55 | return NewTallyResult(math.ZeroInt(), math.ZeroInt(), math.ZeroInt(), math.ZeroInt()) 56 | } 57 | 58 | // Equals returns if two proposals are equal. 59 | func (tr TallyResult) Equals(comp TallyResult) bool { 60 | return tr.Yes.Equal(comp.Yes) && 61 | tr.Abstain.Equal(comp.Abstain) && 62 | tr.No.Equal(comp.No) && 63 | tr.NoWithVeto.Equal(comp.NoWithVeto) 64 | } 65 | 66 | // String implements stringer interface 67 | func (tr TallyResult) String() string { 68 | out, _ := yaml.Marshal(tr) 69 | return string(out) 70 | } 71 | -------------------------------------------------------------------------------- /x/photon/ante/ante.go: -------------------------------------------------------------------------------- 1 | package ante 2 | 3 | import ( 4 | errorsmod "cosmossdk.io/errors" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 8 | 9 | "github.com/atomone-hub/atomone/x/photon/types" 10 | ) 11 | 12 | var _ sdk.AnteDecorator = ValidateFeeDecorator{} 13 | 14 | type ValidateFeeDecorator struct { 15 | k PhotonKeeper 16 | } 17 | 18 | func NewValidateFeeDecorator(k PhotonKeeper) ValidateFeeDecorator { 19 | return ValidateFeeDecorator{k: k} 20 | } 21 | 22 | // AnteHandle implements the sdk.AnteDecorator interface. 23 | // It returns an error if the tx fee denom is not photon, with some exceptions: 24 | // - tx has no fees or 0 fees. 25 | // - tx messages' type URLs match the `TxFeeExceptions` field of the 26 | // [types.Params]. 27 | func (vfd ValidateFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) { 28 | feeTx, ok := tx.(sdk.FeeTx) 29 | if !ok { 30 | return ctx, errorsmod.Wrap(sdkerrors.ErrTxDecode, "Tx must be a FeeTx") 31 | } 32 | feeCoins := feeTx.GetFee() 33 | if feeCoins.IsZero() { 34 | // Skip if no fees 35 | return next(ctx, tx, simulate) 36 | } 37 | 38 | if AllowsAnyTxFee(tx, vfd.k.GetParams(ctx).TxFeeExceptions) { 39 | // Skip if tx is declared in TxFeeExceptions (any fee coins are allowed). 40 | return next(ctx, tx, simulate) 41 | } 42 | 43 | if len(feeCoins) > 1 { 44 | return ctx, types.ErrTooManyFeeCoins 45 | } 46 | if feeDenom := feeCoins[0].Denom; feeDenom != types.Denom { 47 | // feeDenom not allowed 48 | return ctx, errorsmod.Wrapf(types.ErrInvalidFeeToken, "fee denom %s not allowed", feeDenom) 49 | } 50 | // feeDenom photon is allowed 51 | return next(ctx, tx, simulate) 52 | } 53 | 54 | // AllowsAnyTxFee returns true if all tx messages type URL are presents in 55 | // txFeeExceptions, or if it starts with a wildcard "*". 56 | func AllowsAnyTxFee(tx sdk.Tx, txFeeExceptions []string) bool { 57 | if len(txFeeExceptions) > 0 && txFeeExceptions[0] == "*" { 58 | // wildcard detected, all tx fees are allowed. 59 | return true 60 | } 61 | var anyTxFeeMsgCount int 62 | for _, msg := range tx.GetMsgs() { 63 | msgTypeURL := sdk.MsgTypeURL(msg) 64 | for _, exception := range txFeeExceptions { 65 | if exception == msgTypeURL { 66 | anyTxFeeMsgCount++ 67 | break 68 | } 69 | } 70 | } 71 | return anyTxFeeMsgCount == len(tx.GetMsgs()) 72 | } 73 | -------------------------------------------------------------------------------- /x/photon/ante/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package ante 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | 6 | photontypes "github.com/atomone-hub/atomone/x/photon/types" 7 | ) 8 | 9 | // PhotonKeeper defines the expected photon keeper. 10 | type PhotonKeeper interface { 11 | GetParams(ctx sdk.Context) photontypes.Params 12 | } 13 | -------------------------------------------------------------------------------- /x/photon/ante/expected_keepers_mocks_test.go: -------------------------------------------------------------------------------- 1 | // Code generated by MockGen. DO NOT EDIT. 2 | // Source: x/photon/ante/expected_keepers.go 3 | 4 | // Package ante_test is a generated GoMock package. 5 | package ante_test 6 | 7 | import ( 8 | reflect "reflect" 9 | 10 | types "github.com/atomone-hub/atomone/x/photon/types" 11 | types0 "github.com/cosmos/cosmos-sdk/types" 12 | gomock "github.com/golang/mock/gomock" 13 | ) 14 | 15 | // MockPhotonKeeper is a mock of PhotonKeeper interface. 16 | type MockPhotonKeeper struct { 17 | ctrl *gomock.Controller 18 | recorder *MockPhotonKeeperMockRecorder 19 | } 20 | 21 | // MockPhotonKeeperMockRecorder is the mock recorder for MockPhotonKeeper. 22 | type MockPhotonKeeperMockRecorder struct { 23 | mock *MockPhotonKeeper 24 | } 25 | 26 | // NewMockPhotonKeeper creates a new mock instance. 27 | func NewMockPhotonKeeper(ctrl *gomock.Controller) *MockPhotonKeeper { 28 | mock := &MockPhotonKeeper{ctrl: ctrl} 29 | mock.recorder = &MockPhotonKeeperMockRecorder{mock} 30 | return mock 31 | } 32 | 33 | // EXPECT returns an object that allows the caller to indicate expected use. 34 | func (m *MockPhotonKeeper) EXPECT() *MockPhotonKeeperMockRecorder { 35 | return m.recorder 36 | } 37 | 38 | // GetParams mocks base method. 39 | func (m *MockPhotonKeeper) GetParams(ctx types0.Context) types.Params { 40 | m.ctrl.T.Helper() 41 | ret := m.ctrl.Call(m, "GetParams", ctx) 42 | ret0, _ := ret[0].(types.Params) 43 | return ret0 44 | } 45 | 46 | // GetParams indicates an expected call of GetParams. 47 | func (mr *MockPhotonKeeperMockRecorder) GetParams(ctx interface{}) *gomock.Call { 48 | mr.mock.ctrl.T.Helper() 49 | return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetParams", reflect.TypeOf((*MockPhotonKeeper)(nil).GetParams), ctx) 50 | } 51 | -------------------------------------------------------------------------------- /x/photon/client/cli/query.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/cosmos/cosmos-sdk/client" 9 | "github.com/cosmos/cosmos-sdk/client/flags" 10 | 11 | "github.com/atomone-hub/atomone/x/photon/types" 12 | ) 13 | 14 | // GetQueryCmd returns the cli query commands for this module 15 | func GetQueryCmd(queryRoute string) *cobra.Command { 16 | // Group photon queries under a subcommand 17 | cmd := &cobra.Command{ 18 | Use: types.ModuleName, 19 | Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), 20 | DisableFlagParsing: true, 21 | SuggestionsMinimumDistance: 2, 22 | RunE: client.ValidateCmd, 23 | } 24 | 25 | cmd.AddCommand( 26 | GetQueryParamsCmd(), 27 | GetQueryConversionRateCmd(), 28 | ) 29 | return cmd 30 | } 31 | 32 | func GetQueryParamsCmd() *cobra.Command { 33 | cmd := &cobra.Command{ 34 | Use: "params", 35 | Short: "shows the parameters of the module", 36 | Args: cobra.NoArgs, 37 | RunE: func(cmd *cobra.Command, args []string) error { 38 | clientCtx, err := client.GetClientQueryContext(cmd) 39 | if err != nil { 40 | return err 41 | } 42 | queryClient := types.NewQueryClient(clientCtx) 43 | res, err := queryClient.Params(cmd.Context(), &types.QueryParamsRequest{}) 44 | if err != nil { 45 | return err 46 | } 47 | return clientCtx.PrintProto(res) 48 | }, 49 | } 50 | flags.AddQueryFlagsToCmd(cmd) 51 | return cmd 52 | } 53 | 54 | func GetQueryConversionRateCmd() *cobra.Command { 55 | cmd := &cobra.Command{ 56 | Use: "conversion-rate", 57 | Short: "shows the atone to photon conversion rate", 58 | Args: cobra.NoArgs, 59 | RunE: func(cmd *cobra.Command, args []string) error { 60 | clientCtx, err := client.GetClientQueryContext(cmd) 61 | if err != nil { 62 | return err 63 | } 64 | queryClient := types.NewQueryClient(clientCtx) 65 | res, err := queryClient.ConversionRate(cmd.Context(), &types.QueryConversionRateRequest{}) 66 | if err != nil { 67 | return err 68 | } 69 | return clientCtx.PrintProto(res) 70 | }, 71 | } 72 | flags.AddQueryFlagsToCmd(cmd) 73 | return cmd 74 | } 75 | -------------------------------------------------------------------------------- /x/photon/client/cli/tx.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/cosmos/cosmos-sdk/client" 9 | "github.com/cosmos/cosmos-sdk/client/flags" 10 | "github.com/cosmos/cosmos-sdk/client/tx" 11 | sdk "github.com/cosmos/cosmos-sdk/types" 12 | 13 | "github.com/atomone-hub/atomone/x/photon/types" 14 | ) 15 | 16 | // GetTxCmd returns the transaction commands for this module 17 | func GetTxCmd() *cobra.Command { 18 | cmd := &cobra.Command{ 19 | Use: types.ModuleName, 20 | Short: fmt.Sprintf("%s transactions subcommands", types.ModuleName), 21 | DisableFlagParsing: true, 22 | SuggestionsMinimumDistance: 2, 23 | RunE: client.ValidateCmd, 24 | } 25 | cmd.AddCommand(GetTxMintPhotonCmd()) 26 | return cmd 27 | } 28 | 29 | func GetTxMintPhotonCmd() *cobra.Command { 30 | cmd := &cobra.Command{ 31 | Use: "mint [amount]", 32 | Short: "Broadcast MintPhoton message which burns [amount] and mint photons.", 33 | Args: cobra.ExactArgs(1), 34 | RunE: func(cmd *cobra.Command, args []string) (err error) { 35 | clientCtx, err := client.GetClientTxContext(cmd) 36 | if err != nil { 37 | return err 38 | } 39 | toBurn, err := sdk.ParseCoinNormalized(args[0]) 40 | if err != nil { 41 | return err 42 | } 43 | msg := types.NewMsgMintPhoton( 44 | clientCtx.GetFromAddress(), 45 | toBurn, 46 | ) 47 | if err := msg.ValidateBasic(); err != nil { 48 | return err 49 | } 50 | return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) 51 | }, 52 | } 53 | flags.AddTxFlagsToCmd(cmd) 54 | return cmd 55 | } 56 | -------------------------------------------------------------------------------- /x/photon/genesis.go: -------------------------------------------------------------------------------- 1 | package photon 2 | 3 | import ( 4 | "fmt" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | 8 | "github.com/atomone-hub/atomone/x/photon/keeper" 9 | "github.com/atomone-hub/atomone/x/photon/types" 10 | ) 11 | 12 | // InitGenesis initializes the module's state from a provided genesis state. 13 | func InitGenesis(ctx sdk.Context, k keeper.Keeper, genState types.GenesisState) { 14 | if err := k.SetParams(ctx, genState.Params); err != nil { 15 | panic(fmt.Sprintf("%s module params has not been set", types.ModuleName)) 16 | } 17 | } 18 | 19 | // ExportGenesis returns the module's exported genesis 20 | func ExportGenesis(ctx sdk.Context, k keeper.Keeper) *types.GenesisState { 21 | genesis := types.DefaultGenesis() 22 | genesis.Params = k.GetParams(ctx) 23 | return genesis 24 | } 25 | -------------------------------------------------------------------------------- /x/photon/genesis_test.go: -------------------------------------------------------------------------------- 1 | package photon_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | "github.com/atomone-hub/atomone/x/photon" 9 | "github.com/atomone-hub/atomone/x/photon/testutil" 10 | "github.com/atomone-hub/atomone/x/photon/types" 11 | ) 12 | 13 | func TestGenesis(t *testing.T) { 14 | genesisState := types.GenesisState{ 15 | Params: types.DefaultParams(), 16 | } 17 | k, _, ctx := testutil.SetupPhotonKeeper(t) 18 | 19 | photon.InitGenesis(ctx, *k, genesisState) 20 | got := photon.ExportGenesis(ctx, *k) 21 | 22 | require.NotNil(t, got) 23 | require.Equal(t, genesisState, *got) 24 | } 25 | -------------------------------------------------------------------------------- /x/photon/keeper/grpc_query.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "google.golang.org/grpc/codes" 7 | "google.golang.org/grpc/status" 8 | 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | 11 | "github.com/atomone-hub/atomone/x/photon/types" 12 | ) 13 | 14 | var _ types.QueryServer = Keeper{} 15 | 16 | func (k Keeper) Params(goCtx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { 17 | if req == nil { 18 | return nil, status.Error(codes.InvalidArgument, "invalid request") 19 | } 20 | ctx := sdk.UnwrapSDKContext(goCtx) 21 | 22 | return &types.QueryParamsResponse{Params: k.GetParams(ctx)}, nil 23 | } 24 | 25 | // ConversionRate returns the staking denom to photon conversion ratio. 26 | func (k Keeper) ConversionRate(goCtx context.Context, req *types.QueryConversionRateRequest) (*types.QueryConversionRateResponse, error) { 27 | var ( 28 | ctx = sdk.UnwrapSDKContext(goCtx) 29 | bondDenom = k.stakingKeeper.BondDenom(ctx) 30 | stakingDenomSupply = k.bankKeeper.GetSupply(ctx, bondDenom).Amount.ToLegacyDec() 31 | uphotonSupply = k.bankKeeper.GetSupply(ctx, types.Denom).Amount.ToLegacyDec() 32 | cr = k.conversionRate(ctx, stakingDenomSupply, uphotonSupply) 33 | ) 34 | return &types.QueryConversionRateResponse{ConversionRate: cr.String()}, nil 35 | } 36 | -------------------------------------------------------------------------------- /x/photon/keeper/grpc_query_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "testing" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | 8 | appparams "github.com/atomone-hub/atomone/app/params" 9 | "github.com/atomone-hub/atomone/x/photon/testutil" 10 | "github.com/atomone-hub/atomone/x/photon/types" 11 | "github.com/stretchr/testify/require" 12 | ) 13 | 14 | func TestParamsQuery(t *testing.T) { 15 | k, _, ctx := testutil.SetupPhotonKeeper(t) 16 | params := types.DefaultParams() 17 | k.SetParams(ctx, params) 18 | 19 | resp, err := k.Params(ctx, &types.QueryParamsRequest{}) 20 | 21 | require.NoError(t, err) 22 | require.Equal(t, &types.QueryParamsResponse{Params: params}, resp) 23 | } 24 | 25 | func TestConversionRateQuery(t *testing.T) { 26 | tests := []struct { 27 | name string 28 | uatoneSupply int64 29 | uphotonSupply int64 30 | expectedResponse *types.QueryConversionRateResponse 31 | }{ 32 | { 33 | name: "nominal case", 34 | uatoneSupply: 100_000_000_000_000, // 100,000,000atone 35 | uphotonSupply: 100_000_000_000, // 100,000photon 36 | expectedResponse: &types.QueryConversionRateResponse{ 37 | ConversionRate: "9.999000000000000000", 38 | }, 39 | }, 40 | { 41 | name: "max supply of photon exceeded", 42 | uatoneSupply: 100_000_000_000_000, // 100,000,000atone 43 | uphotonSupply: types.MaxSupply + 1, 44 | expectedResponse: &types.QueryConversionRateResponse{ 45 | ConversionRate: "0.000000000000000000", 46 | }, 47 | }, 48 | } 49 | for _, tt := range tests { 50 | t.Run(tt.name, func(t *testing.T) { 51 | k, m, ctx := testutil.SetupPhotonKeeper(t) 52 | m.StakingKeeper.EXPECT().BondDenom(ctx).Return(appparams.BondDenom) 53 | m.BankKeeper.EXPECT().GetSupply(ctx, appparams.BondDenom). 54 | Return(sdk.NewInt64Coin(appparams.BondDenom, tt.uatoneSupply)) 55 | m.BankKeeper.EXPECT().GetSupply(ctx, types.Denom). 56 | Return(sdk.NewInt64Coin(appparams.BondDenom, tt.uphotonSupply)) 57 | 58 | resp, err := k.ConversionRate(ctx, &types.QueryConversionRateRequest{}) 59 | 60 | require.NoError(t, err) 61 | require.Equal(t, tt.expectedResponse, resp) 62 | }) 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /x/photon/keeper/keeper.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/cometbft/cometbft/libs/log" 7 | 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | storetypes "github.com/cosmos/cosmos-sdk/store/types" 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | 12 | "github.com/atomone-hub/atomone/x/photon/types" 13 | ) 14 | 15 | type Keeper struct { 16 | cdc codec.BinaryCodec 17 | storeKey storetypes.StoreKey 18 | authority string 19 | 20 | bankKeeper types.BankKeeper 21 | accountKeeper types.AccountKeeper 22 | stakingKeeper types.StakingKeeper 23 | } 24 | 25 | func NewKeeper( 26 | cdc codec.BinaryCodec, 27 | storeKey storetypes.StoreKey, 28 | authority string, 29 | bankKeeper types.BankKeeper, 30 | accountKeeper types.AccountKeeper, 31 | stakingKeeper types.StakingKeeper, 32 | ) *Keeper { 33 | return &Keeper{ 34 | cdc: cdc, 35 | storeKey: storeKey, 36 | authority: authority, 37 | bankKeeper: bankKeeper, 38 | accountKeeper: accountKeeper, 39 | stakingKeeper: stakingKeeper, 40 | } 41 | } 42 | 43 | func (k Keeper) Logger(ctx sdk.Context) log.Logger { 44 | return ctx.Logger().With("module", fmt.Sprintf("x/%s", types.ModuleName)) 45 | } 46 | 47 | // conversionRate returns the conversion rate for converting bond denom to 48 | // photon. 49 | func (k Keeper) conversionRate(_ sdk.Context, bondDenomSupply, uphotonSupply sdk.Dec) sdk.Dec { 50 | remainMintableUphotons := sdk.NewDec(types.MaxSupply).Sub(uphotonSupply) 51 | if remainMintableUphotons.IsNegative() { 52 | // If for any reason the max supply is exceeded, avoid returning a negative number 53 | return sdk.ZeroDec() 54 | } 55 | return remainMintableUphotons.Quo(bondDenomSupply) 56 | } 57 | -------------------------------------------------------------------------------- /x/photon/keeper/params.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | 6 | "github.com/atomone-hub/atomone/x/photon/types" 7 | ) 8 | 9 | // GetParams get all parameters as types.Params 10 | func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { 11 | store := ctx.KVStore(k.storeKey) 12 | bz := store.Get(types.ParamsKey) 13 | if bz == nil { 14 | return params 15 | } 16 | k.cdc.MustUnmarshal(bz, ¶ms) 17 | return params 18 | } 19 | 20 | // SetParams set the params 21 | func (k Keeper) SetParams(ctx sdk.Context, params types.Params) error { 22 | store := ctx.KVStore(k.storeKey) 23 | bz, err := k.cdc.Marshal(¶ms) 24 | if err != nil { 25 | return err 26 | } 27 | store.Set(types.ParamsKey, bz) 28 | return nil 29 | } 30 | -------------------------------------------------------------------------------- /x/photon/keeper/params_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/atomone-hub/atomone/x/photon/testutil" 7 | "github.com/atomone-hub/atomone/x/photon/types" 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestGetParams(t *testing.T) { 12 | k, _, ctx := testutil.SetupPhotonKeeper(t) 13 | params := types.DefaultParams() 14 | 15 | k.SetParams(ctx, params) 16 | got := k.GetParams(ctx) 17 | 18 | require.EqualValues(t, params, got) 19 | } 20 | -------------------------------------------------------------------------------- /x/photon/keeper/resolver.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "fmt" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | 8 | "github.com/atomone-hub/atomone/x/photon/types" 9 | ) 10 | 11 | // ConvertToDenom returns "coin.Amount denom" for all coins that are not the denom. 12 | func (k Keeper) ConvertToDenom(ctx sdk.Context, coin sdk.DecCoin, denom string) (sdk.DecCoin, error) { 13 | if coin.Denom == denom { 14 | return coin, nil 15 | } 16 | 17 | if denom == k.stakingKeeper.BondDenom(ctx) { 18 | // use the conversion rate to convert bond denom to photon 19 | bondDenomSupply := k.bankKeeper.GetSupply(ctx, denom).Amount.ToLegacyDec() 20 | uphotonSupply := k.bankKeeper.GetSupply(ctx, types.Denom).Amount.ToLegacyDec() 21 | conversionRate := k.conversionRate(ctx, bondDenomSupply, uphotonSupply) 22 | 23 | // convert bond denom to photon 24 | amount := coin.Amount.Quo(conversionRate) 25 | return sdk.NewDecCoinFromDec(denom, amount), nil 26 | } 27 | 28 | return sdk.DecCoin{}, fmt.Errorf("error resolving denom") 29 | } 30 | 31 | func (k Keeper) ExtraDenoms(ctx sdk.Context) ([]string, error) { 32 | return []string{ 33 | k.stakingKeeper.BondDenom(ctx), 34 | }, nil 35 | } 36 | -------------------------------------------------------------------------------- /x/photon/module_simulation.go: -------------------------------------------------------------------------------- 1 | package photon 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | "github.com/cosmos/cosmos-sdk/types/module" 6 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 7 | 8 | "github.com/atomone-hub/atomone/x/photon/simulation" 9 | "github.com/atomone-hub/atomone/x/photon/types" 10 | ) 11 | 12 | // GenerateGenesisState creates a randomized GenState of the module. 13 | func (AppModule) GenerateGenesisState(simState *module.SimulationState) { 14 | simulation.RandomizedGenState(simState) 15 | } 16 | 17 | // RegisterStoreDecoder registers a decoder. 18 | func (am AppModule) RegisterStoreDecoder(sdr sdk.StoreDecoderRegistry) { 19 | sdr[types.StoreKey] = simulation.NewDecodeStore(am.cdc) 20 | } 21 | 22 | func (AppModule) ProposalMsgs(simState module.SimulationState) []simtypes.WeightedProposalMsg { 23 | return simulation.ProposalMsgs() 24 | } 25 | 26 | // WeightedOperations returns the all the module operations with their respective weights. 27 | func (am AppModule) WeightedOperations(simState module.SimulationState) []simtypes.WeightedOperation { 28 | return simulation.WeightedOperations(simState.AppParams, simState.Cdc, 29 | am.accountKeeper, am.bankKeeper, am.stakingKeeper, am.keeper) 30 | } 31 | -------------------------------------------------------------------------------- /x/photon/simulation/decoder.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/cosmos/cosmos-sdk/codec" 7 | "github.com/cosmos/cosmos-sdk/types/kv" 8 | ) 9 | 10 | // NewDecodeStore returns a decoder function closure that unmarshals the KVPair's 11 | // Value to the corresponding gov type. 12 | func NewDecodeStore(cdc codec.Codec) func(kvA, kvB kv.Pair) string { 13 | return func(kvA, kvB kv.Pair) string { 14 | panic(fmt.Sprintf("invalid photon key prefix %X", kvA.Key[:1])) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /x/photon/simulation/genesis.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "math/rand" 5 | 6 | "github.com/cosmos/cosmos-sdk/types/module" 7 | 8 | "github.com/atomone-hub/atomone/x/photon/types" 9 | ) 10 | 11 | const ( 12 | MintDisabled = "mint_disabled" 13 | TxFeeExceptions = "tx_fee_exceptions" 14 | ) 15 | 16 | // GenMintDisabled returns a randomized MintDisabled param. 17 | func GenMintDisabled(r *rand.Rand) bool { 18 | return r.Int63n(101) <= 15 // 15% chance of mint being disabled 19 | } 20 | 21 | // GenTxFeeExceptions returns a wildcard to allow all transactions to use any 22 | // fee denom. 23 | // This is needed because other modules' simulations do not allow the fee coins 24 | // to be changed, so w/o this configuration all transactions would fail. 25 | func GenTxFeeExceptions(r *rand.Rand) []string { 26 | return []string{"*"} 27 | } 28 | 29 | // RandomizedGenState generates a random GenesisState for gov 30 | func RandomizedGenState(simState *module.SimulationState) { 31 | var mintDisabled bool 32 | simState.AppParams.GetOrGenerate( 33 | simState.Cdc, MintDisabled, &mintDisabled, simState.Rand, 34 | func(r *rand.Rand) { mintDisabled = GenMintDisabled(r) }, 35 | ) 36 | var txFeeExceptions []string 37 | simState.AppParams.GetOrGenerate( 38 | simState.Cdc, TxFeeExceptions, &txFeeExceptions, simState.Rand, 39 | func(r *rand.Rand) { txFeeExceptions = GenTxFeeExceptions(r) }, 40 | ) 41 | 42 | photonGenesis := types.NewGenesisState( 43 | types.NewParams(mintDisabled, txFeeExceptions), 44 | ) 45 | 46 | simState.GenState[types.ModuleName] = simState.Cdc.MustMarshalJSON(photonGenesis) 47 | } 48 | -------------------------------------------------------------------------------- /x/photon/simulation/proposals.go: -------------------------------------------------------------------------------- 1 | package simulation 2 | 3 | import ( 4 | "math/rand" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/cosmos/cosmos-sdk/types/address" 8 | simtypes "github.com/cosmos/cosmos-sdk/types/simulation" 9 | "github.com/cosmos/cosmos-sdk/x/simulation" 10 | 11 | "github.com/atomone-hub/atomone/x/photon/types" 12 | ) 13 | 14 | // Simulation operation weights constants 15 | const ( 16 | DefaultWeightMsgUpdateParams int = 100 17 | 18 | OpWeightMsgUpdateParams = "op_weight_msg_update_params" //nolint:gosec 19 | ) 20 | 21 | // ProposalMsgs defines the module weighted proposals' contents 22 | func ProposalMsgs() []simtypes.WeightedProposalMsg { 23 | return []simtypes.WeightedProposalMsg{ 24 | simulation.NewWeightedProposalMsg( 25 | OpWeightMsgUpdateParams, 26 | DefaultWeightMsgUpdateParams, 27 | SimulateMsgUpdateParams, 28 | ), 29 | } 30 | } 31 | 32 | // SimulateMsgUpdateParams returns a random MsgUpdateParams 33 | func SimulateMsgUpdateParams(r *rand.Rand, _ sdk.Context, _ []simtypes.Account) sdk.Msg { 34 | // use the default gov module account address as authority 35 | var authority sdk.AccAddress = address.Module("gov") 36 | 37 | params := types.DefaultParams() 38 | params.MintDisabled = r.Intn(2) == 0 39 | return &types.MsgUpdateParams{ 40 | Authority: authority.String(), 41 | Params: params, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /x/photon/testutil/keeper.go: -------------------------------------------------------------------------------- 1 | package testutil 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/golang/mock/gomock" 7 | 8 | tmproto "github.com/cometbft/cometbft/proto/tendermint/types" 9 | tmtime "github.com/cometbft/cometbft/types/time" 10 | 11 | "github.com/cosmos/cosmos-sdk/testutil" 12 | sdk "github.com/cosmos/cosmos-sdk/types" 13 | moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil" 14 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 15 | 16 | govtypes "github.com/atomone-hub/atomone/x/gov/types" 17 | "github.com/atomone-hub/atomone/x/photon/keeper" 18 | "github.com/atomone-hub/atomone/x/photon/types" 19 | ) 20 | 21 | type Mocks struct { 22 | AccountKeeper *MockAccountKeeper 23 | BankKeeper *MockBankKeeper 24 | StakingKeeper *MockStakingKeeper 25 | } 26 | 27 | func SetupMsgServer(t *testing.T) (types.MsgServer, *keeper.Keeper, Mocks, sdk.Context) { 28 | t.Helper() 29 | k, m, ctx := SetupPhotonKeeper(t) 30 | return keeper.NewMsgServerImpl(*k), k, m, ctx 31 | } 32 | 33 | func SetupPhotonKeeper(t *testing.T) ( 34 | *keeper.Keeper, 35 | Mocks, 36 | sdk.Context, 37 | ) { 38 | t.Helper() 39 | ctrl := gomock.NewController(t) 40 | m := Mocks{ 41 | AccountKeeper: NewMockAccountKeeper(ctrl), 42 | BankKeeper: NewMockBankKeeper(ctrl), 43 | StakingKeeper: NewMockStakingKeeper(ctrl), 44 | } 45 | 46 | key := sdk.NewKVStoreKey(types.StoreKey) 47 | testCtx := testutil.DefaultContextWithDB(t, key, sdk.NewTransientStoreKey("transient_test")) 48 | ctx := testCtx.Ctx.WithBlockHeader(tmproto.Header{Time: tmtime.Now()}) 49 | encCfg := moduletestutil.MakeTestEncodingConfig() 50 | types.RegisterInterfaces(encCfg.InterfaceRegistry) 51 | // banktypes.RegisterInterfaces(encCfg.InterfaceRegistry) 52 | authority := authtypes.NewModuleAddress(govtypes.ModuleName).String() 53 | return keeper.NewKeeper(encCfg.Codec, key, authority, m.BankKeeper, m.AccountKeeper, m.StakingKeeper), m, ctx 54 | } 55 | -------------------------------------------------------------------------------- /x/photon/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | "github.com/cosmos/cosmos-sdk/codec/legacy" 6 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types" 7 | cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | "github.com/cosmos/cosmos-sdk/types/msgservice" 10 | ) 11 | 12 | func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { 13 | legacy.RegisterAminoMsg(cdc, &MsgMintPhoton{}, "atomone/photon/v1/MsgMintPhoton") 14 | legacy.RegisterAminoMsg(cdc, &MsgUpdateParams{}, "atomone/x/photon/v1/MsgUpdateParams") 15 | cdc.RegisterConcrete(&Params{}, "atomone/photon/v1/Params", nil) 16 | } 17 | 18 | func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { 19 | registry.RegisterImplementations((*sdk.Msg)(nil), 20 | &MsgMintPhoton{}, &MsgUpdateParams{}, 21 | ) 22 | msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) 23 | } 24 | 25 | var ( 26 | amino = codec.NewLegacyAmino() 27 | ModuleCdc = codec.NewAminoCodec(amino) 28 | ) 29 | 30 | func init() { 31 | RegisterLegacyAminoCodec(amino) 32 | cryptocodec.RegisterCrypto(amino) 33 | sdk.RegisterLegacyAminoCodec(amino) 34 | } 35 | -------------------------------------------------------------------------------- /x/photon/types/const.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | 5 | // Denom name 6 | Denom = "uphoton" 7 | 8 | // Photon max supply is 1B 9 | MaxSupply int64 = 1_000_000_000 * 1_000_000 10 | ) 11 | -------------------------------------------------------------------------------- /x/photon/types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | errorsmod "cosmossdk.io/errors" 5 | ) 6 | 7 | // x/photon module sentinel errors 8 | var ( 9 | ErrMintDisabled = errorsmod.Register(ModuleName, 1, "photon mint disabled") 10 | ErrBurnInvalidDenom = errorsmod.Register(ModuleName, 2, "invalid burned amount denom: expected bond denom") 11 | ErrZeroMintPhotons = errorsmod.Register(ModuleName, 3, "no mintable photon after rounding, try higher burn") 12 | ErrTooManyFeeCoins = errorsmod.Register(ModuleName, 5, "too many fee coins, only accepts fees in one denom") 13 | ErrInvalidFeeToken = errorsmod.Register(ModuleName, 6, "invalid fee token") 14 | ) 15 | -------------------------------------------------------------------------------- /x/photon/types/events.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // Photon module event types 4 | const ( 5 | EventTypeMintPhoton = "mint_photon" 6 | 7 | AttributeKeyBurned = "burned" 8 | AttributeKeyMinted = "minted" 9 | ) 10 | -------------------------------------------------------------------------------- /x/photon/types/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | "github.com/cosmos/cosmos-sdk/x/auth/types" 6 | ) 7 | 8 | // AccountKeeper defines the expected account keeper used for simulations (noalias) 9 | type AccountKeeper interface { 10 | GetAccount(ctx sdk.Context, addr sdk.AccAddress) types.AccountI 11 | } 12 | 13 | // StakingKeeper defines the expected staking keeper. 14 | type StakingKeeper interface { 15 | BondDenom(sdk.Context) string 16 | } 17 | 18 | // BankKeeper defines the expected interface needed to retrieve account balances. 19 | type BankKeeper interface { 20 | SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins 21 | MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error 22 | BurnCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error 23 | SendCoinsFromModuleToAccount(ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error 24 | SendCoinsFromAccountToModule(ctx sdk.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error 25 | GetSupply(ctx sdk.Context, denom string) sdk.Coin 26 | } 27 | -------------------------------------------------------------------------------- /x/photon/types/genesis.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // NewGenesisState creates a new genesis state for the governance module 4 | func NewGenesisState(params Params) *GenesisState { 5 | return &GenesisState{ 6 | Params: params, 7 | } 8 | } 9 | 10 | // DefaultGenesis returns the default genesis state 11 | func DefaultGenesis() *GenesisState { 12 | return NewGenesisState(DefaultParams()) 13 | } 14 | 15 | // Validate performs basic genesis state validation returning an error upon any 16 | // failure. 17 | func (gs GenesisState) Validate() error { 18 | return gs.Params.ValidateBasic() 19 | } 20 | -------------------------------------------------------------------------------- /x/photon/types/genesis_test.go: -------------------------------------------------------------------------------- 1 | package types_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/atomone-hub/atomone/x/photon/types" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestGenesisState_Validate(t *testing.T) { 11 | tests := []struct { 12 | desc string 13 | genState *types.GenesisState 14 | valid bool 15 | }{ 16 | { 17 | desc: "default is valid", 18 | genState: types.DefaultGenesis(), 19 | valid: true, 20 | }, 21 | { 22 | desc: "valid genesis state", 23 | genState: &types.GenesisState{}, 24 | valid: true, 25 | }, 26 | } 27 | for _, tc := range tests { 28 | t.Run(tc.desc, func(t *testing.T) { 29 | err := tc.genState.Validate() 30 | if tc.valid { 31 | require.NoError(t, err) 32 | } else { 33 | require.Error(t, err) 34 | } 35 | }) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /x/photon/types/keys.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // ModuleName defines the module name 5 | ModuleName = "photon" 6 | 7 | // StoreKey defines the primary module store key 8 | StoreKey = ModuleName 9 | 10 | // RouterKey defines the module's message routing key 11 | RouterKey = ModuleName 12 | ) 13 | 14 | var ParamsKey = []byte{0x00} 15 | -------------------------------------------------------------------------------- /x/photon/types/params.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // NewParams creates a new Params instance 4 | func NewParams(mintDisabled bool, txFeeExceptions []string) Params { 5 | return Params{ 6 | MintDisabled: mintDisabled, 7 | TxFeeExceptions: txFeeExceptions, 8 | } 9 | } 10 | 11 | const ( 12 | defaultMintDisabled = false 13 | ) 14 | 15 | // NOTE(tb): Not possible to use `sdk.MsgTypeURL(types.MsgMintPhoton{})` 16 | // instead of plain text because at this step the msg is not registered yet. 17 | var defaultTxFeeExceptions = []string{"/atomone.photon.v1.MsgMintPhoton"} 18 | 19 | // DefaultParams returns a default set of parameters 20 | func DefaultParams() Params { 21 | return NewParams(defaultMintDisabled, defaultTxFeeExceptions) 22 | } 23 | 24 | // Validate validates the set of params 25 | func (p Params) ValidateBasic() error { 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /x/photon/types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | --------------------------------------------------------------------------------