├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug-report.md │ └── feature-request.md ├── PULL_REQUEST_TEMPLATE.md ├── dependabot.yml └── workflows │ ├── build.yml │ ├── clean-artifacts.yml │ ├── deploy-contract.yml │ ├── linkchecker.yml │ ├── lint.yml │ ├── release-sims.yml │ ├── sims.yml │ ├── stale.yml │ └── test.yml ├── .gitignore ├── .golangci.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── app ├── ante │ ├── ante.go │ ├── ante_test.go │ ├── doc.go │ ├── eth.go │ └── utils_test.go ├── ethermint.go ├── ethermint_test.go ├── export.go ├── simulation_test.go └── test_helpers.go ├── buf.yaml ├── client ├── config.go ├── export.go ├── keys.go └── testnet.go ├── cmd ├── ethermintcli │ └── main.go └── ethermintd │ ├── genaccounts.go │ └── main.go ├── codec └── codec.go ├── codecov.yml ├── core ├── chain.go └── chain_test.go ├── crypto ├── ethsecp256k1 │ ├── codec.go │ ├── ethsecp256k1.go │ └── ethsecp256k1_test.go └── hd │ ├── algorithm.go │ └── algorithm_test.go ├── docker-compose.yml ├── docs ├── .vuepress │ ├── components │ │ ├── Home.vue │ │ ├── IconCode.vue │ │ ├── IconRocket.vue │ │ ├── TmIconEthereumIntro.vue │ │ ├── TmLogoEthereumBlack.vue │ │ └── TmLogoEthereumWhite.vue │ ├── config.js │ ├── public │ │ ├── logo-bw.svg │ │ └── logo.svg │ └── styles │ │ └── index.styl ├── DOCS_README.md ├── README.md ├── architecture │ ├── README.md │ └── adr-template.md ├── basics │ ├── README.md │ ├── accounts.md │ ├── gas.md │ ├── img │ │ └── photon.png │ ├── json_rpc.md │ ├── photon.md │ └── transactions.md ├── core │ ├── README.md │ └── encoding.md ├── guides │ ├── README.md │ ├── cloud_testnet.md │ ├── img │ │ ├── metamask_import.png │ │ ├── metamask_network_settings.png │ │ ├── remix_deploy.png │ │ ├── remix_deployed.png │ │ └── remix_interact.png │ ├── metamask.md │ ├── remix.md │ └── truffle.md ├── intro │ ├── README.md │ ├── architecture.md │ ├── overview.md │ └── resources.md ├── package.json ├── post.sh ├── pre.sh ├── quickstart │ ├── README.md │ ├── clients.md │ ├── events.md │ ├── installation.md │ ├── run_node.md │ ├── testnet.md │ ├── upgrade.md │ └── validator-setup.md └── yarn.lock ├── go.mod ├── go.sum ├── gometalinter.json ├── importer ├── blockchain └── importer_test.go ├── init.sh ├── networks └── local │ ├── Makefile │ └── ethermintnode │ └── Dockerfile ├── rpc ├── addrlock.go ├── apis.go ├── args │ └── send_tx.go ├── backend.go ├── config.go ├── eth_api.go ├── filter_api.go ├── filter_system.go ├── filters.go ├── net_api.go ├── personal_api.go ├── types.go ├── web3_api.go └── websockets.go ├── scripts ├── contract-test.sh ├── integration-test-all.sh ├── protocgen.sh ├── run-solidity-tests.sh └── start.sh ├── tests-solidity ├── .gitattributes ├── .gitignore ├── README.md ├── init-test-node.sh ├── package.json ├── suites │ ├── basic │ │ ├── contracts │ │ │ ├── Counter.sol │ │ │ └── test │ │ │ │ └── Migrations.sol │ │ ├── main.go │ │ ├── migrations │ │ │ └── 1_initial_migration.js │ │ ├── package.json │ │ ├── test │ │ │ ├── .gitkeep │ │ │ └── counter.js │ │ └── truffle-config.js │ ├── initializable-buidler │ │ ├── .gitignore │ │ ├── buidler.config.js │ │ ├── contracts │ │ │ ├── Initializable.sol │ │ │ ├── Petrifiable.sol │ │ │ ├── TimeHelpers.sol │ │ │ ├── Uint256Helpers.sol │ │ │ ├── UnstructuredStorage.sol │ │ │ └── test │ │ │ │ └── InitializableMock.sol │ │ ├── package.json │ │ └── test │ │ │ └── lifecycle.js │ ├── initializable │ │ ├── contracts │ │ │ ├── Initializable.sol │ │ │ ├── Petrifiable.sol │ │ │ ├── TimeHelpers.sol │ │ │ ├── Uint256Helpers.sol │ │ │ ├── UnstructuredStorage.sol │ │ │ └── test │ │ │ │ ├── InitializableMock.sol │ │ │ │ └── Migrations.sol │ │ ├── migrations │ │ │ └── 1_initial_migration.js │ │ ├── package.json │ │ ├── test │ │ │ ├── .gitkeep │ │ │ └── lifecycle.js │ │ └── truffle-config.js │ ├── proxy │ │ ├── contracts │ │ │ ├── DelegateProxy.sol │ │ │ ├── DepositableDelegateProxy.sol │ │ │ ├── DepositableStorage.sol │ │ │ ├── ERCProxy.sol │ │ │ ├── IsContract.sol │ │ │ ├── UnstructuredStorage.sol │ │ │ └── test │ │ │ │ ├── DepositableDelegateProxyMock.sol │ │ │ │ ├── EthSender.sol │ │ │ │ ├── Migrations.sol │ │ │ │ └── ProxyTarget.sol │ │ ├── migrations │ │ │ └── 1_initial_migration.js │ │ ├── package.json │ │ ├── test │ │ │ ├── .gitkeep │ │ │ └── depositable_delegate_proxy.js │ │ └── truffle-config.js │ └── staking │ │ ├── .github │ │ └── workflows │ │ │ └── ci_contracts.yml │ │ ├── contracts │ │ ├── Staking.sol │ │ ├── StakingFactory.sol │ │ ├── lib │ │ │ ├── Checkpointing.sol │ │ │ └── os │ │ │ │ ├── Autopetrified.sol │ │ │ │ ├── DelegateProxy.sol │ │ │ │ ├── ERC20.sol │ │ │ │ ├── ERCProxy.sol │ │ │ │ ├── Initializable.sol │ │ │ │ ├── IsContract.sol │ │ │ │ ├── Migrations.sol │ │ │ │ ├── Petrifiable.sol │ │ │ │ ├── SafeERC20.sol │ │ │ │ ├── SafeMath.sol │ │ │ │ ├── SafeMath64.sol │ │ │ │ ├── ScriptHelpers.sol │ │ │ │ ├── TimeHelpers.sol │ │ │ │ ├── Uint256Helpers.sol │ │ │ │ └── UnstructuredStorage.sol │ │ ├── locking │ │ │ ├── ILockManager.sol │ │ │ ├── IStakingLocking.sol │ │ │ └── TimeLockManager.sol │ │ ├── proxies │ │ │ ├── StakingProxy.sol │ │ │ └── ThinProxy.sol │ │ ├── standards │ │ │ └── ERC900.sol │ │ └── test │ │ │ ├── TestImports.sol │ │ │ ├── lib │ │ │ ├── EchidnaStaking.sol │ │ │ ├── ITokenController.sol │ │ │ └── MiniMeToken.sol │ │ │ └── mocks │ │ │ ├── BadTokenMock.sol │ │ │ ├── CheckpointingMock.sol │ │ │ ├── ERC20.sol │ │ │ ├── LockManagerMock.sol │ │ │ ├── NoApproveTokenMock.sol │ │ │ ├── StakingMock.sol │ │ │ ├── StandardTokenMock.sol │ │ │ ├── TimeHelpersMock.sol │ │ │ └── TimeLockManagerMock.sol │ │ ├── package.json │ │ ├── test │ │ ├── approve_and_call.js │ │ ├── gas.js │ │ ├── helpers │ │ │ ├── constants.js │ │ │ ├── deploy.js │ │ │ ├── errors.js │ │ │ └── helpers.js │ │ ├── lib │ │ │ └── checkpointing.js │ │ ├── locking │ │ │ ├── funds_flows.js │ │ │ ├── locking.js │ │ │ └── locking_time.js │ │ ├── staking.js │ │ ├── staking_factory.js │ │ ├── staking_proxy.js │ │ └── transfers.js │ │ └── truffle-config.js └── yarn.lock ├── tests ├── personal_test.go └── rpc_test.go ├── types ├── account.go ├── account_test.go ├── chain_id.go ├── chain_id_test.go ├── code.go ├── codec.go ├── coin.go ├── config.go ├── config_test.go ├── errors.go ├── params.go └── types.proto ├── utils ├── int.go └── int_test.go ├── version └── version.go └── x ├── evm ├── abci.go ├── alias.go ├── client │ └── cli │ │ ├── query.go │ │ ├── tx.go │ │ ├── utils.go │ │ └── utils_test.go ├── genesis.go ├── genesis_test.go ├── handler.go ├── handler_test.go ├── keeper │ ├── keeper.go │ ├── keeper_test.go │ ├── params.go │ ├── params_test.go │ ├── querier.go │ ├── querier_test.go │ ├── statedb.go │ └── statedb_test.go ├── module.go ├── module_test.go ├── spec │ └── README.md └── types │ ├── chain_config.go │ ├── chain_config_test.go │ ├── codec.go │ ├── errors.go │ ├── events.go │ ├── expected_keepers.go │ ├── genesis.go │ ├── genesis_test.go │ ├── journal.go │ ├── journal_test.go │ ├── key.go │ ├── logs.go │ ├── logs_test.go │ ├── msg.go │ ├── msg_test.go │ ├── params.go │ ├── params_test.go │ ├── querier.go │ ├── state_object.go │ ├── state_object_test.go │ ├── state_transition.go │ ├── state_transition_test.go │ ├── statedb.go │ ├── statedb_test.go │ ├── storage.go │ ├── storage_test.go │ ├── tx_data.go │ ├── tx_data_test.go │ ├── utils.go │ └── utils_test.go └── faucet ├── alias.go ├── client ├── cli │ ├── query.go │ └── tx.go └── rest │ └── tx.go ├── genesis.go ├── handler.go ├── keeper ├── keeper.go └── querier.go ├── module.go └── types ├── codec.go ├── errors.go ├── events.go ├── expected_keepers.go ├── genesis.go ├── key.go └── msgs.go /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # CODEOWNERS: https://help.github.com/articles/about-codeowners/ 2 | 3 | # Primary repo maintainers 4 | * @fedekunze @noot 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: create a bug report 4 | --- 5 | 6 | __System info:__ [Include Ethermint commit, operating system name, and other relevant details] 7 | 8 | __Steps to reproduce:__ 9 | 10 | 1. [First Step] 11 | 2. [Second Step] 12 | 3. [and so on...] 13 | 14 | __Expected behavior:__ [What you expected to happen] 15 | 16 | __Actual behavior:__ [What actually happened] 17 | 18 | __Additional info:__ [Include gist of relevant config, logs, etc.] 19 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Opening a feature request kicks off a discussion 4 | 5 | --- 6 | 7 | __Proposal:__ [Description of the feature] 8 | 9 | __Current behavior:__ [What currently happens] 10 | 11 | __Desired behavior:__ [What you would like to happen] 12 | 13 | __Use case:__ [Why is this important (helps with prioritizing requests)] 14 | 15 | Requests may be closed if we're not actively planning to work on them. 16 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | Closes: #XXX 8 | 9 | ## Description 10 | 11 | 14 | 15 | ______ 16 | 17 | For contributor use: 18 | 19 | - [ ] Targeted PR against correct branch (see [CONTRIBUTING.md](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#pr-targeting)) 20 | - [ ] Linked to Github issue with discussion and accepted design OR link to spec that describes this work. 21 | - [ ] Code follows the [module structure standards](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/structure.md). 22 | - [ ] Wrote unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/master/CONTRIBUTING.md#testing) 23 | - [ ] Updated relevant documentation (`docs/`) or specification (`x//spec/`) 24 | - [ ] Added relevant `godoc` [comments](https://blog.golang.org/godoc-documenting-go-code). 25 | - [ ] Added a relevant changelog entry to the `Unreleased` section in `CHANGELOG.md` 26 | - [ ] Re-reviewed `Files changed` in the Github PR explorer 27 | 28 | ______ 29 | 30 | For admin use: 31 | 32 | - [ ] Added appropriate labels to PR (ex. `WIP`, `R4R`, `docs`, etc) 33 | - [ ] Reviewers assigned 34 | - [ ] Squashed all commits, uses message "Merge pull request #XYZ: [title]" ([coding standards](https://github.com/tendermint/coding/blob/master/README.md#merging-a-pr)) 35 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "10:00" 8 | open-pull-requests-limit: 10 9 | reviewers: 10 | - fedekunze 11 | - noot 12 | labels: 13 | - dependencies 14 | - package-ecosystem: docker 15 | directory: "/" 16 | schedule: 17 | interval: daily 18 | time: "10:00" 19 | open-pull-requests-limit: 10 20 | reviewers: 21 | - araskachoi 22 | - fedekunze 23 | - noot 24 | - package-ecosystem: gomod 25 | directory: "/" 26 | schedule: 27 | interval: daily 28 | time: "10:00" 29 | open-pull-requests-limit: 10 30 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | pull_request: 4 | branches: 5 | - "development" 6 | 7 | jobs: 8 | cleanup-runs: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: rokroskar/workflow-run-cleanup-action@master 12 | env: 13 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 14 | if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/development'" 15 | 16 | build: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: technote-space/get-diff-action@v4 21 | id: git_diff 22 | with: 23 | SUFFIX_FILTER: | 24 | .go 25 | .mod 26 | .sum 27 | - run: | 28 | make build 29 | if: "env.GIT_DIFF != ''" 30 | -------------------------------------------------------------------------------- /.github/workflows/clean-artifacts.yml: -------------------------------------------------------------------------------- 1 | name: Remove old artifacts 2 | # Remove old artifacts runs a crob job that removes old artifacts 3 | # generated from the split tests workflow. 4 | 5 | on: 6 | schedule: 7 | # Every day at 1am 8 | - cron: "0 1 * * *" 9 | 10 | jobs: 11 | remove-old-artifacts: 12 | runs-on: ubuntu-latest 13 | timeout-minutes: 10 14 | 15 | steps: 16 | - name: Remove old artifacts 17 | uses: c-hive/gha-remove-artifacts@v1 18 | with: 19 | age: "7 days" 20 | -------------------------------------------------------------------------------- /.github/workflows/deploy-contract.yml: -------------------------------------------------------------------------------- 1 | name: Deploy Contract 2 | on: 3 | pull_request: 4 | branches: 5 | - "development" 6 | 7 | jobs: 8 | cleanup-runs: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: rokroskar/workflow-run-cleanup-action@master 12 | env: 13 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 14 | if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/development'" 15 | 16 | deploy: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Use Node.js 21 | uses: actions/setup-node@v2.1.2 22 | with: 23 | node-version: '12.x' 24 | - name: Install dependencies 25 | run: npm install 26 | - uses: technote-space/get-diff-action@v4 27 | id: git_diff 28 | with: 29 | SUFFIX_FILTER: | 30 | .go 31 | .mod 32 | .sum 33 | .sol 34 | - name: Test contract 35 | run: | 36 | sudo make contract-tools 37 | sudo make test-contract 38 | if: "env.GIT_DIFF != ''" 39 | -------------------------------------------------------------------------------- /.github/workflows/linkchecker.yml: -------------------------------------------------------------------------------- 1 | name: Check Markdown links 2 | on: 3 | schedule: 4 | - cron: "* */24 * * *" 5 | jobs: 6 | markdown-link-check: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@development 10 | - uses: gaurav-nelson/github-action-markdown-link-check@1.0.7 11 | with: 12 | folder-path: "docs" 13 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | # Lint runs golangci-lint over the entire cosmos-sdk repository 3 | # This workflow is run on every pull request and push to development 4 | # The `golangci` will pass without running if no *.{go, mod, sum} files have been changed. 5 | on: 6 | pull_request: 7 | push: 8 | branches: 9 | - development 10 | jobs: 11 | golangci: 12 | name: golangci-lint 13 | runs-on: ubuntu-latest 14 | timeout-minutes: 10 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: technote-space/get-diff-action@v4 18 | with: 19 | SUFFIX_FILTER: | 20 | .go 21 | .mod 22 | .sum 23 | - uses: golangci/golangci-lint-action@master 24 | with: 25 | # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. 26 | version: v1.28 27 | args: --timeout 10m 28 | github-token: ${{ secrets.github_token }} 29 | if: "env.GIT_DIFF != ''" 30 | -------------------------------------------------------------------------------- /.github/workflows/release-sims.yml: -------------------------------------------------------------------------------- 1 | name: Release Sims 2 | # Release Sims workflow runs long-lived (multi-seed & large block size) simulations of 500 blocks. 3 | # This workflow only runs on a pull request when the branch contains rc** (rc1/vX.X.x) 4 | # This release workflow *cannot* be skipped with the 'skip-sims' commit message. 5 | on: 6 | pull_request: 7 | branches: 8 | - "rc**" 9 | 10 | jobs: 11 | cleanup-runs: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: rokroskar/workflow-run-cleanup-action@master 15 | env: 16 | GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" 17 | if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/development'" 18 | 19 | install-runsim: 20 | runs-on: ubuntu-latest 21 | if: "!contains(github.event.head_commit.message, 'skip-sims')" 22 | steps: 23 | - name: install runsim 24 | run: | 25 | export GO111MODULE="on" && go get github.com/cosmos/tools/cmd/runsim@v1.0.0 26 | - uses: actions/cache@v2.1.2 27 | with: 28 | path: ~/go/bin 29 | key: ${{ runner.os }}-go-runsim-binary 30 | 31 | test-sim-multi-seed-long: 32 | runs-on: ubuntu-latest 33 | needs: install-runsim 34 | steps: 35 | - uses: actions/checkout@v2 36 | - uses: actions/cache@v2.1.2 37 | with: 38 | path: ~/go/bin 39 | key: ${{ runner.os }}-go-runsim-binary 40 | - name: test-sim-multi-seed-long 41 | run: | 42 | make test-sim-multi-seed-long 43 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: "Close stale issues & pull requests" 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/stale@v3 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 14 | recent activity. It will be closed in 7 days-before-close if no further activity occurs. Thank you 15 | for your contributions." 16 | stale-issue-message: "This issue is stale because it has been open 45 days with no activity. Remove `stale` label or comment or this will be closed in 7 days." 17 | days-before-stale: 45 18 | days-before-close: 7 19 | exempt-issue-labels: "Status: On Ice, Status: Blocked, Type: Bug, Type: Security, Type: Meta-Issue, Type: Enhancement, Epic" 20 | exempt-pr-labels: "Status: On Ice, Status: Blocked, Type: Bug, Type: Security, Type: Meta-Issue, Type: Enhancement, Epic" 21 | stale-pr-label: "stale" 22 | stale-issue-label: "stale" 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | .DS_Store 3 | *.swp 4 | *.swo 5 | *.swl 6 | *.swm 7 | *.swn 8 | .vscode 9 | .idea 10 | *.pyc 11 | *.exe 12 | *.exe~ 13 | *.dll 14 | *.so 15 | *.dylib 16 | 17 | # Build 18 | *.test 19 | .glide/ 20 | vendor 21 | build 22 | bin 23 | tools/bin/* 24 | docs/_build 25 | docs/tutorial 26 | docs/node_modules 27 | docs/modules 28 | dist 29 | tools-stamp 30 | docs-tools-stamp 31 | proto-tools-stamp 32 | golangci-lint 33 | keyring_test_cosmos 34 | 35 | # Testing 36 | coverage.txt 37 | *.out 38 | sim_log_file 39 | importer/tmp 40 | 41 | # Vagrant 42 | .vagrant/ 43 | *.box 44 | *.log 45 | vagrant 46 | 47 | # IDE 48 | .idea/ 49 | *.iml 50 | 51 | # Graphviz 52 | dependency-graph.png 53 | 54 | # Latex 55 | *.aux 56 | *.out 57 | *.synctex.gz 58 | 59 | # Contracts 60 | *.bin 61 | *.abi 62 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | tests: false 3 | # # timeout for analysis, e.g. 30s, 5m, default is 1m 4 | # timeout: 5m 5 | 6 | linters: 7 | disable-all: true 8 | enable: 9 | - bodyclose 10 | - deadcode 11 | - depguard 12 | - dogsled 13 | - errcheck 14 | - goconst 15 | - gocritic 16 | - gofmt 17 | - goimports 18 | - golint 19 | - gosec 20 | - gosimple 21 | - govet 22 | - ineffassign 23 | - interfacer 24 | - maligned 25 | - misspell 26 | - nakedret 27 | - prealloc 28 | - scopelint 29 | - staticcheck 30 | - structcheck 31 | - stylecheck 32 | - typecheck 33 | - unconvert 34 | - unused 35 | - unparam 36 | - misspell 37 | 38 | issues: 39 | exclude-rules: 40 | - text: "Use of weak random number generator" 41 | linters: 42 | - gosec 43 | - text: "comment on exported var" 44 | linters: 45 | - golint 46 | - text: "don't use an underscore in package name" 47 | linters: 48 | - golint 49 | - text: "ST1003:" 50 | linters: 51 | - stylecheck 52 | # FIXME: Disabled until golangci-lint updates stylecheck with this fix: 53 | # https://github.com/dominikh/go-tools/issues/389 54 | - text: "ST1016:" 55 | linters: 56 | - stylecheck 57 | max-issues-per-linter: 10000 58 | max-same-issues: 10000 59 | 60 | linters-settings: 61 | dogsled: 62 | max-blank-identifiers: 3 63 | maligned: 64 | # print struct with more effective memory layout or not, false by default 65 | suggest-new: true 66 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS build-env 2 | 3 | # Set up dependencies 4 | ENV PACKAGES git build-base 5 | 6 | # Set working directory for the build 7 | WORKDIR /go/src/github.com/cosmos/ethermint 8 | 9 | # Install dependencies 10 | RUN apk add --update $PACKAGES 11 | RUN apk add linux-headers 12 | 13 | # Add source files 14 | COPY . . 15 | 16 | # Make the binary 17 | RUN make build 18 | 19 | # Final image 20 | FROM alpine 21 | 22 | # Install ca-certificates 23 | RUN apk add --update ca-certificates 24 | WORKDIR /root 25 | 26 | # Copy over binaries from the build-env 27 | COPY --from=build-env /go/src/github.com/cosmos/ethermint/build/ethermintd /usr/bin/ethermintd 28 | COPY --from=build-env /go/src/github.com/cosmos/ethermint/build/ethermintcli /usr/bin/ethermintcli 29 | 30 | # Run ethermintd by default 31 | CMD ["ethermintd"] 32 | -------------------------------------------------------------------------------- /app/ante/doc.go: -------------------------------------------------------------------------------- 1 | /*Package ante defines the SDK auth module's AnteHandler as well as an internal 2 | AnteHandler for an Ethereum transaction (i.e MsgEthereumTx). 3 | 4 | During CheckTx, the transaction is passed through a series of 5 | pre-message execution validation checks such as signature and account 6 | verification in addition to minimum fees being checked. Otherwise, during 7 | DeliverTx, the transaction is simply passed to the EVM which will also 8 | perform the same series of checks. The distinction is made in CheckTx to 9 | prevent spam and DoS attacks. 10 | */ 11 | package ante 12 | -------------------------------------------------------------------------------- /app/ethermint_test.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | abci "github.com/tendermint/tendermint/abci/types" 10 | "github.com/tendermint/tendermint/libs/log" 11 | dbm "github.com/tendermint/tm-db" 12 | 13 | "github.com/cosmos/cosmos-sdk/codec" 14 | ) 15 | 16 | func TestEthermintAppExport(t *testing.T) { 17 | db := dbm.NewMemDB() 18 | app := NewEthermintApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0) 19 | 20 | genesisState := ModuleBasics.DefaultGenesis() 21 | stateBytes, err := codec.MarshalJSONIndent(app.cdc, genesisState) 22 | require.NoError(t, err) 23 | 24 | // Initialize the chain 25 | app.InitChain( 26 | abci.RequestInitChain{ 27 | Validators: []abci.ValidatorUpdate{}, 28 | AppStateBytes: stateBytes, 29 | }, 30 | ) 31 | app.Commit() 32 | 33 | // Making a new app object with the db, so that initchain hasn't been called 34 | app2 := NewEthermintApp(log.NewTMLogger(log.NewSyncWriter(os.Stdout)), db, nil, true, map[int64]bool{}, 0) 35 | _, _, err = app2.ExportAppStateAndValidators(false, []string{}) 36 | require.NoError(t, err, "ExportAppStateAndValidators should not have an error") 37 | } 38 | -------------------------------------------------------------------------------- /app/test_helpers.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | abci "github.com/tendermint/tendermint/abci/types" 5 | "github.com/tendermint/tendermint/libs/log" 6 | dbm "github.com/tendermint/tm-db" 7 | 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | ) 10 | 11 | // Setup initializes a new EthermintApp. A Nop logger is set in EthermintApp. 12 | func Setup(isCheckTx bool) *EthermintApp { 13 | db := dbm.NewMemDB() 14 | app := NewEthermintApp(log.NewNopLogger(), db, nil, true, map[int64]bool{}, 0) 15 | 16 | if !isCheckTx { 17 | // init chain must be called to stop deliverState from being nil 18 | genesisState := NewDefaultGenesisState() 19 | stateBytes, err := codec.MarshalJSONIndent(app.Codec(), genesisState) 20 | if err != nil { 21 | panic(err) 22 | } 23 | 24 | // Initialize the chain 25 | app.InitChain( 26 | abci.RequestInitChain{ 27 | Validators: []abci.ValidatorUpdate{}, 28 | AppStateBytes: stateBytes, 29 | }, 30 | ) 31 | } 32 | 33 | return app 34 | } 35 | -------------------------------------------------------------------------------- /buf.yaml: -------------------------------------------------------------------------------- 1 | build: 2 | roots: 3 | - . 4 | lint: 5 | use: 6 | - DEFAULT 7 | - COMMENTS 8 | - FILE_LOWER_SNAKE_CASE 9 | except: 10 | - UNARY_RPC 11 | - COMMENT_FIELD 12 | - PACKAGE_DIRECTORY_MATCH 13 | ignore: 14 | - third_party 15 | - codec/testdata 16 | breaking: 17 | use: 18 | - FILE 19 | ignore: 20 | - third_party 21 | -------------------------------------------------------------------------------- /client/config.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | 8 | "github.com/spf13/cobra" 9 | "github.com/spf13/viper" 10 | 11 | "github.com/tendermint/tendermint/libs/cli" 12 | 13 | "github.com/cosmos/cosmos-sdk/client/flags" 14 | 15 | ethermint "github.com/cosmos/ethermint/types" 16 | ) 17 | 18 | // InitConfig adds the chain-id, encoding and output flags to the persistent flag set. 19 | func InitConfig(cmd *cobra.Command) error { 20 | home, err := cmd.PersistentFlags().GetString(cli.HomeFlag) 21 | if err != nil { 22 | return err 23 | } 24 | 25 | configFile := path.Join(home, "config", "config.toml") 26 | if _, err := os.Stat(configFile); err == nil { 27 | viper.SetConfigFile(configFile) 28 | 29 | if err := viper.ReadInConfig(); err != nil { 30 | return err 31 | } 32 | } 33 | 34 | if err := viper.BindPFlag(flags.FlagChainID, cmd.PersistentFlags().Lookup(flags.FlagChainID)); err != nil { 35 | return err 36 | } 37 | 38 | if err := viper.BindPFlag(cli.EncodingFlag, cmd.PersistentFlags().Lookup(cli.EncodingFlag)); err != nil { 39 | return err 40 | } 41 | 42 | return viper.BindPFlag(cli.OutputFlag, cmd.PersistentFlags().Lookup(cli.OutputFlag)) 43 | } 44 | 45 | // ValidateChainID wraps a cobra command with a RunE function with base 10 integer chain-id verification. 46 | func ValidateChainID(baseCmd *cobra.Command) *cobra.Command { 47 | // Copy base run command to be used after chain verification 48 | baseRunE := baseCmd.RunE 49 | 50 | // Function to replace command's RunE function 51 | validateFn := func(cmd *cobra.Command, args []string) error { 52 | chainID := viper.GetString(flags.FlagChainID) 53 | 54 | if !ethermint.IsValidChainID(chainID) { 55 | return fmt.Errorf("invalid chain-id format: %s", chainID) 56 | } 57 | 58 | return baseRunE(cmd, args) 59 | } 60 | 61 | baseCmd.RunE = validateFn 62 | return baseCmd 63 | } 64 | -------------------------------------------------------------------------------- /client/export.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/spf13/cobra" 9 | "github.com/spf13/viper" 10 | 11 | "github.com/ethereum/go-ethereum/common/hexutil" 12 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 13 | 14 | "github.com/cosmos/cosmos-sdk/client/flags" 15 | "github.com/cosmos/cosmos-sdk/client/input" 16 | "github.com/cosmos/cosmos-sdk/crypto/keys" 17 | sdk "github.com/cosmos/cosmos-sdk/types" 18 | "github.com/cosmos/ethermint/crypto/ethsecp256k1" 19 | "github.com/cosmos/ethermint/crypto/hd" 20 | ) 21 | 22 | // UnsafeExportEthKeyCommand exports a key with the given name as a private key in hex format. 23 | func UnsafeExportEthKeyCommand() *cobra.Command { 24 | return &cobra.Command{ 25 | Use: "unsafe-export-eth-key [name]", 26 | Short: "**UNSAFE** Export an Ethereum private key", 27 | Long: `**UNSAFE** Export an Ethereum private key unencrypted to use in dev tooling`, 28 | Args: cobra.ExactArgs(1), 29 | RunE: func(cmd *cobra.Command, args []string) error { 30 | inBuf := bufio.NewReader(cmd.InOrStdin()) 31 | 32 | kb, err := keys.NewKeyring( 33 | sdk.KeyringServiceName(), 34 | viper.GetString(flags.FlagKeyringBackend), 35 | viper.GetString(flags.FlagHome), 36 | inBuf, 37 | hd.EthSecp256k1Options()..., 38 | ) 39 | if err != nil { 40 | return err 41 | } 42 | 43 | decryptPassword := "" 44 | conf := true 45 | keyringBackend := viper.GetString(flags.FlagKeyringBackend) 46 | switch keyringBackend { 47 | case keys.BackendFile: 48 | decryptPassword, err = input.GetPassword( 49 | "**WARNING this is an unsafe way to export your unencrypted private key**\nEnter key password:", 50 | inBuf) 51 | case keys.BackendOS: 52 | conf, err = input.GetConfirmation( 53 | "**WARNING** this is an unsafe way to export your unencrypted private key, are you sure?", 54 | inBuf) 55 | } 56 | if err != nil || !conf { 57 | return err 58 | } 59 | 60 | // Exports private key from keybase using password 61 | privKey, err := kb.ExportPrivateKeyObject(args[0], decryptPassword) 62 | if err != nil { 63 | return err 64 | } 65 | 66 | // Converts key to Ethermint secp256 implementation 67 | emintKey, ok := privKey.(ethsecp256k1.PrivKey) 68 | if !ok { 69 | return fmt.Errorf("invalid private key type, must be Ethereum key: %T", privKey) 70 | } 71 | 72 | // Formats key for output 73 | privB := ethcrypto.FromECDSA(emintKey.ToECDSA()) 74 | keyS := strings.ToUpper(hexutil.Encode(privB)[2:]) 75 | 76 | fmt.Println(keyS) 77 | 78 | return nil 79 | }, 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /client/keys.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "bufio" 5 | "io" 6 | 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/viper" 9 | 10 | "github.com/cosmos/cosmos-sdk/client/flags" 11 | clientkeys "github.com/cosmos/cosmos-sdk/client/keys" 12 | "github.com/cosmos/cosmos-sdk/crypto/keys" 13 | sdk "github.com/cosmos/cosmos-sdk/types" 14 | 15 | "github.com/cosmos/ethermint/crypto/hd" 16 | ) 17 | 18 | const ( 19 | flagDryRun = "dry-run" 20 | ) 21 | 22 | // KeyCommands registers a sub-tree of commands to interact with 23 | // local private key storage. 24 | func KeyCommands() *cobra.Command { 25 | cmd := &cobra.Command{ 26 | Use: "keys", 27 | Short: "Add or view local private keys", 28 | Long: `Keys allows you to manage your local keystore for tendermint. 29 | 30 | These keys may be in any format supported by go-crypto and can be 31 | used by light-clients, full nodes, or any other application that 32 | needs to sign with a private key.`, 33 | } 34 | 35 | // support adding Ethereum supported keys 36 | addCmd := clientkeys.AddKeyCommand() 37 | 38 | // update the default signing algorithm value to "eth_secp256k1" 39 | algoFlag := addCmd.Flag("algo") 40 | algoFlag.DefValue = string(hd.EthSecp256k1) 41 | err := algoFlag.Value.Set(string(hd.EthSecp256k1)) 42 | if err != nil { 43 | panic(err) 44 | } 45 | addCmd.RunE = runAddCmd 46 | 47 | cmd.AddCommand( 48 | clientkeys.MnemonicKeyCommand(), 49 | addCmd, 50 | clientkeys.ExportKeyCommand(), 51 | clientkeys.ImportKeyCommand(), 52 | clientkeys.ListKeysCmd(), 53 | clientkeys.ShowKeysCmd(), 54 | flags.LineBreak, 55 | clientkeys.DeleteKeyCommand(), 56 | clientkeys.ParseKeyStringCommand(), 57 | clientkeys.MigrateCommand(), 58 | flags.LineBreak, 59 | UnsafeExportEthKeyCommand(), 60 | ) 61 | return cmd 62 | } 63 | 64 | func runAddCmd(cmd *cobra.Command, args []string) error { 65 | inBuf := bufio.NewReader(cmd.InOrStdin()) 66 | kb, err := getKeybase(viper.GetBool(flagDryRun), inBuf) 67 | if err != nil { 68 | return err 69 | } 70 | 71 | return clientkeys.RunAddCmd(cmd, args, kb, inBuf) 72 | } 73 | 74 | func getKeybase(transient bool, buf io.Reader) (keys.Keybase, error) { 75 | if transient { 76 | return keys.NewInMemory( 77 | hd.EthSecp256k1Options()..., 78 | ), nil 79 | } 80 | 81 | return keys.NewKeyring( 82 | sdk.KeyringServiceName(), 83 | viper.GetString(flags.FlagKeyringBackend), 84 | viper.GetString(flags.FlagHome), 85 | buf, 86 | hd.EthSecp256k1Options()..., 87 | ) 88 | } 89 | -------------------------------------------------------------------------------- /codec/codec.go: -------------------------------------------------------------------------------- 1 | package codec 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | "github.com/cosmos/cosmos-sdk/crypto/keys" 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | "github.com/cosmos/cosmos-sdk/types/module" 8 | "github.com/cosmos/cosmos-sdk/x/auth/vesting" 9 | 10 | cryptocodec "github.com/cosmos/ethermint/crypto/ethsecp256k1" 11 | ethermint "github.com/cosmos/ethermint/types" 12 | ) 13 | 14 | // MakeCodec registers the necessary types and interfaces for an sdk.App. This 15 | // codec is provided to all the modules the application depends on. 16 | // 17 | // NOTE: This codec will be deprecated in favor of AppCodec once all modules are 18 | // migrated to protobuf. 19 | func MakeCodec(bm module.BasicManager) *codec.Codec { 20 | cdc := codec.New() 21 | 22 | bm.RegisterCodec(cdc) 23 | vesting.RegisterCodec(cdc) 24 | sdk.RegisterCodec(cdc) 25 | cryptocodec.RegisterCodec(cdc) 26 | codec.RegisterCrypto(cdc) 27 | ethermint.RegisterCodec(cdc) 28 | keys.RegisterCodec(cdc) // temporary. Used to register keyring.Info 29 | 30 | return cdc 31 | } 32 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # 2 | # This codecov.yml is the default configuration for 3 | # all repositories on Codecov. You may adjust the settings 4 | # below in your own codecov.yml in your repository. 5 | # 6 | coverage: 7 | precision: 2 8 | round: down 9 | range: 70...100 10 | 11 | status: 12 | # Learn more at https://docs.codecov.io/docs/commit-status 13 | project: 14 | default: 15 | threshold: 1% # allow this much decrease on project 16 | app: 17 | target: 70% 18 | flags: app 19 | modules: 20 | target: 50% 21 | flags: modules 22 | core: 23 | target: 50% 24 | flags: core 25 | clients: 26 | flags: clients 27 | changes: false 28 | 29 | comment: 30 | layout: "reach, diff, files" 31 | behavior: default # update if exists else create new 32 | require_changes: true 33 | 34 | flags: 35 | app: 36 | paths: 37 | - "app/" 38 | modules: 39 | paths: 40 | - "x/" 41 | - "!x/**/client/" # ignore client package 42 | core: 43 | paths: 44 | - "core/" 45 | - "crypto/" 46 | - "types/" 47 | clients: 48 | paths: 49 | - "rpc/" 50 | - "client/" 51 | - "x/**/client/" 52 | 53 | ignore: 54 | - "docs" 55 | - "*.md" 56 | - "cmd" 57 | - "x/faucet" 58 | - "**/*.pb.go" 59 | - "types/*.pb.go" 60 | - "x/**/*.pb.go" 61 | -------------------------------------------------------------------------------- /crypto/ethsecp256k1/codec.go: -------------------------------------------------------------------------------- 1 | package ethsecp256k1 2 | 3 | import ( 4 | cryptoamino "github.com/tendermint/tendermint/crypto/encoding/amino" 5 | 6 | "github.com/cosmos/cosmos-sdk/codec" 7 | "github.com/cosmos/cosmos-sdk/crypto/keys" 8 | ) 9 | 10 | // CryptoCodec is the default amino codec used by ethermint 11 | var CryptoCodec = codec.New() 12 | 13 | func init() { 14 | // replace the keyring codec with the ethermint crypto codec to prevent 15 | // amino panics because of unregistered Priv/PubKey 16 | keys.CryptoCdc = CryptoCodec 17 | keys.RegisterCodec(CryptoCodec) 18 | cryptoamino.RegisterAmino(CryptoCodec) 19 | RegisterCodec(CryptoCodec) 20 | } 21 | 22 | // RegisterCodec registers all the necessary types with amino for the given 23 | // codec. 24 | func RegisterCodec(cdc *codec.Codec) { 25 | cdc.RegisterConcrete(PubKey{}, PubKeyName, nil) 26 | cdc.RegisterConcrete(PrivKey{}, PrivKeyName, nil) 27 | } 28 | -------------------------------------------------------------------------------- /crypto/ethsecp256k1/ethsecp256k1_test.go: -------------------------------------------------------------------------------- 1 | package ethsecp256k1 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | ethcrypto "github.com/ethereum/go-ethereum/crypto" 9 | ethsecp256k1 "github.com/ethereum/go-ethereum/crypto/secp256k1" 10 | 11 | tmcrypto "github.com/tendermint/tendermint/crypto" 12 | ) 13 | 14 | func TestPrivKeyPrivKey(t *testing.T) { 15 | // validate type and equality 16 | privKey, err := GenerateKey() 17 | require.NoError(t, err) 18 | require.True(t, privKey.Equals(privKey)) 19 | require.Implements(t, (*tmcrypto.PrivKey)(nil), privKey) 20 | 21 | // validate inequality 22 | privKey2, err := GenerateKey() 23 | require.NoError(t, err) 24 | require.False(t, privKey.Equals(privKey2)) 25 | 26 | // validate Ethereum address equality 27 | addr := privKey.PubKey().Address() 28 | expectedAddr := ethcrypto.PubkeyToAddress(privKey.ToECDSA().PublicKey) 29 | require.Equal(t, expectedAddr.Bytes(), addr.Bytes()) 30 | 31 | // validate we can sign some bytes 32 | msg := []byte("hello world") 33 | sigHash := ethcrypto.Keccak256Hash(msg) 34 | expectedSig, _ := ethsecp256k1.Sign(sigHash.Bytes(), privKey) 35 | 36 | sig, err := privKey.Sign(msg) 37 | require.NoError(t, err) 38 | require.Equal(t, expectedSig, sig) 39 | } 40 | 41 | func TestPrivKeyPubKey(t *testing.T) { 42 | privKey, err := GenerateKey() 43 | require.NoError(t, err) 44 | 45 | // validate type and equality 46 | pubKey := privKey.PubKey().(PubKey) 47 | require.Implements(t, (*tmcrypto.PubKey)(nil), pubKey) 48 | 49 | // validate inequality 50 | privKey2, err := GenerateKey() 51 | require.NoError(t, err) 52 | require.False(t, pubKey.Equals(privKey2.PubKey())) 53 | 54 | // validate signature 55 | msg := []byte("hello world") 56 | sig, err := privKey.Sign(msg) 57 | require.NoError(t, err) 58 | 59 | res := pubKey.VerifyBytes(msg, sig) 60 | require.True(t, res) 61 | } 62 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | 3 | services: 4 | ethermintdnode0: 5 | container_name: ethermintdnode0 6 | image: "ethermintd/node" 7 | ports: 8 | - "26656-26657:26656-26657" 9 | - "1317:1317" 10 | - "8545:8545" 11 | - "8546:8546" 12 | environment: 13 | - ID=0 14 | - LOG=${LOG:-ethermintd.log} 15 | volumes: 16 | - ./build:/ethermint:Z 17 | networks: 18 | localnet: 19 | ipv4_address: 192.168.10.2 20 | entrypoint: "bash start.sh" 21 | 22 | ethermintdnode1: 23 | container_name: ethermintdnode1 24 | image: "ethermintd/node" 25 | ports: 26 | - "26659-26660:26656-26657" 27 | - "1318:1317" 28 | - "8547:8545" 29 | - "8548:8546" 30 | environment: 31 | - ID=1 32 | - LOG=${LOG:-ethermintd.log} 33 | volumes: 34 | - ./build:/ethermint:Z 35 | networks: 36 | localnet: 37 | ipv4_address: 192.168.10.3 38 | entrypoint: "bash start.sh" 39 | 40 | ethermintdnode2: 41 | container_name: ethermintdnode2 42 | image: "ethermintd/node" 43 | environment: 44 | - ID=2 45 | - LOG=${LOG:-ethermintd.log} 46 | ports: 47 | - "26661-26662:26656-26657" 48 | - "1319:1317" 49 | - "8549:8545" 50 | - "8550:8546" 51 | volumes: 52 | - ./build:/ethermint:Z 53 | networks: 54 | localnet: 55 | ipv4_address: 192.168.10.4 56 | entrypoint: "bash start.sh" 57 | 58 | ethermintdnode3: 59 | container_name: ethermintdnode3 60 | image: "ethermintd/node" 61 | environment: 62 | - ID=3 63 | - LOG=${LOG:-ethermintd.log} 64 | ports: 65 | - "26663-26664:26656-26657" 66 | - "1320:1317" 67 | - "8551:8545" 68 | - "8552:8546" 69 | volumes: 70 | - ./build:/ethermint:Z 71 | networks: 72 | localnet: 73 | ipv4_address: 192.168.10.5 74 | entrypoint: "bash start.sh" 75 | 76 | networks: 77 | localnet: 78 | driver: bridge 79 | ipam: 80 | driver: default 81 | config: 82 | - subnet: 192.168.10.0/16 83 | -------------------------------------------------------------------------------- /docs/.vuepress/components/TmIconEthereumIntro.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.vuepress/components/TmLogoEthereumBlack.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.vuepress/components/TmLogoEthereumWhite.vue: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/.vuepress/styles/index.styl: -------------------------------------------------------------------------------- 1 | :root 2 | --color-link #3171B0 3 | --color-primary #7499BF -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | 38 | 39 | # Ethermint Documentation 40 | 41 | ## Get Started 42 | 43 | - **[Introduction](./intro/overview.md)**: High-level overview of Ethermint. 44 | 45 | ## Reference 46 | 47 | - **[Basics](./basics/)**: Documentation on the basic concepts of Ethermint, like the standard anatomy of an application, the transaction lifecycle and accounts management. 48 | - **[Core](./core/)**: Documentation on the core concepts of Ethermint, like `encoding`, and `events`. 49 | - **[Building Modules](./building-modules/)**: Important concepts for module developers like `message`s, `keeper`s, `handler`s and `querier`s. 50 | - **[Interfaces](./interfaces/)**: Documentation on building interfaces for Ethermint applications. 51 | 52 | ## Other Resources 53 | 54 | - **[Module Directory](../x/)**: Module implementations and their respective documentation. 55 | - **[Ethermint API Reference](https://godoc.org/github.com/cosmos/ethermint)**: Godocs of Ethermint. 56 | - **[REST API spec](https://cosmos.network/rpc/)**: List of REST endpoints to interact with an full-node through REST. 57 | 58 | ## Contribute 59 | 60 | See [this file](https://github.com/cosmos/ethermint/blob/development/docs/DOCS_README.md) for details of the build process and considerations when making changes. 61 | -------------------------------------------------------------------------------- /docs/architecture/README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Architecture Decision Records (ADR) 8 | 9 | This is a location to record all high-level architecture decisions in Ethermint. 10 | 11 | You can read more about the ADR concept in this blog posts: 12 | 13 | - [GitHub - Why Write ADRs](https://github.blog/2020-08-13-why-write-adrs/) 14 | - [Reverb - Documenting architecture decisions, the Reverb way](https://product.reverb.com/documenting-architecture-decisions-the-reverb-way-a3563bb24bd0#.78xhdix6t) 15 | 16 | An ADR should provide: 17 | 18 | - Context on the relevant goals and the current state 19 | - Proposed changes to achieve the goals 20 | - Summary of pros and cons 21 | - References 22 | - Changelog 23 | 24 | Note the distinction between an ADR and a spec. The ADR provides the context, intuition, reasoning, and 25 | justification for a change in architecture, or for the architecture of something 26 | new. The spec is much more compressed and streamlined summary of everything as 27 | it stands today. 28 | 29 | If recorded decisions turned out to be lacking, convene a discussion, record the new decisions here, and then modify the code to match. 30 | 31 | Note the context/background should be written in the present tense. 32 | 33 | Please add a entry below in your Pull Request for an ADR. 34 | 35 | ## ADR Table of Contents 36 | 37 | 41 | -------------------------------------------------------------------------------- /docs/architecture/adr-template.md: -------------------------------------------------------------------------------- 1 | # ADR {XXX}: {TITLE} 2 | 3 | ## Changelog 4 | 5 | - {date}: {changelog} 6 | 7 | ## Status 8 | 9 | > A decision may be "proposed" if the project stakeholders haven't agreed with it yet, or "accepted" once it is agreed. If a later ADR changes or reverses a decision, it may be marked as "deprecated" or "superseded" with a reference to its replacement. 10 | > {Deprecated|Proposed|Accepted} {Implemented|Not Implemented} 11 | 12 | ## Context 13 | 14 | > This section describes the forces at play, including technological, political, social, and project local. These forces are probably in tension, and should be called out as such. The language in this section is value-neutral. It is simply describing facts. 15 | > {context body} 16 | 17 | ## Decision 18 | 19 | > This section describes our response to these forces. It is stated in full sentences, with active voice. "We will ..." 20 | > {decision body} 21 | 22 | ## Consequences 23 | 24 | > This section describes the resulting context, after applying the decision. All consequences should be listed here, not just the "positive" ones. A particular decision may have positive, negative, and neutral consequences, but all of them affect the team and project in the future. 25 | 26 | ### Positive 27 | 28 | {positive consequences} 29 | 30 | ### Negative 31 | 32 | {negative consequences} 33 | 34 | ### Neutral 35 | 36 | {neutral consequences} 37 | 38 | ## References 39 | 40 | - {reference link} 41 | -------------------------------------------------------------------------------- /docs/basics/README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Basics 8 | 9 | This repository contains reference documentation on the basic concepts of Ethermint. 10 | 11 | 1. [Accounts](./accounts.md) 12 | 2. [Gas and Fees](./gas.md) 13 | 3. [Lifecycle of a transaction](./transactions.md) 14 | 4. [Photon](./photon.md) 15 | 5. [JSON-RPC Server](./json_rpc.md) 16 | 17 | After reading the basics, head on to the [Core Reference](../core/README.md) for more advanced material. 18 | -------------------------------------------------------------------------------- /docs/basics/gas.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Gas and Fees 6 | 7 | Learn about the differences between `Gas` and `Fees` in Ethereum and Cosmos. {synopsis} 8 | 9 | ## Introduction to `Gas` in the SDK 10 | 11 | 12 | 13 | ## Matching EVM Gas consumption 14 | 15 | 16 | 17 | ## Gas refunds 18 | 19 | 20 | 21 | ## AnteHandler 22 | 23 | The `AnteHandler` is a special `handler` that is run for every transaction during `CheckTx` and `DeliverTx`, before the `handler` of each `message` in the transaction. `AnteHandler`s have a different signature than `handler`s 24 | 25 | 26 | 27 | ## Next {hide} 28 | 29 | Learn about the [Photon](./photon.md) token {hide} 30 | -------------------------------------------------------------------------------- /docs/basics/img/photon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChainSafe/ethermint/0870a270183d445745b1933310a6796d319e52b0/docs/basics/img/photon.png -------------------------------------------------------------------------------- /docs/basics/photon.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Photon 6 | 7 | Learn about the Photon, Ethermint's staking token. {synopsis} 8 | 9 | ## Introduction 10 | 11 | ::: tip 12 | The photon's initial distribution and supply is still TBD and will be announced in the future. 13 | ::: 14 | 15 | The photon is the staking token used in Ethermint. 16 | 17 | ## Base Denomination 18 | 19 | Ethermint uses [Atto](https://en.wikipedia.org/wiki/Atto-) Photon as the base denomination to maintain parity with Ethereum. 20 | 21 | ``` 22 | 1 photon = 1×10⁻¹⁸ aphoton 23 | ``` 24 | 25 | This matches Ethereum denomination of: 26 | 27 | ``` 28 | 1 ETH = 1x10⁻¹⁸ wei 29 | ``` 30 | 31 | ## Next {hide} 32 | 33 | Learn about the supported [JSON-RPC](./json_rpc.md) methods on Ethermint {hide} 34 | -------------------------------------------------------------------------------- /docs/basics/transactions.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Transactions 6 | 7 | ## Routing 8 | 9 | Ethermint needs to parse and handle transactions routed for both the EVM and for the Cosmos hub. We 10 | attempt to achieve this by mimicking [Geth's](https://github.com/ethereum/go-ethereum) `Transaction` 11 | structure and treat it as a unique Cosmos SDK message type. An Ethereum transaction is a single 12 | [`sdk.Msg`](https://godoc.org/github.com/cosmos/cosmos-sdk/types#Msg) contained in an 13 | [`auth.StdTx`](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth#StdTx). All relevant Ethereum 14 | transaction information is contained in this message. This includes the signature, gas, payload, 15 | etc. 16 | 17 | Being that Ethermint implements the Tendermint ABCI application interface, as transactions are 18 | consumed, they are passed through a series of handlers. Once such handler, the `AnteHandler`, is 19 | responsible for performing preliminary message execution business logic such as fee payment, 20 | signature verification, etc. This is particular to Cosmos SDK routed transactions. Ethereum routed 21 | transactions will bypass this as the EVM handles the same business logic. 22 | 23 | Ethereum routed transactions coming from a web3 source are expected to be RLP encoded, however all 24 | internal interaction between Ethermint and Tendermint will utilize one of the supported encoding 25 | formats: Protobuf, Amino or Hybrid of the previous two. 26 | 27 | ## Transaction formats 28 | 29 | 30 | 31 | - Cosmos transactions 32 | - Ethereum transaction 33 | 34 | ## Signatures 35 | 36 | Ethermint supports [EIP-155](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md) 37 | signatures. A `Transaction` is expected to have a single signature for Ethereum routed transactions. 38 | However, just as in Cosmos, Ethermint will support multiple signers for non-Ethereum transactions. 39 | Signatures over the `Transaction` type are identical to Ethereum and the signatures will not be 40 | duplicated in the embedding 41 | [`auth.StdTx`](https://godoc.org/github.com/cosmos/cosmos-sdk/x/auth#StdTx). 42 | 43 | ## Next {hide} 44 | 45 | Learn about how [gas](./gas.md) is used on Ethermint {hide} 46 | -------------------------------------------------------------------------------- /docs/core/README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Core Concepts 8 | 9 | This repository contains reference documentation on the core concepts of Ethermint. 10 | 11 | 1. [Encoding](./encoding.md) 12 | 13 | After reading the core concepts, head on to the [guides](../guides/README.md) to learn how to use Ethereum tooling with Ethermint. 14 | -------------------------------------------------------------------------------- /docs/guides/README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Guides 8 | 9 | This section contains different guides to use popular Ethereum tools with Ethermint. 10 | 11 | 1. [Truffle](./truffle.md) 12 | 2. [Metamask](./metamask.md) 13 | 3. [Remix](./remix.md) 14 | 4. [Deploy Testnet on Cloud Provider](./cloud_testnet.md) 15 | -------------------------------------------------------------------------------- /docs/guides/img/metamask_import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChainSafe/ethermint/0870a270183d445745b1933310a6796d319e52b0/docs/guides/img/metamask_import.png -------------------------------------------------------------------------------- /docs/guides/img/metamask_network_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChainSafe/ethermint/0870a270183d445745b1933310a6796d319e52b0/docs/guides/img/metamask_network_settings.png -------------------------------------------------------------------------------- /docs/guides/img/remix_deploy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChainSafe/ethermint/0870a270183d445745b1933310a6796d319e52b0/docs/guides/img/remix_deploy.png -------------------------------------------------------------------------------- /docs/guides/img/remix_deployed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChainSafe/ethermint/0870a270183d445745b1933310a6796d319e52b0/docs/guides/img/remix_deployed.png -------------------------------------------------------------------------------- /docs/guides/img/remix_interact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChainSafe/ethermint/0870a270183d445745b1933310a6796d319e52b0/docs/guides/img/remix_interact.png -------------------------------------------------------------------------------- /docs/guides/remix.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Remix 6 | 7 | Set up a Remix Ethermint local development environment. {synopsis} 8 | 9 | ## Pre-requisite Readings 10 | 11 | - [Installation](./../quickstart/installation.md) {prereq} 12 | - [Run a node](./../quickstart/run_node.md) {prereq} 13 | - [Metamask](./metamask.md) {prereq} 14 | 15 | [Remix](http://remix.ethereum.org/) is an in-browser IDE for [Solidity](https://github.com/ethereum/solidity) smart contracts. In this guide, we will learn how to deploy a contract to a running Ethermint network through Remix and interact with it. 16 | 17 | ## Connect Ethermint account to Remix 18 | 19 | First, follow the steps in the [Metamask guide](./metamask.md) to import your Ethermint private key into Metamask. Start the Ethermint daemon and rest server. 20 | 21 | Once that is complete, go to [Remix](http://remix.ethereum.org/). There are some contracts in the File Explorer. Select any of these contracts. In this example, we use `Counter.sol` from the [Truffle](./truffle.md) guide. On the left-most bar, select the Solidity Compiler and compile the contract. 22 | 23 | Next, select the `Deploy and Run` option. Select `injected web3` as the environment. This will open a metamask popup for you to confirm connecting your Metamask to Remix. Hit confirm. 24 | 25 | You should see your account show up in the left-hand panel. 26 | 27 | ![remix connected to ethermint](./img/remix_deploy.png) 28 | 29 | ## Deploy and Interact 30 | 31 | Now that your account is connected, you are able to deploy the contract. Press the `Deploy` button. A metamask pop-up will appear asking you to confirm. Confirm the transaction. You should see a log for the deployment transaction in the ethermint daemon logs: 32 | 33 | ```bash 34 | I[2020-07-15|17:26:43.155] Added good transaction module=mempool tx=877A8E6600FA27EC2B2362719274314977B243671DC4E5F8796ED97FFC0CBE42 res="&{CheckTx:log:\"[]\" gas_wanted:121193 }" height=31 total=1 35 | ``` 36 | 37 | Once the contract has been successfully deployed, you will see it show up in the `Deployed Contracts` section in the left-hand side, as well as a green check in the Remix console showing the transaction details. 38 | 39 | ![deployed contract through remix](./img/remix_deployed.png) 40 | 41 | Now, you are able to interact with the contract through Remix. For `Counter.sol`, click `add`. This will open a Metamask pop-up asking you to confirm. Confirm the transaction. Then, click `getCounter` to get the count, which should be `1`. 42 | 43 | ![interacting with deployed contract through remix](./img/remix_interact.png) 44 | -------------------------------------------------------------------------------- /docs/intro/README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Introduction 8 | 9 | This folder contains introduction material for Ethermint. 10 | 11 | 1. [Overview](./overview.md) 12 | 1. [Architecture](./architecture.md) 13 | 1. [Resources](./resources.md) 14 | 15 | After reading the introduction material, head over to the [basics](../basics/README.md) to learn more. 16 | 17 | ## Next {hide} 18 | 19 | Get an high-level [overview](./overview.md) of Ethermint {hide} 20 | -------------------------------------------------------------------------------- /docs/intro/architecture.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Architecture 6 | 7 | Learn how Ethermint's architecture leverages the Cosmos SDK Proof-of-Stake functionality, EVM compatibility and fast-finality from Tendermint Core's BFT consensus. {synopsis} 8 | 9 | ## Cosmos-SDK 10 | 11 | 12 | 13 | ## Tendermint Core & the Application Blockchain Interface (ABCI) 14 | 15 | Tendermint consists of two chief technical components: a blockchain consensus 16 | engine and a generic application interface. The consensus engine, called 17 | Tendermint Core, ensures that the same transactions are recorded on every machine 18 | in the same order. The application interface, called the Application Blockchain 19 | Interface (ABCI), enables the transactions to be processed in any programming 20 | language. 21 | 22 | Tendermint has evolved to be a general purpose blockchain consensus engine that 23 | can host arbitrary application states. Since Tendermint can replicate arbitrary 24 | applications, it can be used as a plug-and-play replacement for the consensus 25 | engines of other blockchains. Ethermint is such an example of an ABCI application 26 | replacing Ethereum's PoW via Tendermint's consensus engine. 27 | 28 | Another example of a cryptocurrency application built on Tendermint is the Cosmos 29 | network. Tendermint is able to decompose the blockchain design by offering a very 30 | simple API (ie. the ABCI) between the application process and consensus process. 31 | 32 | ## EVM module 33 | 34 | 35 | 36 | ## Next {hide} 37 | 38 | Check the available Ethermint [resources](./resources.md) {hide} 39 | -------------------------------------------------------------------------------- /docs/intro/overview.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # High-level Overview 6 | 7 | ## What is Ethermint 8 | 9 | Ethermint is a scalable, high-throughput Proof-of-Stake blockchain that is fully compatible and 10 | interoperable with Ethereum. It's built using the [Cosmos SDK](https://github.com/cosmos/cosmos-sdk/) which runs on top of [Tendermint Core](https://github.com/tendermint/tendermint) consensus engine. 11 | 12 | Ethermint allows for running vanilla Ethereum as a [Cosmos](https://cosmos.network/) application-specific blockchain. This allows developers 13 | to have all the desired features of Ethereum, while at the same time, benefit 14 | from Tendermint’s PoS implementation. Also, because it is built on top of the 15 | Cosmos SDK, it will be able to exchange value with the rest of the Cosmos Ecosystem through the Inter Blockchain Communication Protocol (IBC). 16 | 17 | ### Features 18 | 19 | Here’s a glance at some of the key features of Ethermint: 20 | 21 | * Web3 compatibility 22 | * High throughput via [Tendermint Core](https://github.com/tendermint/tendermint) 23 | * Horizontal scalability via [IBC](https://github.com/cosmos/ics) 24 | * Fast transaction finality 25 | * [Hard Spoon](https://blog.cosmos.network/introducing-the-hard-spoon-4a9288d3f0df) 26 | 27 | Ethermint enables these key features through: 28 | 29 | * Implementing Tendermint Core's ABCI application interface to manage the blockchain 30 | * Leveraging [modules](https://github.com/cosmos/cosmos-sdk/tree/master/x/) and other mechanisms implemented by the Cosmos SDK 31 | * Utilizing [`geth`](https://github.com/ethereum/go-ethereum) as a library to avoid code reuse and improve maintainability. 32 | * Exposing a fully compatible Web3 RPC layer for interacting with existing Ethereum clients and tooling (Metamask, Remix, Truffle, etc). 33 | 34 | The sum of these features allows developers to leverage existing Ethereum ecosystem tooling and 35 | software to seamlessly deploy smart contracts which interact with the rest of the Cosmos ecosystem! 36 | 37 | ## Next {hide} 38 | 39 | Learn about Ethermint's [architecture](./architectures.md) {hide} 40 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "1.0.0", 4 | "description": "Ethermint Documentation", 5 | "main": "index.js", 6 | "scripts": { 7 | "preserve": "./pre.sh", 8 | "serve": "trap 'exit 0' SIGINT; vuepress dev --no-cache", 9 | "postserve": "./post.sh", 10 | "prebuild": "./pre.sh", 11 | "build": "trap 'exit 0' SIGINT; vuepress build --no-cache", 12 | "postbuild": "./post.sh" 13 | }, 14 | "keywords": [ 15 | "ethermint", 16 | "cosmos", 17 | "ethereum", 18 | "blockchain", 19 | "cryptocurrency" 20 | ], 21 | "author": "ChainSafe Systems", 22 | "license": "ISC", 23 | "dependencies": { 24 | "entities": "^2.0.3", 25 | "vuepress-theme-cosmos": "^1.0.172" 26 | }, 27 | "devDependencies": { 28 | "watchpack": "^1.7.2" 29 | }, 30 | "resolutions": { 31 | "serialize-javascript": "^4.0.0" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docs/post.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Modules 4 | rm -rf modules 5 | -------------------------------------------------------------------------------- /docs/pre.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir -p modules 4 | 5 | for D in ../x/*; do 6 | if [ -d "${D}" ]; then 7 | rm -rf "modules/$(echo $D | awk -F/ '{print $NF}')" 8 | mkdir -p "modules/$(echo $D | awk -F/ '{print $NF}')" && cp -r $D/spec/* "$_" 9 | fi 10 | done 11 | 12 | cat ../x/README.md | sed 's/\.\/x/\/modules/g' | sed 's/spec\/README.md//g' -------------------------------------------------------------------------------- /docs/quickstart/README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Quick Start 8 | 9 | This repository contains reference documentation on how to install and run an Etheremint full node. 10 | 11 | 1. [Installation](./installation.md) 12 | 2. [Run a Node](./run_node.md) 13 | 3. [Testnet](./testnet.md) 14 | 4. [Validator Setup](./validator-setup.md) 15 | 5. [Upgrade](./upgrade.md) 16 | 6. [Clients](./clients.md) 17 | 7. [Events](./events.md) 18 | 19 | After going throught the Quick Start contents, head over to the [basics](./../basics/README.md) to learn more. 20 | 21 | ## Next {hide} 22 | 23 | Learn how to [install](./../quickstart/intallation.md) Ethermint {hide} 24 | -------------------------------------------------------------------------------- /docs/quickstart/clients.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Clients 6 | 7 | Learn how to connect a client to a running node. {synopsis} 8 | 9 | ## Pre-requisite Readings 10 | 11 | - [Run a Node](./run_node.md) {prereq} 12 | 13 | ## Client Integrations 14 | 15 | ### Command Line Interface 16 | 17 | Ethermint is integrated with a CLI client that can be used to send transactions and query the state from each module. 18 | 19 | ```bash 20 | # available query commands 21 | ethermintcli query -h 22 | 23 | # available transaction commands 24 | ethermintcli tx -h 25 | ``` 26 | 27 | ### Client Servers 28 | 29 | The Ethermint client supports both [REST endpoints](https://cosmos.network/rpc) from the SDK and Ethereum's [JSON-RPC](https://eth.wiki/json-rpc/API). 30 | 31 | #### REST and Tendermint RPC 32 | 33 | Ethermint exposes REST endpoints for all the integrated Cosmos-SDK modules. This makes it easier for wallets and block explorers to interact with the proof-of-stake logic. 34 | 35 | To run the REST Server, you need to run the Ethermint daemon (`ethermintd`) and then execute (in another 36 | process): 37 | 38 | ```bash 39 | ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key $KEY --chain-id $CHAINID --trace 40 | ``` 41 | 42 | You should see the logs from the REST and the RPC server. 43 | 44 | ```bash 45 | I[2020-07-17|16:54:35.037] Starting application REST service (chain-id: "8")... module=rest-server 46 | I[2020-07-17|16:54:35.037] Starting RPC HTTP server on 127.0.0.1:8545 module=rest-server 47 | ``` 48 | 49 | #### Ethereum JSON-RPC server 50 | 51 | Ethermint also supports most of the standard web3 [JSON-RPC 52 | APIs](https://eth.wiki/json-rpc/API) to connect with existing web3 tooling. 53 | 54 | ::: tip 55 | Some of the JSON-RPC API [namespaces](https://geth.ethereum.org/docs/rpc/server) are currently under development. 56 | ::: 57 | 58 | To connect to the JSON-PRC server, use the `rest-server` command as shown on the section above. Then, you can point any Ethereum development tooling to `http://localhost:8545` or whatever port you choose with the listen address flag (`--laddr`). 59 | 60 | For further information JSON-RPC calls, please refer to [this](../basics/json_rpc.md) document. 61 | 62 | ## Next {hide} 63 | 64 | Process and subscribe to [events](./events.md) via websockets {hide} 65 | -------------------------------------------------------------------------------- /docs/quickstart/installation.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Installation 6 | 7 | ## Binaries 8 | 9 | Clone and build Ethermint using `git`: 10 | 11 | ```bash 12 | git clone https://github.com/cosmos/ethermint.git 13 | cd ethermint 14 | make install 15 | ``` 16 | 17 | Check that the binaries have been successfuly installed: 18 | 19 | ```bash 20 | ethermintd -h 21 | ethermintcli -h 22 | ``` 23 | 24 | ## Docker 25 | 26 | You can build Ethermint using Docker by running: 27 | 28 | ```bash 29 | make docker-build 30 | ``` 31 | 32 | This will install the binaries on the `./build` directory. Now, check that the binaries have been 33 | successfuly installed: 34 | 35 | ```bash 36 | ethermintd -h 37 | ethermintcli -h 38 | ``` 39 | 40 | ## Releases 41 | 42 | ::: warning 43 | Ethermint is under VERY ACTIVE DEVELOPMENT and should be treated as pre-alpha software. This means it is not meant to be run in production, its APIs are subject to change without warning and should not be relied upon, and it should not be used to hold any value. We will remove this warning when we have a release that is stable, secure, and properly tested. 44 | ::: 45 | 46 | You can also download a specific release available on the [Ethermint repository](https://github.com/cosmos/ethermint/releases) 47 | 48 | ## Next {hide} 49 | 50 | Learn how to [run a node](./.run_node.md) {hide} 51 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/cosmos/ethermint 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/aristanetworks/goarista v0.0.0-20200331225509-2cc472e8fbd6 // indirect 7 | github.com/btcsuite/btcd v0.21.0-beta 8 | github.com/btcsuite/btcutil v1.0.2 9 | github.com/cespare/cp v1.1.1 // indirect 10 | github.com/cosmos/cosmos-sdk v0.39.1 11 | github.com/deckarep/golang-set v1.7.1 // indirect 12 | github.com/ethereum/go-ethereum v1.9.21 13 | github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 // indirect 14 | github.com/gorilla/mux v1.8.0 15 | github.com/gorilla/websocket v1.4.2 16 | github.com/mattn/go-colorable v0.1.7 // indirect 17 | github.com/miguelmota/go-ethereum-hdwallet v0.0.0-20200123000308-a60dcd172b4c 18 | github.com/pkg/errors v0.9.1 19 | github.com/prometheus/tsdb v0.9.1 // indirect 20 | github.com/spf13/afero v1.2.2 // indirect 21 | github.com/spf13/cobra v1.1.0 22 | github.com/spf13/viper v1.7.1 23 | github.com/status-im/keycard-go v0.0.0-20190424133014-d95853db0f48 24 | github.com/stretchr/testify v1.6.1 25 | github.com/tendermint/tendermint v0.33.7 26 | github.com/tendermint/tm-db v0.5.1 27 | github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef 28 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 29 | gopkg.in/yaml.v2 v2.3.0 30 | ) 31 | -------------------------------------------------------------------------------- /gometalinter.json: -------------------------------------------------------------------------------- 1 | { 2 | "Linters": { 3 | "vet": "go tool vet -composites=false :PATH:LINE:MESSAGE" 4 | }, 5 | "Enable": [ 6 | "golint", 7 | "vet", 8 | "ineffassign", 9 | "unparam", 10 | "unconvert", 11 | "misspell" 12 | ], 13 | "Deadline": "500s", 14 | "Vendor": true, 15 | "Cyclo": 11 16 | } -------------------------------------------------------------------------------- /importer/blockchain: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChainSafe/ethermint/0870a270183d445745b1933310a6796d319e52b0/importer/blockchain -------------------------------------------------------------------------------- /networks/local/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | docker build --tag ethermintd/node ethermintnode 3 | 4 | .PHONY: all 5 | -------------------------------------------------------------------------------- /networks/local/ethermintnode/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:stretch as build-env 2 | 3 | # Install minimum necessary dependencies 4 | ENV PACKAGES curl make git libc-dev bash gcc 5 | RUN apt-get update && apt-get upgrade -y && \ 6 | apt-get install -y $PACKAGES 7 | 8 | # Set working directory for the build 9 | WORKDIR /go/src/github.com/cosmos/ethermint 10 | 11 | # Add source files 12 | COPY . . 13 | 14 | # build Ethermint 15 | RUN make build-ethermint-linux 16 | 17 | # Final image 18 | FROM golang:1.14 as final 19 | 20 | WORKDIR / 21 | 22 | RUN apt-get update 23 | 24 | # Copy over binaries from the build-env 25 | COPY --from=build-env /go/src/github.com/cosmos/ethermint/build/ethermintd /usr/bin/ethermintd 26 | COPY --from=build-env /go/src/github.com/cosmos/ethermint/build/ethermintcli /usr/bin/ethermintcli 27 | COPY --from=build-env /go/src/github.com/cosmos/ethermint/scripts/start.sh / 28 | 29 | EXPOSE 26656 26657 1317 8545 8546 30 | 31 | # Run ethermintd by default, omit entrypoint to ease using container with ethermintcli 32 | ENTRYPOINT ["/bin/bash", "-c"] -------------------------------------------------------------------------------- /rpc/addrlock.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "sync" 5 | 6 | "github.com/ethereum/go-ethereum/common" 7 | ) 8 | 9 | // AddrLocker is a mutex structure used to avoid querying outdated account data 10 | type AddrLocker struct { 11 | mu sync.Mutex 12 | locks map[common.Address]*sync.Mutex 13 | } 14 | 15 | // lock returns the lock of the given address. 16 | func (l *AddrLocker) lock(address common.Address) *sync.Mutex { 17 | l.mu.Lock() 18 | defer l.mu.Unlock() 19 | if l.locks == nil { 20 | l.locks = make(map[common.Address]*sync.Mutex) 21 | } 22 | if _, ok := l.locks[address]; !ok { 23 | l.locks[address] = new(sync.Mutex) 24 | } 25 | return l.locks[address] 26 | } 27 | 28 | // LockAddr locks an account's mutex. This is used to prevent another tx getting the 29 | // same nonce until the lock is released. The mutex prevents the (an identical nonce) from 30 | // being read again during the time that the first transaction is being signed. 31 | func (l *AddrLocker) LockAddr(address common.Address) { 32 | l.lock(address).Lock() 33 | } 34 | 35 | // UnlockAddr unlocks the mutex of the given account. 36 | func (l *AddrLocker) UnlockAddr(address common.Address) { 37 | l.lock(address).Unlock() 38 | } 39 | -------------------------------------------------------------------------------- /rpc/apis.go: -------------------------------------------------------------------------------- 1 | // Package rpc contains RPC handler methods and utilities to start 2 | // Ethermint's Web3-compatibly JSON-RPC server. 3 | package rpc 4 | 5 | import ( 6 | "github.com/ethereum/go-ethereum/rpc" 7 | 8 | "github.com/cosmos/cosmos-sdk/client/context" 9 | 10 | "github.com/cosmos/ethermint/crypto/ethsecp256k1" 11 | ) 12 | 13 | // RPC namespaces and API version 14 | const ( 15 | Web3Namespace = "web3" 16 | EthNamespace = "eth" 17 | PersonalNamespace = "personal" 18 | NetNamespace = "net" 19 | 20 | apiVersion = "1.0" 21 | ) 22 | 23 | // GetRPCAPIs returns the list of all APIs 24 | func GetRPCAPIs(cliCtx context.CLIContext, keys []ethsecp256k1.PrivKey) []rpc.API { 25 | nonceLock := new(AddrLocker) 26 | backend := NewEthermintBackend(cliCtx) 27 | ethAPI := NewPublicEthAPI(cliCtx, backend, nonceLock, keys) 28 | 29 | return []rpc.API{ 30 | { 31 | Namespace: Web3Namespace, 32 | Version: apiVersion, 33 | Service: NewPublicWeb3API(), 34 | Public: true, 35 | }, 36 | { 37 | Namespace: EthNamespace, 38 | Version: apiVersion, 39 | Service: ethAPI, 40 | Public: true, 41 | }, 42 | { 43 | Namespace: PersonalNamespace, 44 | Version: apiVersion, 45 | Service: NewPersonalEthAPI(ethAPI), 46 | Public: false, 47 | }, 48 | { 49 | Namespace: EthNamespace, 50 | Version: apiVersion, 51 | Service: NewPublicFilterAPI(cliCtx, backend), 52 | Public: true, 53 | }, 54 | { 55 | Namespace: NetNamespace, 56 | Version: apiVersion, 57 | Service: NewPublicNetAPI(cliCtx), 58 | Public: true, 59 | }, 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /rpc/args/send_tx.go: -------------------------------------------------------------------------------- 1 | package args 2 | 3 | import ( 4 | "github.com/ethereum/go-ethereum/common" 5 | "github.com/ethereum/go-ethereum/common/hexutil" 6 | ) 7 | 8 | // SendTxArgs represents the arguments to submit a new transaction into the transaction pool. 9 | // Duplicate struct definition since geth struct is in internal package 10 | // Ref: https://github.com/ethereum/go-ethereum/blob/release/1.9/internal/ethapi/api.go#L1346 11 | type SendTxArgs struct { 12 | From common.Address `json:"from"` 13 | To *common.Address `json:"to"` 14 | Gas *hexutil.Uint64 `json:"gas"` 15 | GasPrice *hexutil.Big `json:"gasPrice"` 16 | Value *hexutil.Big `json:"value"` 17 | Nonce *hexutil.Uint64 `json:"nonce"` 18 | // We accept "data" and "input" for backwards-compatibility reasons. "input" is the 19 | // newer name and should be preferred by clients. 20 | Data *hexutil.Bytes `json:"data"` 21 | Input *hexutil.Bytes `json:"input"` 22 | } 23 | -------------------------------------------------------------------------------- /rpc/net_api.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/spf13/viper" 7 | 8 | "github.com/cosmos/cosmos-sdk/client/context" 9 | "github.com/cosmos/cosmos-sdk/client/flags" 10 | ethermint "github.com/cosmos/ethermint/types" 11 | ) 12 | 13 | // PublicNetAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec. 14 | type PublicNetAPI struct { 15 | networkVersion uint64 16 | } 17 | 18 | // NewPublicNetAPI creates an instance of the public Net Web3 API. 19 | func NewPublicNetAPI(_ context.CLIContext) *PublicNetAPI { 20 | chainID := viper.GetString(flags.FlagChainID) 21 | // parse the chainID from a integer string 22 | chainIDEpoch, err := ethermint.ParseChainID(chainID) 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | return &PublicNetAPI{ 28 | networkVersion: chainIDEpoch.Uint64(), 29 | } 30 | } 31 | 32 | // Version returns the current ethereum protocol version. 33 | func (s *PublicNetAPI) Version() string { 34 | return fmt.Sprintf("%d", s.networkVersion) 35 | } 36 | -------------------------------------------------------------------------------- /rpc/types.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/big" 7 | "strings" 8 | 9 | "github.com/ethereum/go-ethereum/common/hexutil" 10 | ) 11 | 12 | // BlockNumber represents decoding hex string to block values 13 | type BlockNumber int64 14 | 15 | const ( 16 | // LatestBlockNumber mapping from "latest" to 0 for tm query 17 | LatestBlockNumber = BlockNumber(0) 18 | 19 | // EarliestBlockNumber mapping from "earliest" to 1 for tm query (earliest query not supported) 20 | EarliestBlockNumber = BlockNumber(1) 21 | ) 22 | 23 | // NewBlockNumber creates a new BlockNumber instance. 24 | func NewBlockNumber(n *big.Int) BlockNumber { 25 | return BlockNumber(n.Int64()) 26 | } 27 | 28 | // UnmarshalJSON parses the given JSON fragment into a BlockNumber. It supports: 29 | // - "latest", "earliest" or "pending" as string arguments 30 | // - the block number 31 | // Returned errors: 32 | // - an invalid block number error when the given argument isn't a known strings 33 | // - an out of range error when the given block number is either too little or too large 34 | func (bn *BlockNumber) UnmarshalJSON(data []byte) error { 35 | input := strings.TrimSpace(string(data)) 36 | if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { 37 | input = input[1 : len(input)-1] 38 | } 39 | 40 | switch input { 41 | case "earliest": 42 | *bn = EarliestBlockNumber 43 | return nil 44 | case "latest": 45 | *bn = LatestBlockNumber 46 | return nil 47 | case "pending": 48 | *bn = LatestBlockNumber 49 | return nil 50 | } 51 | 52 | blckNum, err := hexutil.DecodeUint64(input) 53 | if err != nil { 54 | return err 55 | } 56 | if blckNum > math.MaxInt64 { 57 | return fmt.Errorf("blocknumber too high") 58 | } 59 | 60 | *bn = BlockNumber(blckNum) 61 | return nil 62 | } 63 | 64 | // Int64 converts block number to primitive type 65 | func (bn BlockNumber) Int64() int64 { 66 | return (int64)(bn) 67 | } 68 | -------------------------------------------------------------------------------- /rpc/web3_api.go: -------------------------------------------------------------------------------- 1 | package rpc 2 | 3 | import ( 4 | "github.com/cosmos/ethermint/version" 5 | 6 | "github.com/ethereum/go-ethereum/common/hexutil" 7 | "github.com/ethereum/go-ethereum/crypto" 8 | ) 9 | 10 | // PublicWeb3API is the web3_ prefixed set of APIs in the Web3 JSON-RPC spec. 11 | type PublicWeb3API struct{} 12 | 13 | // NewPublicWeb3API creates an instance of the Web3 API. 14 | func NewPublicWeb3API() *PublicWeb3API { 15 | return &PublicWeb3API{} 16 | } 17 | 18 | // ClientVersion returns the client version in the Web3 user agent format. 19 | func (a *PublicWeb3API) ClientVersion() string { 20 | return version.ClientVersion() 21 | } 22 | 23 | // Sha3 returns the keccak-256 hash of the passed-in input. 24 | func (a *PublicWeb3API) Sha3(input hexutil.Bytes) hexutil.Bytes { 25 | return crypto.Keccak256(input) 26 | } 27 | -------------------------------------------------------------------------------- /scripts/protocgen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | proto_dirs=$(find . -path ./third_party -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) 6 | for dir in $proto_dirs; do 7 | protoc \ 8 | -I. \ 9 | --gocosmos_out=plugins=interfacetype,paths=source_relative:. \ 10 | $(find "${dir}" -name '*.proto') 11 | done 12 | -------------------------------------------------------------------------------- /scripts/run-solidity-tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export GOPATH=~/go 4 | export PATH=$PATH:$GOPATH/bin 5 | go build -o ./build/ethermintd ./cmd/ethermintd 6 | go build -o ./build/ethermintcli ./cmd/ethermintcli 7 | mkdir $GOPATH/bin 8 | cp ./build/ethermintd $GOPATH/bin 9 | cp ./build/ethermintcli $GOPATH/bin 10 | 11 | CHAINID="ethermint-1337" 12 | 13 | cd tests-solidity 14 | 15 | if command -v yarn &> /dev/null; then 16 | yarn install 17 | else 18 | curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - 19 | echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list 20 | sudo apt update && sudo apt install yarn 21 | yarn install 22 | fi 23 | 24 | chmod +x ./init-test-node.sh 25 | ./init-test-node.sh > ethermintd.log & 26 | sleep 5 27 | ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key localkey,user1,user2 --chain-id $CHAINID --trace --wsport 8546 > ethermintcli.log & 28 | 29 | cd suites/initializable 30 | yarn test-ethermint 31 | 32 | ok=$? 33 | 34 | if (( $? != 0 )); then 35 | echo "initializable test failed: exit code $?" 36 | fi 37 | 38 | killall ethermintcli 39 | killall ethermintd 40 | 41 | echo "Script exited with code $ok" 42 | exit $ok 43 | 44 | # initializable-buidler fails on CI, re-add later 45 | 46 | ./../../init-test-node.sh > ethermintd.log & 47 | sleep 5 48 | ethermintcli rest-server --laddr "tcp://localhost:8545" --unlock-key localkey,user1,user2 --chain-id $CHAINID --trace --wsport 8546 > ethermintcli.log & 49 | 50 | cd ../initializable-buidler 51 | yarn test-ethermint 52 | 53 | ok=$(($? + $ok)) 54 | 55 | if (( $? != 0 )); then 56 | echo "initializable-buidler test failed: exit code $?" 57 | fi 58 | 59 | killall ethermintcli 60 | killall ethermintd 61 | 62 | echo "Script exited with code $ok" 63 | exit $ok -------------------------------------------------------------------------------- /scripts/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ethermintd --home /ethermint/node$ID/ethermintd/ start > ethermintd.log & 3 | sleep 5 4 | ethermintcli rest-server --laddr "tcp://localhost:8545" --chain-id "ethermint-7305661614933169792" --trace > ethermintcli.log & 5 | tail -f /dev/null -------------------------------------------------------------------------------- /tests-solidity/.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity 2 | -------------------------------------------------------------------------------- /tests-solidity/.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | node_modules/ 3 | 4 | # ignore package-lock files (only use yarn.lock) 5 | package-lock.json 6 | -------------------------------------------------------------------------------- /tests-solidity/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tests-solidity", 3 | "private": true, 4 | "version": "1.0.0", 5 | "author": "Aragon Association ", 6 | "license": "GPL-3.0-or-later", 7 | "workspaces": { 8 | "packages": [ 9 | "suites/*" 10 | ], 11 | "nohoist": [ 12 | "**/@aragon/contract-helpers-test" 13 | ] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests-solidity/suites/basic/contracts/Counter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | contract Counter { 4 | uint256 counter = 0; 5 | string internal constant ERROR_TOO_LOW = "COUNTER_TOO_LOW"; 6 | event Changed(uint256 counter); 7 | event Added(uint256 counter); 8 | 9 | function add() public { 10 | counter++; 11 | emit Added(counter); 12 | emit Changed(counter); 13 | } 14 | 15 | function subtract() public { 16 | require(counter > 0, ERROR_TOO_LOW); 17 | counter--; 18 | emit Changed(counter); 19 | } 20 | 21 | function getCounter() public view returns (uint256) { 22 | return counter; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests-solidity/suites/basic/contracts/test/Migrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.22 <0.8.0; 3 | 4 | contract Migrations { 5 | address public owner = msg.sender; 6 | uint public last_completed_migration; 7 | 8 | modifier restricted() { 9 | require( 10 | msg.sender == owner, 11 | "This function is restricted to the contract's owner" 12 | ); 13 | _; 14 | } 15 | 16 | function setCompleted(uint completed) public restricted { 17 | last_completed_migration = completed; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests-solidity/suites/basic/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /tests-solidity/suites/basic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "basic", 3 | "version": "1.0.0", 4 | "author": "Aragon Association ", 5 | "license": "GPL-3.0-or-later", 6 | "scripts": { 7 | "test-ganache": "yarn truffle test", 8 | "test-ethermint": "yarn truffle test --network ethermint" 9 | }, 10 | "devDependencies": { 11 | "truffle": "^5.1.42", 12 | "web3": "^1.2.11" 13 | }, 14 | "dependencies": { 15 | "truffle": "^5.1.43" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests-solidity/suites/basic/test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChainSafe/ethermint/0870a270183d445745b1933310a6796d319e52b0/tests-solidity/suites/basic/test/.gitkeep -------------------------------------------------------------------------------- /tests-solidity/suites/basic/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // Development network is just left as truffle's default settings 4 | ethermint: { 5 | host: "127.0.0.1", // Localhost (default: none) 6 | port: 8545, // Standard Ethereum port (default: none) 7 | network_id: "*", // Any network (default: none) 8 | gas: 5000000, // Gas sent with each transaction 9 | gasPrice: 1000000000, // 1 gwei (in wei) 10 | }, 11 | }, 12 | compilers: { 13 | solc: { 14 | version: "0.5.17", 15 | }, 16 | }, 17 | } 18 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable-buidler/.gitignore: -------------------------------------------------------------------------------- 1 | # Buidler 2 | artifacts 3 | cache 4 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable-buidler/buidler.config.js: -------------------------------------------------------------------------------- 1 | const { usePlugin } = require('@nomiclabs/buidler/config') 2 | 3 | usePlugin("@nomiclabs/buidler-ganache") 4 | usePlugin('@nomiclabs/buidler-truffle5') 5 | 6 | module.exports = { 7 | networks: { 8 | // Development network is just left as truffle's default settings 9 | ganache: { 10 | url: 'http://localhost:8545', 11 | gasLimit: 5000000, 12 | gasPrice: 1000000000, // 1 gwei (in wei) 13 | defaultBalanceEther: 100 14 | }, 15 | ethermint: { 16 | url: 'http://localhost:8545', 17 | gasLimit: 5000000, // Gas sent with each transaction 18 | gasPrice: 1000000000, // 1 gwei (in wei) 19 | }, 20 | }, 21 | solc: { 22 | version: '0.4.24', 23 | optimizer: { 24 | enabled: true, 25 | runs: 10000, 26 | }, 27 | }, 28 | } 29 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable-buidler/contracts/Initializable.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./TimeHelpers.sol"; 8 | import "./UnstructuredStorage.sol"; 9 | 10 | 11 | contract Initializable is TimeHelpers { 12 | using UnstructuredStorage for bytes32; 13 | 14 | // keccak256("aragonOS.initializable.initializationBlock") 15 | bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e; 16 | 17 | string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED"; 18 | string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED"; 19 | 20 | modifier onlyInit { 21 | require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED); 22 | _; 23 | } 24 | 25 | modifier isInitialized { 26 | require(hasInitialized(), ERROR_NOT_INITIALIZED); 27 | _; 28 | } 29 | 30 | /** 31 | * @return Block number in which the contract was initialized 32 | */ 33 | function getInitializationBlock() public view returns (uint256) { 34 | return INITIALIZATION_BLOCK_POSITION.getStorageUint256(); 35 | } 36 | 37 | /** 38 | * @return Whether the contract has been initialized by the time of the current block 39 | */ 40 | function hasInitialized() public view returns (bool) { 41 | uint256 initializationBlock = getInitializationBlock(); 42 | return initializationBlock != 0 && getBlockNumber() >= initializationBlock; 43 | } 44 | 45 | /** 46 | * @dev Function to be called by top level contract after initialization has finished. 47 | */ 48 | function initialized() internal onlyInit { 49 | INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber()); 50 | } 51 | 52 | /** 53 | * @dev Function to be called by top level contract after initialization to enable the contract 54 | * at a future block number rather than immediately. 55 | */ 56 | function initializedAt(uint256 _blockNumber) internal onlyInit { 57 | INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable-buidler/contracts/Petrifiable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./Initializable.sol"; 4 | 5 | 6 | contract Petrifiable is Initializable { 7 | // Use block UINT256_MAX (which should be never) as the initializable date 8 | uint256 internal constant PETRIFIED_BLOCK = uint256(-1); 9 | 10 | function isPetrified() public view returns (bool) { 11 | return getInitializationBlock() == PETRIFIED_BLOCK; 12 | } 13 | 14 | /** 15 | * @dev Function to be called by top level contract to prevent being initialized. 16 | * Useful for freezing base contracts when they're used behind proxies. 17 | */ 18 | function petrify() internal onlyInit { 19 | initializedAt(PETRIFIED_BLOCK); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable-buidler/contracts/TimeHelpers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./Uint256Helpers.sol"; 4 | 5 | 6 | contract TimeHelpers { 7 | using Uint256Helpers for uint256; 8 | 9 | /** 10 | * @dev Returns the current block number. 11 | * Using a function rather than `block.number` allows us to easily mock the block number in 12 | * tests. 13 | */ 14 | function getBlockNumber() internal view returns (uint256) { 15 | return block.number; 16 | } 17 | 18 | /** 19 | * @dev Returns the current block number, converted to uint64. 20 | * Using a function rather than `block.number` allows us to easily mock the block number in 21 | * tests. 22 | */ 23 | function getBlockNumber64() internal view returns (uint64) { 24 | return getBlockNumber().toUint64(); 25 | } 26 | 27 | /** 28 | * @dev Returns the current timestamp. 29 | * Using a function rather than `block.timestamp` allows us to easily mock it in 30 | * tests. 31 | */ 32 | function getTimestamp() internal view returns (uint256) { 33 | return block.timestamp; // solium-disable-line security/no-block-members 34 | } 35 | 36 | /** 37 | * @dev Returns the current timestamp, converted to uint64. 38 | * Using a function rather than `block.timestamp` allows us to easily mock it in 39 | * tests. 40 | */ 41 | function getTimestamp64() internal view returns (uint64) { 42 | return getTimestamp().toUint64(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable-buidler/contracts/Uint256Helpers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | 4 | library Uint256Helpers { 5 | uint256 private constant MAX_UINT64 = uint64(-1); 6 | 7 | string private constant ERROR_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG"; 8 | 9 | function toUint64(uint256 a) internal pure returns (uint64) { 10 | require(a <= MAX_UINT64, ERROR_NUMBER_TOO_BIG); 11 | return uint64(a); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable-buidler/contracts/UnstructuredStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | 4 | library UnstructuredStorage { 5 | function getStorageBool(bytes32 position) internal view returns (bool data) { 6 | assembly { data := sload(position) } 7 | } 8 | 9 | function getStorageAddress(bytes32 position) internal view returns (address data) { 10 | assembly { data := sload(position) } 11 | } 12 | 13 | function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { 14 | assembly { data := sload(position) } 15 | } 16 | 17 | function getStorageUint256(bytes32 position) internal view returns (uint256 data) { 18 | assembly { data := sload(position) } 19 | } 20 | 21 | function setStorageBool(bytes32 position, bool data) internal { 22 | assembly { sstore(position, data) } 23 | } 24 | 25 | function setStorageAddress(bytes32 position, address data) internal { 26 | assembly { sstore(position, data) } 27 | } 28 | 29 | function setStorageBytes32(bytes32 position, bytes32 data) internal { 30 | assembly { sstore(position, data) } 31 | } 32 | 33 | function setStorageUint256(bytes32 position, uint256 data) internal { 34 | assembly { sstore(position, data) } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable-buidler/contracts/test/InitializableMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../Initializable.sol"; 4 | import "../Petrifiable.sol"; 5 | 6 | 7 | contract LifecycleMock is Initializable, Petrifiable { 8 | function initializeMock() public { 9 | initialized(); 10 | } 11 | 12 | function petrifyMock() public { 13 | petrify(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable-buidler/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "initializable-buidler", 3 | "version": "1.0.0", 4 | "author": "Aragon Association ", 5 | "license": "GPL-3.0-or-later", 6 | "scripts": { 7 | "test-ganache": "yarn buidler test --network ganache", 8 | "test-ethermint": "yarn buidler test --network ethermint" 9 | }, 10 | "devDependencies": { 11 | "@aragon/contract-helpers-test": "^0.1.0", 12 | "@nomiclabs/buidler": "^1.4.3", 13 | "@nomiclabs/buidler-ganache": "^1.3.3", 14 | "@nomiclabs/buidler-truffle5": "^1.3.4", 15 | "@nomiclabs/buidler-web3": "^1.3.4", 16 | "chai": "^4.2.0", 17 | "web3": "^1.2.11" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable-buidler/test/lifecycle.js: -------------------------------------------------------------------------------- 1 | const { assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 2 | 3 | // Mocks 4 | const LifecycleMock = artifacts.require('LifecycleMock') 5 | 6 | const ERRORS = { 7 | INIT_ALREADY_INITIALIZED: 'INIT_ALREADY_INITIALIZED', 8 | } 9 | 10 | contract('Lifecycle', () => { 11 | let lifecycle 12 | 13 | beforeEach(async () => { 14 | lifecycle = await LifecycleMock.new() 15 | }) 16 | 17 | it('is not initialized', async () => { 18 | assert.isFalse(await lifecycle.hasInitialized(), 'should not be initialized') 19 | }) 20 | 21 | it('is not petrified', async () => { 22 | assert.isFalse(await lifecycle.isPetrified(), 'should not be petrified') 23 | }) 24 | 25 | context('> Initialized', () => { 26 | beforeEach(async () => { 27 | await lifecycle.initializeMock() 28 | }) 29 | 30 | it('is initialized', async () => { 31 | assert.isTrue(await lifecycle.hasInitialized(), 'should be initialized') 32 | }) 33 | 34 | it('is not petrified', async () => { 35 | assert.isFalse(await lifecycle.isPetrified(), 'should not be petrified') 36 | }) 37 | 38 | it('has correct initialization block', async () => { 39 | assert.equal(await lifecycle.getInitializationBlock(), await web3.eth.getBlockNumber(), 'initialization block should be correct') 40 | }) 41 | 42 | it('cannot be re-initialized', async () => { 43 | await assertRevert(lifecycle.initializeMock()/*, ERRORS.INIT_ALREADY_INITIALIZED*/) 44 | }) 45 | 46 | it('cannot be petrified', async () => { 47 | await assertRevert(lifecycle.petrifyMock()/*, ERRORS.INIT_ALREADY_INITIALIZED*/) 48 | }) 49 | }) 50 | 51 | context('> Petrified', () => { 52 | beforeEach(async () => { 53 | await lifecycle.petrifyMock() 54 | }) 55 | 56 | it('is not initialized', async () => { 57 | assert.isFalse(await lifecycle.hasInitialized(), 'should not be initialized') 58 | }) 59 | 60 | it('is petrified', async () => { 61 | assert.isTrue(await lifecycle.isPetrified(), 'should be petrified') 62 | }) 63 | 64 | it('cannot be petrified again', async () => { 65 | await assertRevert(lifecycle.petrifyMock()/*, ERRORS.INIT_ALREADY_INITIALIZED*/) 66 | }) 67 | 68 | it('has initialization block in the future', async () => { 69 | const petrifiedBlock = await lifecycle.getInitializationBlock() 70 | const blockNumber = await web3.eth.getBlockNumber() 71 | assert.isTrue(petrifiedBlock.gt(blockNumber), 'petrified block should be in the future') 72 | }) 73 | }) 74 | }) 75 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable/contracts/Initializable.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: MIT 3 | */ 4 | 5 | pragma solidity ^0.4.24; 6 | 7 | import "./TimeHelpers.sol"; 8 | import "./UnstructuredStorage.sol"; 9 | 10 | 11 | contract Initializable is TimeHelpers { 12 | using UnstructuredStorage for bytes32; 13 | 14 | // keccak256("aragonOS.initializable.initializationBlock") 15 | bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e; 16 | 17 | string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED"; 18 | string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED"; 19 | 20 | modifier onlyInit { 21 | require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED); 22 | _; 23 | } 24 | 25 | modifier isInitialized { 26 | require(hasInitialized(), ERROR_NOT_INITIALIZED); 27 | _; 28 | } 29 | 30 | /** 31 | * @return Block number in which the contract was initialized 32 | */ 33 | function getInitializationBlock() public view returns (uint256) { 34 | return INITIALIZATION_BLOCK_POSITION.getStorageUint256(); 35 | } 36 | 37 | /** 38 | * @return Whether the contract has been initialized by the time of the current block 39 | */ 40 | function hasInitialized() public view returns (bool) { 41 | uint256 initializationBlock = getInitializationBlock(); 42 | return initializationBlock != 0 && getBlockNumber() >= initializationBlock; 43 | } 44 | 45 | /** 46 | * @dev Function to be called by top level contract after initialization has finished. 47 | */ 48 | function initialized() internal onlyInit { 49 | INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber()); 50 | } 51 | 52 | /** 53 | * @dev Function to be called by top level contract after initialization to enable the contract 54 | * at a future block number rather than immediately. 55 | */ 56 | function initializedAt(uint256 _blockNumber) internal onlyInit { 57 | INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable/contracts/Petrifiable.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./Initializable.sol"; 4 | 5 | 6 | contract Petrifiable is Initializable { 7 | // Use block UINT256_MAX (which should be never) as the initializable date 8 | uint256 internal constant PETRIFIED_BLOCK = uint256(-1); 9 | 10 | function isPetrified() public view returns (bool) { 11 | return getInitializationBlock() == PETRIFIED_BLOCK; 12 | } 13 | 14 | /** 15 | * @dev Function to be called by top level contract to prevent being initialized. 16 | * Useful for freezing base contracts when they're used behind proxies. 17 | */ 18 | function petrify() internal onlyInit { 19 | initializedAt(PETRIFIED_BLOCK); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable/contracts/TimeHelpers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | import "./Uint256Helpers.sol"; 4 | 5 | 6 | contract TimeHelpers { 7 | using Uint256Helpers for uint256; 8 | 9 | /** 10 | * @dev Returns the current block number. 11 | * Using a function rather than `block.number` allows us to easily mock the block number in 12 | * tests. 13 | */ 14 | function getBlockNumber() internal view returns (uint256) { 15 | return block.number; 16 | } 17 | 18 | /** 19 | * @dev Returns the current block number, converted to uint64. 20 | * Using a function rather than `block.number` allows us to easily mock the block number in 21 | * tests. 22 | */ 23 | function getBlockNumber64() internal view returns (uint64) { 24 | return getBlockNumber().toUint64(); 25 | } 26 | 27 | /** 28 | * @dev Returns the current timestamp. 29 | * Using a function rather than `block.timestamp` allows us to easily mock it in 30 | * tests. 31 | */ 32 | function getTimestamp() internal view returns (uint256) { 33 | return block.timestamp; // solium-disable-line security/no-block-members 34 | } 35 | 36 | /** 37 | * @dev Returns the current timestamp, converted to uint64. 38 | * Using a function rather than `block.timestamp` allows us to easily mock it in 39 | * tests. 40 | */ 41 | function getTimestamp64() internal view returns (uint64) { 42 | return getTimestamp().toUint64(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable/contracts/Uint256Helpers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | 4 | library Uint256Helpers { 5 | uint256 private constant MAX_UINT64 = uint64(-1); 6 | 7 | string private constant ERROR_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG"; 8 | 9 | function toUint64(uint256 a) internal pure returns (uint64) { 10 | require(a <= MAX_UINT64, ERROR_NUMBER_TOO_BIG); 11 | return uint64(a); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable/contracts/UnstructuredStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | 4 | library UnstructuredStorage { 5 | function getStorageBool(bytes32 position) internal view returns (bool data) { 6 | assembly { data := sload(position) } 7 | } 8 | 9 | function getStorageAddress(bytes32 position) internal view returns (address data) { 10 | assembly { data := sload(position) } 11 | } 12 | 13 | function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { 14 | assembly { data := sload(position) } 15 | } 16 | 17 | function getStorageUint256(bytes32 position) internal view returns (uint256 data) { 18 | assembly { data := sload(position) } 19 | } 20 | 21 | function setStorageBool(bytes32 position, bool data) internal { 22 | assembly { sstore(position, data) } 23 | } 24 | 25 | function setStorageAddress(bytes32 position, address data) internal { 26 | assembly { sstore(position, data) } 27 | } 28 | 29 | function setStorageBytes32(bytes32 position, bytes32 data) internal { 30 | assembly { sstore(position, data) } 31 | } 32 | 33 | function setStorageUint256(bytes32 position, uint256 data) internal { 34 | assembly { sstore(position, data) } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable/contracts/test/InitializableMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../Initializable.sol"; 4 | import "../Petrifiable.sol"; 5 | 6 | 7 | contract LifecycleMock is Initializable, Petrifiable { 8 | function initializeMock() public { 9 | initialized(); 10 | } 11 | 12 | function petrifyMock() public { 13 | petrify(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable/contracts/test/Migrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.22 <0.8.0; 3 | 4 | contract Migrations { 5 | address public owner = msg.sender; 6 | uint public last_completed_migration; 7 | 8 | modifier restricted() { 9 | require( 10 | msg.sender == owner, 11 | "This function is restricted to the contract's owner" 12 | ); 13 | _; 14 | } 15 | 16 | function setCompleted(uint completed) public restricted { 17 | last_completed_migration = completed; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "initializable", 3 | "version": "1.0.0", 4 | "author": "Aragon Association ", 5 | "license": "GPL-3.0-or-later", 6 | "scripts": { 7 | "test-ganache": "yarn truffle test", 8 | "test-ethermint": "yarn truffle test --network ethermint" 9 | }, 10 | "devDependencies": { 11 | "@aragon/contract-helpers-test": "^0.1.0", 12 | "chai": "^4.2.0", 13 | "truffle": "^5.1.42", 14 | "web3": "^1.2.11" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable/test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChainSafe/ethermint/0870a270183d445745b1933310a6796d319e52b0/tests-solidity/suites/initializable/test/.gitkeep -------------------------------------------------------------------------------- /tests-solidity/suites/initializable/test/lifecycle.js: -------------------------------------------------------------------------------- 1 | const { assertRevert } = require('@aragon/contract-helpers-test/src/asserts') 2 | 3 | // Mocks 4 | const LifecycleMock = artifacts.require('LifecycleMock') 5 | 6 | const ERRORS = { 7 | INIT_ALREADY_INITIALIZED: 'INIT_ALREADY_INITIALIZED', 8 | } 9 | 10 | contract('Lifecycle', () => { 11 | let lifecycle 12 | 13 | beforeEach(async () => { 14 | lifecycle = await LifecycleMock.new() 15 | }) 16 | 17 | it('is not initialized', async () => { 18 | assert.isFalse(await lifecycle.hasInitialized(), 'should not be initialized') 19 | }) 20 | 21 | it('is not petrified', async () => { 22 | assert.isFalse(await lifecycle.isPetrified(), 'should not be petrified') 23 | }) 24 | 25 | context('> Initialized', () => { 26 | beforeEach(async () => { 27 | await lifecycle.initializeMock() 28 | }) 29 | 30 | it('is initialized', async () => { 31 | assert.isTrue(await lifecycle.hasInitialized(), 'should be initialized') 32 | }) 33 | 34 | it('is not petrified', async () => { 35 | assert.isFalse(await lifecycle.isPetrified(), 'should not be petrified') 36 | }) 37 | 38 | it('has correct initialization block', async () => { 39 | assert.equal(await lifecycle.getInitializationBlock(), await web3.eth.getBlockNumber(), 'initialization block should be correct') 40 | }) 41 | 42 | it('cannot be re-initialized', async () => { 43 | await assertRevert(lifecycle.initializeMock()/*, ERRORS.INIT_ALREADY_INITIALIZED*/) 44 | }) 45 | 46 | it('cannot be petrified', async () => { 47 | await assertRevert(lifecycle.petrifyMock()/*, ERRORS.INIT_ALREADY_INITIALIZED*/) 48 | }) 49 | }) 50 | 51 | context('> Petrified', () => { 52 | beforeEach(async () => { 53 | await lifecycle.petrifyMock() 54 | }) 55 | 56 | it('is not initialized', async () => { 57 | assert.isFalse(await lifecycle.hasInitialized(), 'should not be initialized') 58 | }) 59 | 60 | it('is petrified', async () => { 61 | assert.isTrue(await lifecycle.isPetrified(), 'should be petrified') 62 | }) 63 | 64 | it('cannot be petrified again', async () => { 65 | await assertRevert(lifecycle.petrifyMock()/*, ERRORS.INIT_ALREADY_INITIALIZED*/) 66 | }) 67 | 68 | it('has initialization block in the future', async () => { 69 | const petrifiedBlock = await lifecycle.getInitializationBlock() 70 | const blockNumber = await web3.eth.getBlockNumber() 71 | assert.isTrue(petrifiedBlock.gt(blockNumber), 'petrified block should be in the future') 72 | }) 73 | }) 74 | }) 75 | -------------------------------------------------------------------------------- /tests-solidity/suites/initializable/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // Development network is just left as truffle's default settings 4 | ethermint: { 5 | host: "127.0.0.1", // Localhost (default: none) 6 | port: 8545, // Standard Ethereum port (default: none) 7 | network_id: "*", // Any network (default: none) 8 | gas: 5000000, // Gas sent with each transaction 9 | gasPrice: 1000000000, // 1 gwei (in wei) 10 | }, 11 | }, 12 | compilers: { 13 | solc: { 14 | version: "0.4.24", 15 | settings: { 16 | optimizer: { 17 | enabled: true, 18 | runs: 10000, 19 | }, 20 | }, 21 | }, 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/contracts/DelegateProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "./IsContract.sol"; 4 | import "./ERCProxy.sol"; 5 | 6 | 7 | contract DelegateProxy is ERCProxy, IsContract { 8 | uint256 internal constant FWD_GAS_LIMIT = 10000; 9 | 10 | /** 11 | * @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!) 12 | * @param _dst Destination address to perform the delegatecall 13 | * @param _calldata Calldata for the delegatecall 14 | */ 15 | function delegatedFwd(address _dst, bytes _calldata) internal { 16 | require(isContract(_dst)); 17 | uint256 fwdGasLimit = FWD_GAS_LIMIT; 18 | 19 | assembly { 20 | let result := delegatecall(sub(gas, fwdGasLimit), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0) 21 | let size := returndatasize 22 | let ptr := mload(0x40) 23 | returndatacopy(ptr, 0, size) 24 | 25 | // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas. 26 | // if the call returned error data, forward it 27 | switch result case 0 { revert(ptr, size) } 28 | default { return(ptr, size) } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/contracts/DepositableDelegateProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "./DelegateProxy.sol"; 4 | import "./DepositableStorage.sol"; 5 | 6 | 7 | contract DepositableDelegateProxy is DepositableStorage, DelegateProxy { 8 | event ProxyDeposit(address sender, uint256 value); 9 | 10 | function () external payable { 11 | uint256 forwardGasThreshold = FWD_GAS_LIMIT; 12 | bytes32 isDepositablePosition = DEPOSITABLE_POSITION; 13 | 14 | // Optimized assembly implementation to prevent EIP-1884 from breaking deposits, reference code in Solidity: 15 | // https://github.com/aragon/aragonOS/blob/v4.2.1/contracts/common/DepositableDelegateProxy.sol#L10-L20 16 | assembly { 17 | // Continue only if the gas left is lower than the threshold for forwarding to the implementation code, 18 | // otherwise continue outside of the assembly block. 19 | if lt(gas, forwardGasThreshold) { 20 | // Only accept the deposit and emit an event if all of the following are true: 21 | // the proxy accepts deposits (isDepositable), msg.data.length == 0, and msg.value > 0 22 | if and(and(sload(isDepositablePosition), iszero(calldatasize)), gt(callvalue, 0)) { 23 | // Equivalent Solidity code for emitting the event: 24 | // emit ProxyDeposit(msg.sender, msg.value); 25 | 26 | let logData := mload(0x40) // free memory pointer 27 | mstore(logData, caller) // add 'msg.sender' to the log data (first event param) 28 | mstore(add(logData, 0x20), callvalue) // add 'msg.value' to the log data (second event param) 29 | 30 | // Emit an event with one topic to identify the event: keccak256('ProxyDeposit(address,uint256)') = 0x15ee...dee1 31 | log1(logData, 0x40, 0x15eeaa57c7bd188c1388020bcadc2c436ec60d647d36ef5b9eb3c742217ddee1) 32 | 33 | stop() // Stop. Exits execution context 34 | } 35 | 36 | // If any of above checks failed, revert the execution (if ETH was sent, it is returned to the sender) 37 | revert(0, 0) 38 | } 39 | } 40 | 41 | address target = implementation(); 42 | delegatedFwd(target, msg.data); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/contracts/DepositableStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "./UnstructuredStorage.sol"; 4 | 5 | 6 | contract DepositableStorage { 7 | using UnstructuredStorage for bytes32; 8 | 9 | // keccak256("aragonOS.depositableStorage.depositable") 10 | bytes32 internal constant DEPOSITABLE_POSITION = 0x665fd576fbbe6f247aff98f5c94a561e3f71ec2d3c988d56f12d342396c50cea; 11 | 12 | function isDepositable() public view returns (bool) { 13 | return DEPOSITABLE_POSITION.getStorageBool(); 14 | } 15 | 16 | function setDepositable(bool _depositable) internal { 17 | DEPOSITABLE_POSITION.setStorageBool(_depositable); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/contracts/ERCProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | 4 | contract ERCProxy { 5 | uint256 internal constant FORWARDING = 1; 6 | uint256 internal constant UPGRADEABLE = 2; 7 | 8 | function proxyType() public pure returns (uint256 proxyTypeId); 9 | function implementation() public view returns (address codeAddr); 10 | } 11 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/contracts/IsContract.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | 4 | contract IsContract { 5 | /* 6 | * NOTE: this should NEVER be used for authentication 7 | * (see pitfalls: https://github.com/fergarrui/ethereum-security/tree/master/contracts/extcodesize). 8 | * 9 | * This is only intended to be used as a sanity check that an address is actually a contract, 10 | * RATHER THAN an address not being a contract. 11 | */ 12 | function isContract(address _target) internal view returns (bool) { 13 | if (_target == address(0)) { 14 | return false; 15 | } 16 | 17 | uint256 size; 18 | assembly { size := extcodesize(_target) } 19 | return size > 0; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/contracts/UnstructuredStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.24; 2 | 3 | 4 | library UnstructuredStorage { 5 | function getStorageBool(bytes32 position) internal view returns (bool data) { 6 | assembly { data := sload(position) } 7 | } 8 | 9 | function getStorageAddress(bytes32 position) internal view returns (address data) { 10 | assembly { data := sload(position) } 11 | } 12 | 13 | function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { 14 | assembly { data := sload(position) } 15 | } 16 | 17 | function getStorageUint256(bytes32 position) internal view returns (uint256 data) { 18 | assembly { data := sload(position) } 19 | } 20 | 21 | function setStorageBool(bytes32 position, bool data) internal { 22 | assembly { sstore(position, data) } 23 | } 24 | 25 | function setStorageAddress(bytes32 position, address data) internal { 26 | assembly { sstore(position, data) } 27 | } 28 | 29 | function setStorageBytes32(bytes32 position, bytes32 data) internal { 30 | assembly { sstore(position, data) } 31 | } 32 | 33 | function setStorageUint256(bytes32 position, uint256 data) internal { 34 | assembly { sstore(position, data) } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/contracts/test/DepositableDelegateProxyMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | import "../DepositableDelegateProxy.sol"; 4 | 5 | 6 | contract DepositableDelegateProxyMock is DepositableDelegateProxy { 7 | address private implementationMock; 8 | 9 | function enableDepositsOnMock() external { 10 | setDepositable(true); 11 | } 12 | 13 | function setImplementationOnMock(address _implementationMock) external { 14 | implementationMock = _implementationMock; 15 | } 16 | 17 | function implementation() public view returns (address) { 18 | return implementationMock; 19 | } 20 | 21 | function proxyType() public pure returns (uint256 proxyTypeId) { 22 | return UPGRADEABLE; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/contracts/test/EthSender.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | 4 | contract EthSender { 5 | function sendEth(address to) external payable { 6 | to.transfer(msg.value); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/contracts/test/Migrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.22 <0.8.0; 3 | 4 | contract Migrations { 5 | address public owner = msg.sender; 6 | uint public last_completed_migration; 7 | 8 | modifier restricted() { 9 | require( 10 | msg.sender == owner, 11 | "This function is restricted to the contract's owner" 12 | ); 13 | _; 14 | } 15 | 16 | function setCompleted(uint completed) public restricted { 17 | last_completed_migration = completed; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/contracts/test/ProxyTarget.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.4.24; 2 | 3 | contract ProxyTargetWithoutFallback { 4 | event Pong(); 5 | 6 | function ping() external { 7 | emit Pong(); 8 | } 9 | } 10 | 11 | contract ProxyTargetWithFallback is ProxyTargetWithoutFallback { 12 | event ReceivedEth(); 13 | 14 | function () external payable { 15 | emit ReceivedEth(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proxy", 3 | "version": "1.0.0", 4 | "author": "Aragon Association ", 5 | "license": "GPL-3.0-or-later", 6 | "scripts": { 7 | "test-ganache": "yarn truffle test", 8 | "test-ethermint": "yarn truffle test --network ethermint" 9 | }, 10 | "devDependencies": { 11 | "@aragon/contract-helpers-test": "^0.1.0", 12 | "chai": "^4.2.0", 13 | "truffle": "^5.1.42", 14 | "web3": "^1.2.11" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/test/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChainSafe/ethermint/0870a270183d445745b1933310a6796d319e52b0/tests-solidity/suites/proxy/test/.gitkeep -------------------------------------------------------------------------------- /tests-solidity/suites/proxy/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // Development network is just left as truffle's default settings 4 | ethermint: { 5 | host: "127.0.0.1", // Localhost (default: none) 6 | port: 8545, // Standard Ethereum port (default: none) 7 | network_id: "*", // Any network (default: none) 8 | gas: 5000000, // Gas sent with each transaction 9 | gasPrice: 1000000000, // 1 gwei (in wei) 10 | }, 11 | }, 12 | compilers: { 13 | solc: { 14 | version: "0.4.24", 15 | settings: { 16 | optimizer: { 17 | enabled: true, 18 | runs: 10000, 19 | }, 20 | }, 21 | }, 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/.github/workflows/ci_contracts.yml: -------------------------------------------------------------------------------- 1 | name: contracts 2 | 3 | on: 4 | push: 5 | branches: master 6 | pull_request: 7 | branches: '*' 8 | 9 | jobs: 10 | CI: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: Install node 15 | uses: actions/setup-node@v1 16 | with: 17 | node-version: 12 18 | - name: Install 19 | run: yarn 20 | - name: Lint 21 | run: yarn lint 22 | - name: Test 23 | run: yarn test 24 | - name: coverage 25 | continue-on-error: true 26 | run: yarn coverage 27 | env: 28 | CI: true 29 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/StakingFactory.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | import "./lib/os/ERC20.sol"; 4 | 5 | import "./Staking.sol"; 6 | import "./proxies/StakingProxy.sol"; 7 | 8 | 9 | contract StakingFactory { 10 | Staking public baseImplementation; 11 | mapping (address => address) internal instances; 12 | 13 | event NewStaking(address indexed instance, address token); 14 | 15 | constructor() public { 16 | baseImplementation = new Staking(); 17 | } 18 | 19 | function existsInstance(ERC20 token) external view returns (bool) { 20 | return _getInstance(token) != address(0); 21 | } 22 | 23 | function getInstance(ERC20 token) external view returns (Staking) { 24 | return Staking(_getInstance(token)); 25 | } 26 | 27 | function getOrCreateInstance(ERC20 token) external returns (Staking) { 28 | address instance = _getInstance(token); 29 | return instance != address(0) ? Staking(instance) : _createInstance(token); 30 | } 31 | 32 | function _getInstance(ERC20 token) internal view returns (address) { 33 | return instances[address(token)]; 34 | } 35 | 36 | function _createInstance(ERC20 token) internal returns (Staking) { 37 | StakingProxy instance = new StakingProxy(baseImplementation, token); 38 | address tokenAddress = address(token); 39 | address instanceAddress = address(instance); 40 | instances[tokenAddress] = instanceAddress; 41 | emit NewStaking(instanceAddress, tokenAddress); 42 | return Staking(instanceAddress); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/Autopetrified.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/Autopetrified.sol 2 | // Adapted to use pragma ^0.5.17 and satisfy our linter rules 3 | 4 | pragma solidity ^0.5.17; 5 | 6 | import "./Petrifiable.sol"; 7 | 8 | 9 | contract Autopetrified is Petrifiable { 10 | constructor() public { 11 | // Immediately petrify base (non-proxy) instances of inherited contracts on deploy. 12 | // This renders them uninitializable (and unusable without a proxy). 13 | petrify(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/DelegateProxy.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/Autopetrified.sol 2 | // Adapted to use pragma ^0.5.17 and satisfy our linter rules 3 | 4 | pragma solidity 0.5.17; 5 | 6 | import "./ERCProxy.sol"; 7 | import "./IsContract.sol"; 8 | 9 | 10 | contract DelegateProxy is ERCProxy, IsContract { 11 | uint256 internal constant FWD_GAS_LIMIT = 10000; 12 | 13 | /** 14 | * @dev Performs a delegatecall and returns whatever the delegatecall returned (entire context execution will return!) 15 | * @param _dst Destination address to perform the delegatecall 16 | * @param _calldata Calldata for the delegatecall 17 | */ 18 | function delegatedFwd(address _dst, bytes memory _calldata) internal { 19 | require(isContract(_dst)); 20 | uint256 fwdGasLimit = FWD_GAS_LIMIT; 21 | 22 | assembly { 23 | let result := delegatecall(sub(gas, fwdGasLimit), _dst, add(_calldata, 0x20), mload(_calldata), 0, 0) 24 | let size := returndatasize 25 | let ptr := mload(0x40) 26 | returndatacopy(ptr, 0, size) 27 | 28 | // revert instead of invalid() bc if the underlying call failed with invalid() it already wasted gas. 29 | // if the call returned error data, forward it 30 | switch result case 0 { revert(ptr, size) } 31 | default { return(ptr, size) } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/ERC20.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/lib/token/ERC20.sol 2 | // Adapted to use pragma ^0.5.8 and satisfy our linter rules 3 | 4 | pragma solidity ^0.5.8; 5 | 6 | 7 | /** 8 | * @title ERC20 interface 9 | * @dev see https://github.com/ethereum/EIPs/issues/20 10 | */ 11 | contract ERC20 { 12 | function totalSupply() public view returns (uint256); 13 | 14 | function balanceOf(address _who) public view returns (uint256); 15 | 16 | function allowance(address _owner, address _spender) public view returns (uint256); 17 | 18 | function transfer(address _to, uint256 _value) public returns (bool); 19 | 20 | function approve(address _spender, uint256 _value) public returns (bool); 21 | 22 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool); 23 | 24 | event Transfer( 25 | address indexed from, 26 | address indexed to, 27 | uint256 value 28 | ); 29 | 30 | event Approval( 31 | address indexed owner, 32 | address indexed spender, 33 | uint256 value 34 | ); 35 | } 36 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/ERCProxy.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/lib/misc/ERCProxy.sol 2 | // Adapted to use pragma ^0.5.17 and satisfy our linter rules 3 | 4 | pragma solidity ^0.5.17; 5 | 6 | 7 | contract ERCProxy { 8 | uint256 internal constant FORWARDING = 1; 9 | uint256 internal constant UPGRADEABLE = 2; 10 | 11 | function proxyType() public pure returns (uint256 proxyTypeId); 12 | function implementation() public view returns (address codeAddr); 13 | } 14 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/Initializable.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/Initializable.sol 2 | // Adapted to use pragma ^0.5.17 and satisfy our linter rules 3 | 4 | pragma solidity ^0.5.17; 5 | 6 | import "./TimeHelpers.sol"; 7 | import "./UnstructuredStorage.sol"; 8 | 9 | 10 | contract Initializable is TimeHelpers { 11 | using UnstructuredStorage for bytes32; 12 | 13 | // keccak256("aragonOS.initializable.initializationBlock") 14 | bytes32 internal constant INITIALIZATION_BLOCK_POSITION = 0xebb05b386a8d34882b8711d156f463690983dc47815980fb82aeeff1aa43579e; 15 | 16 | string private constant ERROR_ALREADY_INITIALIZED = "INIT_ALREADY_INITIALIZED"; 17 | string private constant ERROR_NOT_INITIALIZED = "INIT_NOT_INITIALIZED"; 18 | 19 | modifier onlyInit { 20 | require(getInitializationBlock() == 0, ERROR_ALREADY_INITIALIZED); 21 | _; 22 | } 23 | 24 | modifier isInitialized { 25 | require(hasInitialized(), ERROR_NOT_INITIALIZED); 26 | _; 27 | } 28 | 29 | /** 30 | * @return Block number in which the contract was initialized 31 | */ 32 | function getInitializationBlock() public view returns (uint256) { 33 | return INITIALIZATION_BLOCK_POSITION.getStorageUint256(); 34 | } 35 | 36 | /** 37 | * @return Whether the contract has been initialized by the time of the current block 38 | */ 39 | function hasInitialized() public view returns (bool) { 40 | uint256 initializationBlock = getInitializationBlock(); 41 | return initializationBlock != 0 && getBlockNumber() >= initializationBlock; 42 | } 43 | 44 | /** 45 | * @dev Function to be called by top level contract after initialization has finished. 46 | */ 47 | function initialized() internal onlyInit { 48 | INITIALIZATION_BLOCK_POSITION.setStorageUint256(getBlockNumber()); 49 | } 50 | 51 | /** 52 | * @dev Function to be called by top level contract after initialization to enable the contract 53 | * at a future block number rather than immediately. 54 | */ 55 | function initializedAt(uint256 _blockNumber) internal onlyInit { 56 | INITIALIZATION_BLOCK_POSITION.setStorageUint256(_blockNumber); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/IsContract.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/IsContract.sol 2 | // Adapted to use pragma ^0.5.8 and satisfy our linter rules 3 | 4 | pragma solidity ^0.5.8; 5 | 6 | 7 | contract IsContract { 8 | /* 9 | * NOTE: this should NEVER be used for authentication 10 | * (see pitfalls: https://github.com/fergarrui/ethereum-security/tree/master/contracts/extcodesize). 11 | * 12 | * This is only intended to be used as a sanity check that an address is actually a contract, 13 | * RATHER THAN an address not being a contract. 14 | */ 15 | function isContract(address _target) internal view returns (bool) { 16 | if (_target == address(0)) { 17 | return false; 18 | } 19 | 20 | uint256 size; 21 | assembly { size := extcodesize(_target) } 22 | return size > 0; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/Migrations.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/lib/misc/Migrations.sol 2 | // Adapted to use pragma ^0.5.8 and satisfy our linter rules 3 | 4 | pragma solidity ^0.5.8; 5 | 6 | 7 | contract Migrations { 8 | address public owner; 9 | uint256 public lastCompletedMigration; 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) { 13 | _; 14 | } 15 | } 16 | 17 | constructor() public { 18 | owner = msg.sender; 19 | } 20 | 21 | function setCompleted(uint256 completed) public restricted { 22 | lastCompletedMigration = completed; 23 | } 24 | 25 | function upgrade(address newAddress) public restricted { 26 | Migrations upgraded = Migrations(newAddress); 27 | upgraded.setCompleted(lastCompletedMigration); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/Petrifiable.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/Petrifiable.sol 2 | // Adapted to use pragma ^0.5.17 and satisfy our linter rules 3 | 4 | pragma solidity ^0.5.17; 5 | 6 | import "./Initializable.sol"; 7 | 8 | 9 | contract Petrifiable is Initializable { 10 | // Use block UINT256_MAX (which should be never) as the initializable date 11 | uint256 internal constant PETRIFIED_BLOCK = uint256(-1); 12 | 13 | function isPetrified() public view returns (bool) { 14 | return getInitializationBlock() == PETRIFIED_BLOCK; 15 | } 16 | 17 | /** 18 | * @dev Function to be called by top level contract to prevent being initialized. 19 | * Useful for freezing base contracts when they're used behind proxies. 20 | */ 21 | function petrify() internal onlyInit { 22 | initializedAt(PETRIFIED_BLOCK); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/SafeMath.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/lib/math/SafeMath.sol 2 | // Adapted to use pragma ^0.5.8 and satisfy our linter rules 3 | 4 | pragma solidity >=0.4.24 <0.6.0; 5 | 6 | 7 | /** 8 | * @title SafeMath 9 | * @dev Math operations with safety checks that revert on error 10 | */ 11 | library SafeMath { 12 | string private constant ERROR_ADD_OVERFLOW = "MATH_ADD_OVERFLOW"; 13 | string private constant ERROR_SUB_UNDERFLOW = "MATH_SUB_UNDERFLOW"; 14 | string private constant ERROR_MUL_OVERFLOW = "MATH_MUL_OVERFLOW"; 15 | string private constant ERROR_DIV_ZERO = "MATH_DIV_ZERO"; 16 | 17 | /** 18 | * @dev Multiplies two numbers, reverts on overflow. 19 | */ 20 | function mul(uint256 _a, uint256 _b) internal pure returns (uint256) { 21 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 22 | // benefit is lost if 'b' is also tested. 23 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 24 | if (_a == 0) { 25 | return 0; 26 | } 27 | 28 | uint256 c = _a * _b; 29 | require(c / _a == _b, ERROR_MUL_OVERFLOW); 30 | 31 | return c; 32 | } 33 | 34 | /** 35 | * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. 36 | */ 37 | function div(uint256 _a, uint256 _b) internal pure returns (uint256) { 38 | require(_b > 0, ERROR_DIV_ZERO); // Solidity only automatically asserts when dividing by 0 39 | uint256 c = _a / _b; 40 | // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold 41 | 42 | return c; 43 | } 44 | 45 | /** 46 | * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). 47 | */ 48 | function sub(uint256 _a, uint256 _b) internal pure returns (uint256) { 49 | require(_b <= _a, ERROR_SUB_UNDERFLOW); 50 | uint256 c = _a - _b; 51 | 52 | return c; 53 | } 54 | 55 | /** 56 | * @dev Adds two numbers, reverts on overflow. 57 | */ 58 | function add(uint256 _a, uint256 _b) internal pure returns (uint256) { 59 | uint256 c = _a + _b; 60 | require(c >= _a, ERROR_ADD_OVERFLOW); 61 | 62 | return c; 63 | } 64 | 65 | /** 66 | * @dev Divides two numbers and returns the remainder (unsigned integer modulo), 67 | * reverts when dividing by zero. 68 | */ 69 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 70 | require(b != 0, ERROR_DIV_ZERO); 71 | return a % b; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/SafeMath64.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/lib/math/SafeMath64.sol 2 | // Adapted to use pragma ^0.5.8 and satisfy our linter rules 3 | 4 | pragma solidity ^0.5.8; 5 | 6 | 7 | /** 8 | * @title SafeMath64 9 | * @dev Math operations for uint64 with safety checks that revert on error 10 | */ 11 | library SafeMath64 { 12 | string private constant ERROR_ADD_OVERFLOW = "MATH64_ADD_OVERFLOW"; 13 | string private constant ERROR_SUB_UNDERFLOW = "MATH64_SUB_UNDERFLOW"; 14 | string private constant ERROR_MUL_OVERFLOW = "MATH64_MUL_OVERFLOW"; 15 | string private constant ERROR_DIV_ZERO = "MATH64_DIV_ZERO"; 16 | 17 | /** 18 | * @dev Multiplies two numbers, reverts on overflow. 19 | */ 20 | function mul(uint64 _a, uint64 _b) internal pure returns (uint64) { 21 | uint256 c = uint256(_a) * uint256(_b); 22 | require(c < 0x010000000000000000, ERROR_MUL_OVERFLOW); // 2**64 (less gas this way) 23 | 24 | return uint64(c); 25 | } 26 | 27 | /** 28 | * @dev Integer division of two numbers truncating the quotient, reverts on division by zero. 29 | */ 30 | function div(uint64 _a, uint64 _b) internal pure returns (uint64) { 31 | require(_b > 0, ERROR_DIV_ZERO); // Solidity only automatically asserts when dividing by 0 32 | uint64 c = _a / _b; 33 | // assert(_a == _b * c + _a % _b); // There is no case in which this doesn't hold 34 | 35 | return c; 36 | } 37 | 38 | /** 39 | * @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend). 40 | */ 41 | function sub(uint64 _a, uint64 _b) internal pure returns (uint64) { 42 | require(_b <= _a, ERROR_SUB_UNDERFLOW); 43 | uint64 c = _a - _b; 44 | 45 | return c; 46 | } 47 | 48 | /** 49 | * @dev Adds two numbers, reverts on overflow. 50 | */ 51 | function add(uint64 _a, uint64 _b) internal pure returns (uint64) { 52 | uint64 c = _a + _b; 53 | require(c >= _a, ERROR_ADD_OVERFLOW); 54 | 55 | return c; 56 | } 57 | 58 | /** 59 | * @dev Divides two numbers and returns the remainder (unsigned integer modulo), 60 | * reverts when dividing by zero. 61 | */ 62 | function mod(uint64 a, uint64 b) internal pure returns (uint64) { 63 | require(b != 0, ERROR_DIV_ZERO); 64 | return a % b; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/ScriptHelpers.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/evmscript/ScriptHelpers.sol 2 | // Adapted to use pragma ^0.5.17 and satisfy our linter rules 3 | 4 | pragma solidity ^0.5.17; 5 | 6 | 7 | library ScriptHelpers { 8 | function getSpecId(bytes memory _script) internal pure returns (uint32) { 9 | return uint32At(_script, 0); 10 | } 11 | 12 | function uint256At(bytes memory _data, uint256 _location) internal pure returns (uint256 result) { 13 | assembly { 14 | result := mload(add(_data, add(0x20, _location))) 15 | } 16 | } 17 | 18 | function addressAt(bytes memory _data, uint256 _location) internal pure returns (address result) { 19 | uint256 word = uint256At(_data, _location); 20 | 21 | assembly { 22 | result := div(and(word, 0xffffffffffffffffffffffffffffffffffffffff000000000000000000000000), 23 | 0x1000000000000000000000000) 24 | } 25 | } 26 | 27 | function uint32At(bytes memory _data, uint256 _location) internal pure returns (uint32 result) { 28 | uint256 word = uint256At(_data, _location); 29 | 30 | assembly { 31 | result := div(and(word, 0xffffffff00000000000000000000000000000000000000000000000000000000), 32 | 0x100000000000000000000000000000000000000000000000000000000) 33 | } 34 | } 35 | 36 | function locationOf(bytes memory _data, uint256 _location) internal pure returns (uint256 result) { 37 | assembly { 38 | result := add(_data, add(0x20, _location)) 39 | } 40 | } 41 | 42 | function toBytes(bytes4 _sig) internal pure returns (bytes memory) { 43 | bytes memory payload = new bytes(4); 44 | assembly { mstore(add(payload, 0x20), _sig) } 45 | return payload; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/TimeHelpers.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/TimeHelpers.sol 2 | // Adapted to use pragma ^0.5.8 and satisfy our linter rules 3 | 4 | pragma solidity ^0.5.8; 5 | 6 | import "./Uint256Helpers.sol"; 7 | 8 | 9 | contract TimeHelpers { 10 | using Uint256Helpers for uint256; 11 | 12 | /** 13 | * @dev Returns the current block number. 14 | * Using a function rather than `block.number` allows us to easily mock the block number in 15 | * tests. 16 | */ 17 | function getBlockNumber() internal view returns (uint256) { 18 | return block.number; 19 | } 20 | 21 | /** 22 | * @dev Returns the current block number, converted to uint64. 23 | * Using a function rather than `block.number` allows us to easily mock the block number in 24 | * tests. 25 | */ 26 | function getBlockNumber64() internal view returns (uint64) { 27 | return getBlockNumber().toUint64(); 28 | } 29 | 30 | /** 31 | * @dev Returns the current timestamp. 32 | * Using a function rather than `block.timestamp` allows us to easily mock it in 33 | * tests. 34 | */ 35 | function getTimestamp() internal view returns (uint256) { 36 | return block.timestamp; // solium-disable-line security/no-block-members 37 | } 38 | 39 | /** 40 | * @dev Returns the current timestamp, converted to uint64. 41 | * Using a function rather than `block.timestamp` allows us to easily mock it in 42 | * tests. 43 | */ 44 | function getTimestamp64() internal view returns (uint64) { 45 | return getTimestamp().toUint64(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/Uint256Helpers.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/Uint256Helpers.sol 2 | // Adapted to use pragma ^0.5.8 and satisfy our linter rules 3 | 4 | pragma solidity ^0.5.8; 5 | 6 | 7 | library Uint256Helpers { 8 | uint256 private constant MAX_UINT8 = uint8(-1); 9 | uint256 private constant MAX_UINT64 = uint64(-1); 10 | 11 | string private constant ERROR_UINT8_NUMBER_TOO_BIG = "UINT8_NUMBER_TOO_BIG"; 12 | string private constant ERROR_UINT64_NUMBER_TOO_BIG = "UINT64_NUMBER_TOO_BIG"; 13 | 14 | function toUint8(uint256 a) internal pure returns (uint8) { 15 | require(a <= MAX_UINT8, ERROR_UINT8_NUMBER_TOO_BIG); 16 | return uint8(a); 17 | } 18 | 19 | function toUint64(uint256 a) internal pure returns (uint64) { 20 | require(a <= MAX_UINT64, ERROR_UINT64_NUMBER_TOO_BIG); 21 | return uint64(a); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/lib/os/UnstructuredStorage.sol: -------------------------------------------------------------------------------- 1 | // Brought from https://github.com/aragon/aragonOS/blob/v4.3.0/contracts/common/UnstructuredStorage.sol 2 | // Adapted to use pragma ^0.5.17 and satisfy our linter rules 3 | 4 | pragma solidity ^0.5.17; 5 | 6 | 7 | library UnstructuredStorage { 8 | function getStorageBool(bytes32 position) internal view returns (bool data) { 9 | assembly { data := sload(position) } 10 | } 11 | 12 | function getStorageAddress(bytes32 position) internal view returns (address data) { 13 | assembly { data := sload(position) } 14 | } 15 | 16 | function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { 17 | assembly { data := sload(position) } 18 | } 19 | 20 | function getStorageUint256(bytes32 position) internal view returns (uint256 data) { 21 | assembly { data := sload(position) } 22 | } 23 | 24 | function setStorageBool(bytes32 position, bool data) internal { 25 | assembly { sstore(position, data) } 26 | } 27 | 28 | function setStorageAddress(bytes32 position, address data) internal { 29 | assembly { sstore(position, data) } 30 | } 31 | 32 | function setStorageBytes32(bytes32 position, bytes32 data) internal { 33 | assembly { sstore(position, data) } 34 | } 35 | 36 | function setStorageUint256(bytes32 position, uint256 data) internal { 37 | assembly { sstore(position, data) } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/locking/ILockManager.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | 4 | interface ILockManager { 5 | /** 6 | * @notice Check if `_user`'s by `_lockManager` can be unlocked 7 | * @param _user Owner of lock 8 | * @param _amount Amount of locked tokens to unlock 9 | * @return Whether given lock of given owner can be unlocked by given sender 10 | */ 11 | function canUnlock(address _user, uint256 _amount) external view returns (bool); 12 | } 13 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/locking/IStakingLocking.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | 4 | interface IStakingLocking { 5 | event NewLockManager(address indexed account, address indexed lockManager, bytes data); 6 | event Unlocked(address indexed account, address indexed lockManager, uint256 amount); 7 | event LockAmountChanged(address indexed account, address indexed lockManager, uint256 amount, bool increase); 8 | event LockAllowanceChanged(address indexed account, address indexed lockManager, uint256 allowance, bool increase); 9 | event LockManagerRemoved(address indexed account, address lockManager); 10 | event LockManagerTransferred(address indexed account, address indexed oldLockManager, address newLockManager); 11 | event StakeTransferred(address indexed from, address to, uint256 amount); 12 | 13 | function allowManager(address _lockManager, uint256 _allowance, bytes calldata _data) external; 14 | function allowManagerAndLock(uint256 _amount, address _lockManager, uint256 _allowance, bytes calldata _data) external; 15 | function unlockAndRemoveManager(address _account, address _lockManager) external; 16 | function increaseLockAllowance(address _lockManager, uint256 _allowance) external; 17 | function decreaseLockAllowance(address _account, address _lockManager, uint256 _allowance) external; 18 | function lock(address _account, address _lockManager, uint256 _amount) external; 19 | function unlock(address _account, address _lockManager, uint256 _amount) external; 20 | function setLockManager(address _account, address _newLockManager) external; 21 | function transfer(address _to, uint256 _amount) external; 22 | function transferAndUnstake(address _to, uint256 _amount) external; 23 | function slash(address _account, address _to, uint256 _amount) external; 24 | function slashAndUnstake(address _account, address _to, uint256 _amount) external; 25 | 26 | function getLock(address _account, address _lockManager) external view returns (uint256 _amount, uint256 _allowance); 27 | function unlockedBalanceOf(address _account) external view returns (uint256); 28 | function lockedBalanceOf(address _user) external view returns (uint256); 29 | function getBalancesOf(address _user) external view returns (uint256 staked, uint256 locked); 30 | function canUnlock(address _sender, address _account, address _lockManager, uint256 _amount) external view returns (bool); 31 | } 32 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/proxies/StakingProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | import "../lib/os/ERC20.sol"; 4 | 5 | import "../Staking.sol"; 6 | import "./ThinProxy.sol"; 7 | 8 | 9 | contract StakingProxy is ThinProxy { 10 | // keccak256("aragon.network.staking") 11 | bytes32 internal constant IMPLEMENTATION_SLOT = 0xbd536e2e005accda865e2f0d1827f83ec8824f3ea04ecd6131b7c10058635814; 12 | 13 | constructor(Staking _implementation, ERC20 _token) ThinProxy(address(_implementation)) public { 14 | bytes4 selector = _implementation.initialize.selector; 15 | bytes memory initializeData = abi.encodeWithSelector(selector, _token); 16 | (bool success,) = address(_implementation).delegatecall(initializeData); 17 | 18 | if (!success) { 19 | assembly { 20 | let output := mload(0x40) 21 | mstore(0x40, add(output, returndatasize)) 22 | returndatacopy(output, 0, returndatasize) 23 | revert(output, returndatasize) 24 | } 25 | } 26 | } 27 | 28 | function _implementationSlot() internal pure returns (bytes32) { 29 | return IMPLEMENTATION_SLOT; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/proxies/ThinProxy.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | import "../lib/os/DelegateProxy.sol"; 4 | import "../lib/os/UnstructuredStorage.sol"; 5 | 6 | 7 | contract ThinProxy is DelegateProxy { 8 | using UnstructuredStorage for bytes32; 9 | 10 | constructor(address _implementation) public { 11 | _implementationSlot().setStorageAddress(_implementation); 12 | } 13 | 14 | function () external { 15 | delegatedFwd(implementation(), msg.data); 16 | } 17 | 18 | function proxyType() public pure returns (uint256) { 19 | return FORWARDING; 20 | } 21 | 22 | function implementation() public view returns (address) { 23 | return _implementationSlot().getStorageAddress(); 24 | } 25 | 26 | function _implementationSlot() internal pure returns (bytes32); 27 | } 28 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/standards/ERC900.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | 4 | // Interface for ERC900: https://eips.ethereum.org/EIPS/eip-900 5 | interface ERC900 { 6 | event Staked(address indexed user, uint256 amount, uint256 total, bytes data); 7 | event Unstaked(address indexed user, uint256 amount, uint256 total, bytes data); 8 | 9 | /** 10 | * @dev Stake a certain amount of tokens 11 | * @param _amount Amount of tokens to be staked 12 | * @param _data Optional data that can be used to add signalling information in more complex staking applications 13 | */ 14 | function stake(uint256 _amount, bytes calldata _data) external; 15 | 16 | /** 17 | * @dev Stake a certain amount of tokens in favor of someone 18 | * @param _user Address to stake an amount of tokens to 19 | * @param _amount Amount of tokens to be staked 20 | * @param _data Optional data that can be used to add signalling information in more complex staking applications 21 | */ 22 | function stakeFor(address _user, uint256 _amount, bytes calldata _data) external; 23 | 24 | /** 25 | * @dev Unstake a certain amount of tokens 26 | * @param _amount Amount of tokens to be unstaked 27 | * @param _data Optional data that can be used to add signalling information in more complex staking applications 28 | */ 29 | function unstake(uint256 _amount, bytes calldata _data) external; 30 | 31 | /** 32 | * @dev Tell the total amount of tokens staked for an address 33 | * @param _addr Address querying the total amount of tokens staked for 34 | * @return Total amount of tokens staked for an address 35 | */ 36 | function totalStakedFor(address _addr) external view returns (uint256); 37 | 38 | /** 39 | * @dev Tell the total amount of tokens staked 40 | * @return Total amount of tokens staked 41 | */ 42 | function totalStaked() external view returns (uint256); 43 | 44 | /** 45 | * @dev Tell the address of the token used for staking 46 | * @return Address of the token used for staking 47 | */ 48 | function token() external view returns (address); 49 | 50 | /* 51 | * @dev Tell if the current registry supports historic information or not 52 | * @return True if the optional history functions are implemented, false otherwise 53 | */ 54 | function supportsHistory() external pure returns (bool); 55 | } 56 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/test/TestImports.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.17; 2 | 3 | import "./lib/MiniMeToken.sol"; 4 | import "../lib/os/Migrations.sol"; 5 | 6 | // You might think this file is a bit odd, but let me explain. 7 | // We only use some contracts in our tests, which means Truffle 8 | // will not compile it for us, because it is from an external 9 | // dependency. 10 | // 11 | // We are now left with three options: 12 | // - Copy/paste these contracts 13 | // - Run the tests with `truffle compile --all` on 14 | // - Or trick Truffle by claiming we use it in a Solidity test 15 | // 16 | // You know which one I went for. 17 | 18 | 19 | contract TestImports { 20 | constructor() public { 21 | // solium-disable-previous-line no-empty-blocks 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/test/lib/ITokenController.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | /// @dev The token controller contract must implement these functions 4 | 5 | 6 | interface ITokenController { 7 | /// @notice Called when `_owner` sends ether to the MiniMe Token contract 8 | /// @param _owner The address that sent the ether to create tokens 9 | /// @return True if the ether is accepted, false if it throws 10 | function proxyPayment(address _owner) external payable returns(bool); 11 | 12 | /// @notice Notifies the controller about a token transfer allowing the 13 | /// controller to react if desired 14 | /// @param _from The origin of the transfer 15 | /// @param _to The destination of the transfer 16 | /// @param _amount The amount of the transfer 17 | /// @return False if the controller does not authorize the transfer 18 | function onTransfer(address _from, address _to, uint _amount) external returns(bool); 19 | 20 | /// @notice Notifies the controller about an approval allowing the 21 | /// controller to react if desired 22 | /// @param _owner The address that calls `approve()` 23 | /// @param _spender The spender in the `approve()` call 24 | /// @param _amount The amount in the `approve()` call 25 | /// @return False if the controller does not authorize the approval 26 | function onApprove(address _owner, address _spender, uint _amount) external returns(bool); 27 | } 28 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/test/mocks/CheckpointingMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.17; 2 | 3 | import "../../lib/Checkpointing.sol"; 4 | 5 | 6 | contract CheckpointingMock { 7 | using Checkpointing for Checkpointing.History; 8 | 9 | Checkpointing.History history; 10 | 11 | function add(uint64 value, uint256 time) public { 12 | history.add(value, time); 13 | } 14 | 15 | function getLast() public view returns (uint256) { 16 | return history.getLast(); 17 | } 18 | 19 | function get(uint64 time) public view returns (uint256) { 20 | return history.get(time); 21 | } 22 | 23 | function getHistorySize() public view returns (uint256) { 24 | return history.history.length; 25 | } 26 | 27 | function lastUpdate() public view returns (uint256) { 28 | return history.lastUpdate(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/test/mocks/ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | 4 | /** 5 | * @title ERC20 interface 6 | * @dev see https://github.com/ethereum/EIPs/issues/20 7 | */ 8 | contract ERC20 { 9 | function totalSupply() public view returns (uint256); 10 | 11 | function balanceOf(address _who) public view returns (uint256); 12 | 13 | function allowance(address _owner, address _spender) 14 | public view returns (uint256); 15 | 16 | function transfer(address _to, uint256 _value) public returns (bool); 17 | 18 | function approve(address _spender, uint256 _value) 19 | public returns (bool); 20 | 21 | function transferFrom(address _from, address _to, uint256 _value) 22 | public returns (bool); 23 | 24 | event Transfer(address indexed from, address indexed to, uint256 value); 25 | event Approval(address indexed owner, address indexed spender, uint256 value); 26 | } 27 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/test/mocks/LockManagerMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.17; 2 | 3 | import "../../locking/ILockManager.sol"; 4 | import "../../Staking.sol"; 5 | 6 | 7 | contract LockManagerMock is ILockManager { 8 | bool result; 9 | 10 | function slash(Staking _staking, address _from, address _to, uint256 _amount) external { 11 | _staking.slash(_from, _to, _amount); 12 | } 13 | 14 | function slashAndUnstake(Staking _staking, address _from, address _to, uint256 _amount) external { 15 | _staking.slashAndUnstake(_from, _to, _amount); 16 | } 17 | 18 | function unlock(Staking _staking, address _account, uint256 _amount) external { 19 | _staking.unlock(_account, address(this), _amount); 20 | } 21 | 22 | function unlockAndRemoveManager(Staking _staking, address _account) external { 23 | _staking.unlockAndRemoveManager(_account, address(this)); 24 | } 25 | 26 | function setLockManager(Staking _staking, address _account, ILockManager _newManager) external { 27 | _staking.setLockManager(_account, address(_newManager)); 28 | } 29 | 30 | function canUnlock(address, uint256) external view returns (bool) { 31 | return result; 32 | } 33 | 34 | function setResult(bool _result) public { 35 | result = _result; 36 | } 37 | 38 | function unlockAndRemoveManager(Staking _staking, address _account, address _manager) public { 39 | _staking.unlockAndRemoveManager(_account, _manager); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/test/mocks/StakingMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.17; 2 | 3 | import "../../Staking.sol"; 4 | 5 | import "../../lib/os/SafeMath.sol"; 6 | import "./TimeHelpersMock.sol"; 7 | 8 | 9 | contract StakingMock is Staking, TimeHelpersMock { 10 | using SafeMath for uint256; 11 | 12 | event LogGas(uint256 gas); 13 | 14 | string private constant ERROR_TOKEN_NOT_CONTRACT = "STAKING_TOKEN_NOT_CONTRACT"; 15 | 16 | uint64 private constant MAX_UINT64 = uint64(-1); 17 | 18 | modifier measureGas { 19 | uint256 initialGas = gasleft(); 20 | _; 21 | emit LogGas(initialGas - gasleft()); 22 | } 23 | 24 | constructor(ERC20 _stakingToken) public { 25 | require(isContract(address(_stakingToken)), ERROR_TOKEN_NOT_CONTRACT); 26 | initialized(); 27 | stakingToken = _stakingToken; 28 | } 29 | 30 | function unlockedBalanceOfGas() external returns (uint256) { 31 | uint256 initialGas = gasleft(); 32 | _unlockedBalanceOf(msg.sender); 33 | uint256 gasConsumed = initialGas - gasleft(); 34 | emit LogGas(gasConsumed); 35 | return gasConsumed; 36 | } 37 | 38 | function transferGas(address _to, address, uint256 _amount) external measureGas { 39 | // have enough unlocked funds 40 | require(_amount <= _unlockedBalanceOf(msg.sender)); 41 | 42 | _transfer(msg.sender, _to, _amount); 43 | } 44 | 45 | function setBlockNumber(uint64 _mockedBlockNumber) public { 46 | mockedBlockNumber = _mockedBlockNumber; 47 | } 48 | 49 | // Override petrify functions to allow mocking the initialization process 50 | function petrify() internal onlyInit { 51 | // solium-disable-previous-line no-empty-blocks 52 | // initializedAt(PETRIFIED_BLOCK); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/test/mocks/TimeHelpersMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.17; 2 | 3 | import "../../lib/os/TimeHelpers.sol"; 4 | import "../..//lib/os/SafeMath.sol"; 5 | import "../..//lib/os/SafeMath64.sol"; 6 | 7 | 8 | contract TimeHelpersMock is TimeHelpers { 9 | using SafeMath for uint256; 10 | using SafeMath64 for uint64; 11 | 12 | uint256 public mockedTimestamp; 13 | uint256 public mockedBlockNumber; 14 | 15 | /** 16 | * @dev Sets a mocked timestamp value, used only for testing purposes 17 | */ 18 | function mockSetTimestamp(uint256 _timestamp) external { 19 | mockedTimestamp = _timestamp; 20 | } 21 | 22 | /** 23 | * @dev Increases the mocked timestamp value, used only for testing purposes 24 | */ 25 | function mockIncreaseTime(uint256 _seconds) external { 26 | if (mockedTimestamp != 0) mockedTimestamp = mockedTimestamp.add(_seconds); 27 | else mockedTimestamp = block.timestamp.add(_seconds); 28 | } 29 | 30 | /** 31 | * @dev Decreases the mocked timestamp value, used only for testing purposes 32 | */ 33 | function mockDecreaseTime(uint256 _seconds) external { 34 | if (mockedTimestamp != 0) mockedTimestamp = mockedTimestamp.sub(_seconds); 35 | else mockedTimestamp = block.timestamp.sub(_seconds); 36 | } 37 | 38 | /** 39 | * @dev Advances the mocked block number value, used only for testing purposes 40 | */ 41 | function mockAdvanceBlocks(uint256 _number) external { 42 | if (mockedBlockNumber != 0) mockedBlockNumber = mockedBlockNumber.add(_number); 43 | else mockedBlockNumber = block.number.add(_number); 44 | } 45 | 46 | /** 47 | * @dev Returns the mocked timestamp value 48 | */ 49 | function getTimestampPublic() external view returns (uint64) { 50 | return getTimestamp64(); 51 | } 52 | 53 | /** 54 | * @dev Returns the mocked block number value 55 | */ 56 | function getBlockNumberPublic() external view returns (uint256) { 57 | return getBlockNumber(); 58 | } 59 | 60 | /** 61 | * @dev Returns the mocked timestamp if it was set, or current `block.timestamp` 62 | */ 63 | function getTimestamp() internal view returns (uint256) { 64 | if (mockedTimestamp != 0) return mockedTimestamp; 65 | return super.getTimestamp(); 66 | } 67 | 68 | /** 69 | * @dev Returns the mocked block number if it was set, or current `block.number` 70 | */ 71 | function getBlockNumber() internal view returns (uint256) { 72 | if (mockedBlockNumber != 0) return mockedBlockNumber; 73 | return super.getBlockNumber(); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/contracts/test/mocks/TimeLockManagerMock.sol: -------------------------------------------------------------------------------- 1 | pragma solidity 0.5.17; 2 | 3 | import "../../locking/TimeLockManager.sol"; 4 | import "../../Staking.sol"; 5 | 6 | 7 | contract TimeLockManagerMock is TimeLockManager { 8 | uint64 public constant MAX_UINT64 = uint64(-1); 9 | 10 | uint256 _mockTime = now; 11 | uint256 _mockBlockNumber = block.number; 12 | 13 | function getTimestampExt() external view returns (uint256) { 14 | return getTimestamp(); 15 | } 16 | 17 | function getBlockNumberExt() external view returns (uint256) { 18 | return getBlockNumber(); 19 | } 20 | 21 | function setTimestamp(uint256 i) public { 22 | _mockTime = i; 23 | } 24 | 25 | function setBlockNumber(uint256 i) public { 26 | _mockBlockNumber = i; 27 | } 28 | 29 | function getTimestamp() internal view returns (uint256) { 30 | return _mockTime; 31 | } 32 | 33 | function getBlockNumber() internal view returns (uint256) { 34 | return _mockBlockNumber; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "staking", 3 | "version": "1.0.0", 4 | "author": "Aragon Association ", 5 | "license": "GPL-3.0-or-later", 6 | "scripts": { 7 | "test-ganache": "yarn truffle test", 8 | "test-ethermint": "yarn truffle test --network ethermint" 9 | }, 10 | "devDependencies": { 11 | "@aragon/contract-helpers-test": "^0.0.3", 12 | "chai": "^4.2.0", 13 | "ganache-cli": "^6.1.0", 14 | "truffle": "^5.1.42", 15 | "web3-eth-abi": "^1.2.11", 16 | "web3-utils": "^1.2.11" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/test/helpers/constants.js: -------------------------------------------------------------------------------- 1 | const { bn, bigExp } = require('@aragon/contract-helpers-test/numbers') 2 | const DEFAULT_STAKE_AMOUNT = bigExp(120, 18) 3 | 4 | module.exports = { 5 | DEFAULT_STAKE_AMOUNT, 6 | DEFAULT_LOCK_AMOUNT: DEFAULT_STAKE_AMOUNT.div(bn(3)), 7 | EMPTY_DATA: '0x', 8 | ZERO_ADDRESS: '0x' + '0'.repeat(40), 9 | ACTIVATED_LOCK: '0x01' 10 | } 11 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/test/helpers/deploy.js: -------------------------------------------------------------------------------- 1 | const { bn } = require('@aragon/contract-helpers-test/numbers') 2 | 3 | const { DEFAULT_STAKE_AMOUNT } = require('./constants') 4 | 5 | module.exports = (artifacts) => { 6 | const StakingFactory = artifacts.require('StakingFactory') 7 | const Staking = artifacts.require('Staking') 8 | const StandardTokenMock = artifacts.require('StandardTokenMock') 9 | const LockManagerMock = artifacts.require('LockManagerMock') 10 | 11 | const getEvent = (receipt, event, arg) => { return receipt.logs.filter(l => l.event === event)[0].args[arg] } 12 | 13 | const deploy = async (owner, initialAmount = DEFAULT_STAKE_AMOUNT.mul(bn(1000))) => { 14 | const token = await StandardTokenMock.new(owner, initialAmount) 15 | 16 | const staking = await deployStaking(token) 17 | 18 | const lockManager = await LockManagerMock.new() 19 | 20 | return { token, staking, lockManager } 21 | } 22 | 23 | const deployStaking = async (token) => { 24 | const factory = await StakingFactory.new() 25 | const receipt = await factory.getOrCreateInstance(token.address) 26 | const stakingAddress = getEvent(receipt, 'NewStaking', 'instance') 27 | const staking = await Staking.at(stakingAddress) 28 | 29 | return staking 30 | } 31 | 32 | return { 33 | deploy 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/test/helpers/errors.js: -------------------------------------------------------------------------------- 1 | const CHECKPOINT_ERRORS = { 2 | ERROR_VALUE_TOO_BIG: 'CHECKPOINT_VALUE_TOO_BIG', 3 | ERROR_CANNOT_ADD_PAST_VALUE: 'CHECKPOINT_CANNOT_ADD_PAST_VALUE', 4 | } 5 | 6 | const STAKING_ERRORS = { 7 | ERROR_TOKEN_NOT_CONTRACT: 'STAKING_TOKEN_NOT_CONTRACT', 8 | ERROR_AMOUNT_ZERO: 'STAKING_AMOUNT_ZERO', 9 | ERROR_TOKEN_TRANSFER: 'STAKING_TOKEN_TRANSFER_FAIL', 10 | ERROR_TOKEN_DEPOSIT: 'STAKING_TOKEN_DEPOSIT_FAIL', 11 | ERROR_TOKEN_NOT_SENDER: 'STAKING_TOKEN_NOT_SENDER', 12 | ERROR_WRONG_TOKEN: 'STAKING_WRONG_TOKEN', 13 | ERROR_NOT_ENOUGH_BALANCE: 'STAKING_NOT_ENOUGH_BALANCE', 14 | ERROR_NOT_ENOUGH_ALLOWANCE: 'STAKING_NOT_ENOUGH_ALLOWANCE', 15 | ERROR_SENDER_NOT_ALLOWED: 'STAKING_SENDER_NOT_ALLOWED', 16 | ERROR_ALLOWANCE_ZERO: 'STAKING_ALLOWANCE_ZERO', 17 | ERROR_LOCK_ALREADY_EXISTS: 'STAKING_LOCK_ALREADY_EXISTS', 18 | ERROR_LOCK_DOES_NOT_EXIST: 'STAKING_LOCK_DOES_NOT_EXIST', 19 | ERROR_NOT_ENOUGH_LOCK: 'STAKING_NOT_ENOUGH_LOCK', 20 | ERROR_CANNOT_UNLOCK: 'STAKING_CANNOT_UNLOCK', 21 | ERROR_CANNOT_CHANGE_ALLOWANCE: 'STAKING_CANNOT_CHANGE_ALLOWANCE', 22 | ERROR_LOCKMANAGER_CALL_FAIL: 'STAKING_LOCKMANAGER_CALL_FAIL', 23 | ERROR_BLOCKNUMBER_TOO_BIG: 'STAKING_BLOCKNUMBER_TOO_BIG', 24 | } 25 | 26 | const TIME_LOCK_MANAGER_ERRORS = { 27 | ERROR_ALREADY_LOCKED: 'TLM_ALREADY_LOCKED', 28 | ERROR_WRONG_INTERVAL: 'TLM_WRONG_INTERVAL', 29 | } 30 | 31 | module.exports = { 32 | CHECKPOINT_ERRORS, 33 | STAKING_ERRORS, 34 | TIME_LOCK_MANAGER_ERRORS, 35 | } 36 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/test/staking_proxy.js: -------------------------------------------------------------------------------- 1 | const Staking = artifacts.require('Staking') 2 | const StakingProxy = artifacts.require('StakingProxy') 3 | const StandardTokenMock = artifacts.require('StandardTokenMock') 4 | 5 | contract('StakingProxy', ([_, owner]) => { 6 | let proxy, token, implementation 7 | 8 | const FORWARDING_TYPE = 1 9 | 10 | beforeEach('deploy sample token and staking implementation', async () => { 11 | token = await StandardTokenMock.new(owner, 100000, { from: owner }) 12 | implementation = await Staking.new() 13 | proxy = await StakingProxy.new(implementation.address, token.address) 14 | }) 15 | 16 | describe('initialize', async () => { 17 | it('initializes the given implementation', async () => { 18 | const staking = await Staking.at(proxy.address) 19 | assert(await staking.hasInitialized(), 'should have been initialized') 20 | }) 21 | }) 22 | 23 | describe('implementation', async () => { 24 | it('uses an unstructured storage slot for the implementation address', async () => { 25 | const implementationAddress = await web3.eth.getStorageAt(proxy.address, web3.utils.sha3('aragon.network.staking')) 26 | assert.equal(implementationAddress.toLowerCase(), implementation.address.toLowerCase(), 'implementation address does not match') 27 | }) 28 | 29 | it('uses the given implementation', async () => { 30 | const implementationAddress = await proxy.implementation() 31 | assert.equal(implementationAddress, implementation.address, 'implementation address does not match') 32 | }) 33 | }) 34 | 35 | describe('proxyType', () => { 36 | it('is a forwarding type', async () => { 37 | assert.equal(await proxy.proxyType(), FORWARDING_TYPE, 'proxy type does not match') 38 | }) 39 | }) 40 | 41 | describe('fallback', () => { 42 | it('forward calls to the implementation set', async () => { 43 | const staking = await Staking.at(proxy.address) 44 | assert.equal(await staking.token(), token.address, 'token address does not match') 45 | }) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /tests-solidity/suites/staking/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { 3 | // Development network is just left as truffle's default settings 4 | ethermint: { 5 | host: "127.0.0.1", // Localhost (default: none) 6 | port: 8545, // Standard Ethereum port (default: none) 7 | network_id: "*", // Any network (default: none) 8 | gas: 7000000, // Gas sent with each transaction 9 | gasPrice: 1000000000, // 1 gwei (in wei) 10 | }, 11 | }, 12 | compilers: { 13 | solc: { 14 | version: "0.5.17", // A version or constraint - Ex. "^0.5.0". 15 | settings: { 16 | optimizer: { 17 | enabled: true, 18 | runs: 10000, 19 | }, 20 | }, 21 | }, 22 | }, 23 | } 24 | -------------------------------------------------------------------------------- /types/chain_id.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "regexp" 7 | "strings" 8 | 9 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 10 | ) 11 | 12 | var ( 13 | regexChainID = `[a-z]*` 14 | regexSeparator = `-{1}` 15 | regexEpoch = `[1-9][0-9]*` 16 | ethermintChainID = regexp.MustCompile(fmt.Sprintf(`^(%s)%s(%s)$`, regexChainID, regexSeparator, regexEpoch)) 17 | ) 18 | 19 | // IsValidChainID returns false if the given chain identifier is incorrectly formatted. 20 | func IsValidChainID(chainID string) bool { 21 | if len(chainID) > 48 { 22 | return false 23 | } 24 | 25 | return ethermintChainID.MatchString(chainID) 26 | } 27 | 28 | // ParseChainID parses a string chain identifier's epoch to an Ethereum-compatible 29 | // chain-id in *big.Int format. The function returns an error if the chain-id has an invalid format 30 | func ParseChainID(chainID string) (*big.Int, error) { 31 | chainID = strings.TrimSpace(chainID) 32 | if len(chainID) > 48 { 33 | return nil, sdkerrors.Wrapf(ErrInvalidChainID, "chain-id '%s' cannot exceed 48 chars", chainID) 34 | } 35 | 36 | matches := ethermintChainID.FindStringSubmatch(chainID) 37 | if matches == nil || len(matches) != 3 || matches[1] == "" { 38 | return nil, sdkerrors.Wrap(ErrInvalidChainID, chainID) 39 | } 40 | 41 | // verify that the chain-id entered is a base 10 integer 42 | chainIDInt, ok := new(big.Int).SetString(matches[2], 10) 43 | if !ok { 44 | return nil, sdkerrors.Wrapf(ErrInvalidChainID, "epoch %s must be base-10 integer format", matches[2]) 45 | } 46 | 47 | return chainIDInt, nil 48 | } 49 | -------------------------------------------------------------------------------- /types/chain_id_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "math/big" 5 | "strings" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/require" 9 | ) 10 | 11 | func TestParseChainID(t *testing.T) { 12 | testCases := []struct { 13 | name string 14 | chainID string 15 | expError bool 16 | expInt *big.Int 17 | }{ 18 | { 19 | "valid chain-id, single digit", "ethermint-1", false, big.NewInt(1), 20 | }, 21 | { 22 | "valid chain-id, multiple digits", "aragonchain-256", false, big.NewInt(256), 23 | }, 24 | { 25 | "invalid chain-id, double dash", "aragon-chain-1", true, nil, 26 | }, 27 | { 28 | "invalid chain-id, dash only", "-", true, nil, 29 | }, 30 | { 31 | "invalid chain-id, undefined", "-1", true, nil, 32 | }, 33 | { 34 | "invalid chain-id, uppercases", "ETHERMINT-1", true, nil, 35 | }, 36 | { 37 | "invalid chain-id, mixed cases", "Ethermint-1", true, nil, 38 | }, 39 | { 40 | "invalid chain-id, special chars", "$&*#!-1", true, nil, 41 | }, 42 | { 43 | "invalid epoch, cannot start with 0", "ethermint-001", true, nil, 44 | }, 45 | { 46 | "invalid epoch, cannot invalid base", "ethermint-0x212", true, nil, 47 | }, 48 | { 49 | "invalid epoch, non-integer", "ethermint-ethermint", true, nil, 50 | }, 51 | { 52 | "invalid epoch, undefined", "ethermint-", true, nil, 53 | }, 54 | { 55 | "blank chain ID", " ", true, nil, 56 | }, 57 | { 58 | "empty chain ID", "", true, nil, 59 | }, 60 | { 61 | "long chain-id", "ethermint-" + strings.Repeat("1", 40), true, nil, 62 | }, 63 | } 64 | 65 | for _, tc := range testCases { 66 | chainIDEpoch, err := ParseChainID(tc.chainID) 67 | if tc.expError { 68 | require.Error(t, err, tc.name) 69 | require.Nil(t, chainIDEpoch) 70 | } else { 71 | require.NoError(t, err, tc.name) 72 | require.Equal(t, tc.expInt, chainIDEpoch, tc.name) 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /types/code.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // ---------------------------------------------------------------------------- 4 | // Code 5 | // ---------------------------------------------------------------------------- 6 | 7 | // Code is account Code type alias 8 | type Code []byte 9 | 10 | func (c Code) String() string { 11 | return string(c) 12 | } 13 | -------------------------------------------------------------------------------- /types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | ) 6 | 7 | const ( 8 | // EthAccountName is the amino encoding name for EthAccount 9 | EthAccountName = "ethermint/EthAccount" 10 | ) 11 | 12 | // RegisterCodec registers the account interfaces and concrete types on the 13 | // provided Amino codec. 14 | func RegisterCodec(cdc *codec.Codec) { 15 | cdc.RegisterConcrete(&EthAccount{}, EthAccountName, nil) 16 | } 17 | -------------------------------------------------------------------------------- /types/coin.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | ) 6 | 7 | const ( 8 | // AttoPhoton defines the default coin denomination used in Ethermint in: 9 | // 10 | // - Staking parameters: denomination used as stake in the dPoS chain 11 | // - Mint parameters: denomination minted due to fee distribution rewards 12 | // - Governance parameters: denomination used for spam prevention in proposal deposits 13 | // - Crisis parameters: constant fee denomination used for spam prevention to check broken invariant 14 | // - EVM parameters: denomination used for running EVM state transitions in Ethermint. 15 | AttoPhoton string = "aphoton" 16 | 17 | // BaseDenomUnit defines the base denomination unit for Photons. 18 | // 1 photon = 1x10^{BaseDenomUnit} aphoton 19 | BaseDenomUnit = 18 20 | ) 21 | 22 | // NewPhotonCoin is a utility function that returns an "aphoton" coin with the given sdk.Int amount. 23 | // The function will panic if the provided amount is negative. 24 | func NewPhotonCoin(amount sdk.Int) sdk.Coin { 25 | return sdk.NewCoin(AttoPhoton, amount) 26 | } 27 | 28 | // NewPhotonDecCoin is a utility function that returns an "aphoton" decimal coin with the given sdk.Int amount. 29 | // The function will panic if the provided amount is negative. 30 | func NewPhotonDecCoin(amount sdk.Int) sdk.DecCoin { 31 | return sdk.NewDecCoin(AttoPhoton, amount) 32 | } 33 | 34 | // NewPhotonCoinInt64 is a utility function that returns an "aphoton" coin with the given int64 amount. 35 | // The function will panic if the provided amount is negative. 36 | func NewPhotonCoinInt64(amount int64) sdk.Coin { 37 | return sdk.NewInt64Coin(AttoPhoton, amount) 38 | } 39 | -------------------------------------------------------------------------------- /types/config.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | ethaccounts "github.com/ethereum/go-ethereum/accounts" 6 | ) 7 | 8 | const ( 9 | // EthBech32Prefix defines the Bech32 prefix used for EthAccounts 10 | EthBech32Prefix = "eth" 11 | 12 | // Bech32PrefixAccAddr defines the Bech32 prefix of an account's address 13 | Bech32PrefixAccAddr = EthBech32Prefix 14 | // Bech32PrefixAccPub defines the Bech32 prefix of an account's public key 15 | Bech32PrefixAccPub = EthBech32Prefix + sdk.PrefixPublic 16 | // Bech32PrefixValAddr defines the Bech32 prefix of a validator's operator address 17 | Bech32PrefixValAddr = EthBech32Prefix + sdk.PrefixValidator + sdk.PrefixOperator 18 | // Bech32PrefixValPub defines the Bech32 prefix of a validator's operator public key 19 | Bech32PrefixValPub = EthBech32Prefix + sdk.PrefixValidator + sdk.PrefixOperator + sdk.PrefixPublic 20 | // Bech32PrefixConsAddr defines the Bech32 prefix of a consensus node address 21 | Bech32PrefixConsAddr = EthBech32Prefix + sdk.PrefixValidator + sdk.PrefixConsensus 22 | // Bech32PrefixConsPub defines the Bech32 prefix of a consensus node public key 23 | Bech32PrefixConsPub = EthBech32Prefix + sdk.PrefixValidator + sdk.PrefixConsensus + sdk.PrefixPublic 24 | 25 | // Bip44CoinType satisfies EIP84. See https://github.com/ethereum/EIPs/issues/84 for more info. 26 | Bip44CoinType = 60 27 | ) 28 | 29 | var ( 30 | // BIP44HDPath is the BIP44 HD path used on Ethereum. 31 | BIP44HDPath = ethaccounts.DefaultBaseDerivationPath.String() 32 | ) 33 | 34 | // SetBech32Prefixes sets the global prefixes to be used when serializing addresses and public keys to Bech32 strings. 35 | func SetBech32Prefixes(config *sdk.Config) { 36 | config.SetBech32PrefixForAccount(Bech32PrefixAccAddr, Bech32PrefixAccPub) 37 | config.SetBech32PrefixForValidator(Bech32PrefixValAddr, Bech32PrefixValPub) 38 | config.SetBech32PrefixForConsensusNode(Bech32PrefixConsAddr, Bech32PrefixConsPub) 39 | } 40 | 41 | // SetBip44CoinType sets the global coin type to be used in hierarchical deterministic wallets. 42 | func SetBip44CoinType(config *sdk.Config) { 43 | config.SetCoinType(Bip44CoinType) 44 | config.SetFullFundraiserPath(BIP44HDPath) 45 | } 46 | -------------------------------------------------------------------------------- /types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 5 | ) 6 | 7 | const ( 8 | // RootCodespace is the codespace for all errors defined in this package 9 | RootCodespace = "ethermint" 10 | ) 11 | 12 | // NOTE: We can't use 1 since that error code is reserved for internal errors. 13 | 14 | var ( 15 | // ErrInvalidValue returns an error resulting from an invalid value. 16 | ErrInvalidValue = sdkerrors.Register(RootCodespace, 2, "invalid value") 17 | 18 | // ErrInvalidChainID returns an error resulting from an invalid chain ID. 19 | ErrInvalidChainID = sdkerrors.Register(RootCodespace, 3, "invalid chain ID") 20 | 21 | // ErrVMExecution returns an error resulting from an error in EVM execution. 22 | ErrVMExecution = sdkerrors.Register(RootCodespace, 4, "error while executing evm transaction") 23 | ) 24 | -------------------------------------------------------------------------------- /types/params.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // DefaultGasPrice is default gas price for evm transactions 5 | DefaultGasPrice = 20 6 | // DefaultRPCGasLimit is default gas limit for RPC call operations 7 | DefaultRPCGasLimit = 10000000 8 | ) 9 | -------------------------------------------------------------------------------- /types/types.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package ethermint.v1; 3 | 4 | import "third_party/proto/gogoproto/gogo.proto"; 5 | import "third_party/proto/cosmos-sdk/x/auth/types/types.proto"; 6 | 7 | option go_package = "github.com/cosmos/ethermint/types"; 8 | 9 | 10 | // EthAccount implements the auth.Account interface and embeds an 11 | // auth.BaseAccount type. It is compatible with the auth.AccountKeeper. 12 | message EthAccount { 13 | option (gogoproto.goproto_getters) = false; 14 | option (gogoproto.goproto_stringer) = false; 15 | 16 | cosmos_sdk.x.auth.v1.BaseAccount base_account = 1 [ 17 | (gogoproto.embed) = true, 18 | (gogoproto.moretags) = "yaml:\"base_account\"" 19 | ]; 20 | bytes code_hash = 2 [ 21 | (gogoproto.moretags) = "yaml:\"code_hash\"" 22 | ]; 23 | } -------------------------------------------------------------------------------- /utils/int.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "math/big" 4 | 5 | // MarshalBigInt marshalls big int into text string for consistent encoding 6 | func MarshalBigInt(i *big.Int) (string, error) { 7 | bz, err := i.MarshalText() 8 | if err != nil { 9 | return "", err 10 | } 11 | return string(bz), nil 12 | } 13 | 14 | // MustMarshalBigInt marshalls big int into text string for consistent encoding. 15 | // It panics if an error is encountered. 16 | func MustMarshalBigInt(i *big.Int) string { 17 | str, err := MarshalBigInt(i) 18 | if err != nil { 19 | panic(err) 20 | } 21 | return str 22 | } 23 | 24 | // UnmarshalBigInt unmarshalls string from *big.Int 25 | func UnmarshalBigInt(s string) (*big.Int, error) { 26 | ret := new(big.Int) 27 | err := ret.UnmarshalText([]byte(s)) 28 | if err != nil { 29 | return nil, err 30 | } 31 | return ret, nil 32 | } 33 | 34 | // MustUnmarshalBigInt unmarshalls string from *big.Int. 35 | // It panics if an error is encountered. 36 | func MustUnmarshalBigInt(s string) *big.Int { 37 | ret, err := UnmarshalBigInt(s) 38 | if err != nil { 39 | panic(err) 40 | } 41 | return ret 42 | } 43 | -------------------------------------------------------------------------------- /utils/int_test.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestMarshalAndUnmarshalInt(t *testing.T) { 11 | i := big.NewInt(3) 12 | m, err := MarshalBigInt(i) 13 | require.NoError(t, err) 14 | 15 | i2, err := UnmarshalBigInt(m) 16 | require.NoError(t, err) 17 | require.Equal(t, i, i2) 18 | } 19 | -------------------------------------------------------------------------------- /version/version.go: -------------------------------------------------------------------------------- 1 | package version 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | ) 7 | 8 | // AppName represents the application name as the 'user agent' on the larger Ethereum network. 9 | const AppName = "Ethermint" 10 | 11 | // Version contains the application semantic version. 12 | // 13 | // TODO: How do we want to version this being that an initial Ethermint has 14 | // been developed? 15 | const Version = "0.0.0" 16 | 17 | // ProtocolVersion is the supported Ethereum protocol version (e.g., Homestead, Olympic, etc.) 18 | const ProtocolVersion = 63 19 | 20 | // GitCommit contains the git SHA1 short hash set by build flags. 21 | var GitCommit = "" 22 | 23 | // ClientVersion returns the full version string for identification on the larger Ethereum network. 24 | func ClientVersion() string { 25 | return fmt.Sprintf("%s/%s+%s/%s/%s", AppName, Version, GitCommit, runtime.GOOS, runtime.Version()) 26 | } 27 | -------------------------------------------------------------------------------- /x/evm/abci.go: -------------------------------------------------------------------------------- 1 | package evm 2 | 3 | import ( 4 | "math/big" 5 | 6 | abci "github.com/tendermint/tendermint/abci/types" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | 10 | ethtypes "github.com/ethereum/go-ethereum/core/types" 11 | ) 12 | 13 | // BeginBlock sets the block hash -> block height map and resets the Bloom filter and 14 | // the transaction count to 0. 15 | func BeginBlock(k Keeper, ctx sdk.Context, req abci.RequestBeginBlock) { 16 | if req.Header.LastBlockId.GetHash() == nil || req.Header.GetHeight() < 1 { 17 | return 18 | } 19 | 20 | k.SetBlockHash(ctx, req.Header.LastBlockId.GetHash(), req.Header.GetHeight()-1) 21 | 22 | // reset counters 23 | k.Bloom = big.NewInt(0) 24 | k.TxCount = 0 25 | } 26 | 27 | // EndBlock updates the accounts and commits states objects to the KV Store. 28 | // 29 | func EndBlock(k Keeper, ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { 30 | // Gas costs are handled within msg handler so costs should be ignored 31 | ctx = ctx.WithBlockGasMeter(sdk.NewInfiniteGasMeter()) 32 | 33 | // Update account balances before committing other parts of state 34 | k.UpdateAccounts(ctx) 35 | 36 | // Commit state objects to KV store 37 | _, err := k.Commit(ctx, true) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | // Clear accounts cache after account data has been committed 43 | k.ClearStateObjects(ctx) 44 | 45 | bloom := ethtypes.BytesToBloom(k.Bloom.Bytes()) 46 | k.SetBlockBloom(ctx, ctx.BlockHeight(), bloom) 47 | 48 | return []abci.ValidatorUpdate{} 49 | } 50 | -------------------------------------------------------------------------------- /x/evm/alias.go: -------------------------------------------------------------------------------- 1 | package evm 2 | 3 | import ( 4 | "github.com/cosmos/ethermint/x/evm/keeper" 5 | "github.com/cosmos/ethermint/x/evm/types" 6 | ) 7 | 8 | // nolint 9 | const ( 10 | ModuleName = types.ModuleName 11 | StoreKey = types.StoreKey 12 | RouterKey = types.RouterKey 13 | DefaultParamspace = types.DefaultParamspace 14 | ) 15 | 16 | // nolint 17 | var ( 18 | NewKeeper = keeper.NewKeeper 19 | TxDecoder = types.TxDecoder 20 | ) 21 | 22 | //nolint 23 | type ( 24 | Keeper = keeper.Keeper 25 | GenesisState = types.GenesisState 26 | ) 27 | -------------------------------------------------------------------------------- /x/evm/client/cli/utils.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/pkg/errors" 8 | 9 | "github.com/ethereum/go-ethereum/common" 10 | 11 | sdk "github.com/cosmos/cosmos-sdk/types" 12 | ) 13 | 14 | func accountToHex(addr string) (string, error) { 15 | if strings.HasPrefix(addr, sdk.GetConfig().GetBech32AccountAddrPrefix()) { 16 | // Check to see if address is Cosmos bech32 formatted 17 | toAddr, err := sdk.AccAddressFromBech32(addr) 18 | if err != nil { 19 | return "", errors.Wrap(err, "must provide a valid Bech32 address") 20 | } 21 | ethAddr := common.BytesToAddress(toAddr.Bytes()) 22 | return ethAddr.Hex(), nil 23 | } 24 | 25 | if !strings.HasPrefix(addr, "0x") { 26 | addr = "0x" + addr 27 | } 28 | 29 | valid := common.IsHexAddress(addr) 30 | if !valid { 31 | return "", fmt.Errorf("%s is not a valid Ethereum or Cosmos address", addr) 32 | } 33 | 34 | ethAddr := common.HexToAddress(addr) 35 | 36 | return ethAddr.Hex(), nil 37 | } 38 | 39 | func formatKeyToHash(key string) string { 40 | if !strings.HasPrefix(key, "0x") { 41 | key = "0x" + key 42 | } 43 | 44 | ethkey := common.HexToHash(key) 45 | 46 | return ethkey.Hex() 47 | } 48 | 49 | func cosmosAddressFromArg(addr string) (sdk.AccAddress, error) { 50 | if strings.HasPrefix(addr, sdk.GetConfig().GetBech32AccountAddrPrefix()) { 51 | // Check to see if address is Cosmos bech32 formatted 52 | toAddr, err := sdk.AccAddressFromBech32(addr) 53 | if err != nil { 54 | return nil, errors.Wrap(err, "invalid bech32 formatted address") 55 | } 56 | return toAddr, nil 57 | } 58 | 59 | // Strip 0x prefix if exists 60 | addr = strings.TrimPrefix(addr, "0x") 61 | 62 | return sdk.AccAddressFromHex(addr) 63 | } 64 | -------------------------------------------------------------------------------- /x/evm/genesis.go: -------------------------------------------------------------------------------- 1 | package evm 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | 6 | emint "github.com/cosmos/ethermint/types" 7 | "github.com/cosmos/ethermint/x/evm/types" 8 | 9 | abci "github.com/tendermint/tendermint/abci/types" 10 | ) 11 | 12 | // InitGenesis initializes genesis state based on exported genesis 13 | func InitGenesis(ctx sdk.Context, k Keeper, data GenesisState) []abci.ValidatorUpdate { 14 | for _, account := range data.Accounts { 15 | // FIXME: this will override bank InitGenesis balance! 16 | k.SetBalance(ctx, account.Address, account.Balance) 17 | k.SetCode(ctx, account.Address, account.Code) 18 | for _, storage := range account.Storage { 19 | k.SetState(ctx, account.Address, storage.Key, storage.Value) 20 | } 21 | } 22 | 23 | var err error 24 | for _, txLog := range data.TxsLogs { 25 | err = k.SetLogs(ctx, txLog.Hash, txLog.Logs) 26 | if err != nil { 27 | panic(err) 28 | } 29 | } 30 | 31 | k.SetChainConfig(ctx, data.ChainConfig) 32 | k.SetParams(ctx, data.Params) 33 | 34 | // set state objects and code to store 35 | _, err = k.Commit(ctx, false) 36 | if err != nil { 37 | panic(err) 38 | } 39 | 40 | // set storage to store 41 | // NOTE: don't delete empty object to prevent import-export simulation failure 42 | err = k.Finalise(ctx, false) 43 | if err != nil { 44 | panic(err) 45 | } 46 | 47 | return []abci.ValidatorUpdate{} 48 | } 49 | 50 | // ExportGenesis exports genesis state of the EVM module 51 | func ExportGenesis(ctx sdk.Context, k Keeper, ak types.AccountKeeper) GenesisState { 52 | // nolint: prealloc 53 | var ethGenAccounts []types.GenesisAccount 54 | accounts := ak.GetAllAccounts(ctx) 55 | 56 | for _, account := range accounts { 57 | 58 | ethAccount, ok := account.(*emint.EthAccount) 59 | if !ok { 60 | continue 61 | } 62 | 63 | addr := ethAccount.EthAddress() 64 | 65 | storage, err := k.GetAccountStorage(ctx, addr) 66 | if err != nil { 67 | panic(err) 68 | } 69 | 70 | genAccount := types.GenesisAccount{ 71 | Address: addr, 72 | Balance: k.GetBalance(ctx, addr), 73 | Code: k.GetCode(ctx, addr), 74 | Storage: storage, 75 | } 76 | 77 | ethGenAccounts = append(ethGenAccounts, genAccount) 78 | } 79 | 80 | config, _ := k.GetChainConfig(ctx) 81 | 82 | return GenesisState{ 83 | Accounts: ethGenAccounts, 84 | TxsLogs: k.GetAllTxLogs(ctx), 85 | ChainConfig: config, 86 | Params: k.GetParams(ctx), 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /x/evm/keeper/params.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | 6 | "github.com/cosmos/ethermint/x/evm/types" 7 | ) 8 | 9 | // GetParams returns the total set of evm parameters. 10 | func (k Keeper) GetParams(ctx sdk.Context) (params types.Params) { 11 | return k.CommitStateDB.WithContext(ctx).GetParams() 12 | } 13 | 14 | // SetParams sets the evm parameters to the param space. 15 | func (k Keeper) SetParams(ctx sdk.Context, params types.Params) { 16 | k.CommitStateDB.WithContext(ctx).SetParams(params) 17 | } 18 | -------------------------------------------------------------------------------- /x/evm/keeper/params_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "github.com/cosmos/ethermint/x/evm/types" 5 | ) 6 | 7 | func (suite *KeeperTestSuite) TestParams() { 8 | params := suite.app.EvmKeeper.GetParams(suite.ctx) 9 | suite.Require().Equal(types.DefaultParams(), params) 10 | params.EvmDenom = "ara" 11 | suite.app.EvmKeeper.SetParams(suite.ctx, params) 12 | newParams := suite.app.EvmKeeper.GetParams(suite.ctx) 13 | suite.Require().Equal(newParams, params) 14 | } 15 | -------------------------------------------------------------------------------- /x/evm/keeper/querier_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "math/big" 5 | 6 | "github.com/cosmos/ethermint/x/evm/types" 7 | ethtypes "github.com/ethereum/go-ethereum/core/types" 8 | 9 | abci "github.com/tendermint/tendermint/abci/types" 10 | ) 11 | 12 | func (suite *KeeperTestSuite) TestQuerier() { 13 | 14 | testCases := []struct { 15 | msg string 16 | path []string 17 | malleate func() 18 | expPass bool 19 | }{ 20 | {"protocol version", []string{types.QueryProtocolVersion}, func() {}, true}, 21 | {"balance", []string{types.QueryBalance, addrHex}, func() { 22 | suite.app.EvmKeeper.SetBalance(suite.ctx, suite.address, big.NewInt(5)) 23 | }, true}, 24 | // {"balance fail", []string{types.QueryBalance, "0x01232"}, func() {}, false}, 25 | {"block number", []string{types.QueryBlockNumber, "0x0"}, func() {}, true}, 26 | {"storage", []string{types.QueryStorage, "0x0", "0x0"}, func() {}, true}, 27 | {"code", []string{types.QueryCode, "0x0"}, func() {}, true}, 28 | {"hash to height", []string{types.QueryHashToHeight, hex}, func() { 29 | suite.app.EvmKeeper.SetBlockHash(suite.ctx, hash, 8) 30 | }, true}, 31 | {"tx logs", []string{types.QueryTransactionLogs, "0x0"}, func() {}, true}, 32 | {"bloom", []string{types.QueryBloom, "4"}, func() { 33 | testBloom := ethtypes.BytesToBloom([]byte{0x1, 0x3}) 34 | suite.app.EvmKeeper.SetBlockBloom(suite.ctx, 4, testBloom) 35 | }, true}, 36 | {"logs", []string{types.QueryLogs, "0x0"}, func() {}, true}, 37 | {"account", []string{types.QueryAccount, "0x0"}, func() {}, true}, 38 | {"exportAccount", []string{types.QueryExportAccount, "0x0"}, func() {}, true}, 39 | {"unknown request", []string{"other"}, func() {}, false}, 40 | } 41 | 42 | for i, tc := range testCases { 43 | suite.Run("", func() { 44 | //nolint 45 | tc := tc 46 | suite.SetupTest() // reset 47 | //nolint 48 | tc.malleate() 49 | 50 | bz, err := suite.querier(suite.ctx, tc.path, abci.RequestQuery{}) 51 | 52 | //nolint 53 | if tc.expPass { 54 | //nolint 55 | suite.Require().NoError(err, "valid test %d failed: %s", i, tc.msg) 56 | suite.Require().NotZero(len(bz)) 57 | } else { 58 | //nolint 59 | suite.Require().Error(err, "invalid test %d passed: %s", i, tc.msg) 60 | } 61 | }) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /x/evm/module_test.go: -------------------------------------------------------------------------------- 1 | package evm_test 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/cosmos/ethermint/x/evm" 7 | 8 | "github.com/ethereum/go-ethereum/common" 9 | ) 10 | 11 | var testJSON = `{ 12 | "accounts": [ 13 | { 14 | "address": "0x00cabdd44664b73cfc3194b9d32eb6c351ef7652", 15 | "balance": 34 16 | }, 17 | { 18 | "address": "0x2cc7fdf9fde6746731d7f11979609d455c2c197a", 19 | "balance": 0, 20 | "code": "0x60806040" 21 | } 22 | ], 23 | "params": { 24 | "evm_denom": "aphoton" 25 | } 26 | }` 27 | 28 | func (suite *EvmTestSuite) TestInitGenesis() { 29 | am := evm.NewAppModule(suite.app.EvmKeeper, suite.app.AccountKeeper) 30 | in := json.RawMessage([]byte(testJSON)) 31 | _ = am.InitGenesis(suite.ctx, in) 32 | 33 | testAddr := common.HexToAddress("0x2cc7fdf9fde6746731d7f11979609d455c2c197a") 34 | 35 | res := suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx).GetCode(testAddr) 36 | expectedCode := common.FromHex("0x60806040") 37 | suite.Require().Equal(expectedCode, res) 38 | } 39 | -------------------------------------------------------------------------------- /x/evm/spec/README.md: -------------------------------------------------------------------------------- 1 | 7 | 8 | # `evm` 9 | 10 | ## Abstract 11 | 12 | 13 | 14 | ## Content 15 | 16 | -------------------------------------------------------------------------------- /x/evm/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | ) 6 | 7 | // ModuleCdc defines the evm module's codec 8 | var ModuleCdc = codec.New() 9 | 10 | // RegisterCodec registers all the necessary types and interfaces for the 11 | // evm module 12 | func RegisterCodec(cdc *codec.Codec) { 13 | cdc.RegisterConcrete(MsgEthereumTx{}, "ethermint/MsgEthereumTx", nil) 14 | cdc.RegisterConcrete(MsgEthermint{}, "ethermint/MsgEthermint", nil) 15 | cdc.RegisterConcrete(TxData{}, "ethermint/TxData", nil) 16 | cdc.RegisterConcrete(ChainConfig{}, "ethermint/ChainConfig", nil) 17 | } 18 | 19 | func init() { 20 | RegisterCodec(ModuleCdc) 21 | codec.RegisterCrypto(ModuleCdc) 22 | ModuleCdc.Seal() 23 | } 24 | -------------------------------------------------------------------------------- /x/evm/types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 5 | ) 6 | 7 | // NOTE: We can't use 1 since that error code is reserved for internal errors. 8 | 9 | var ( 10 | // ErrInvalidState returns an error resulting from an invalid Storage State. 11 | ErrInvalidState = sdkerrors.Register(ModuleName, 2, "invalid storage state") 12 | 13 | // ErrChainConfigNotFound returns an error if the chain config cannot be found on the store. 14 | ErrChainConfigNotFound = sdkerrors.Register(ModuleName, 3, "chain configuration not found") 15 | 16 | // ErrInvalidChainConfig returns an error resulting from an invalid ChainConfig. 17 | ErrInvalidChainConfig = sdkerrors.Register(ModuleName, 4, "invalid chain configuration") 18 | ) 19 | -------------------------------------------------------------------------------- /x/evm/types/events.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // Evm module events 4 | const ( 5 | EventTypeEthermint = TypeMsgEthermint 6 | EventTypeEthereumTx = TypeMsgEthereumTx 7 | 8 | AttributeKeyContractAddress = "contract" 9 | AttributeKeyRecipient = "recipient" 10 | AttributeValueCategory = ModuleName 11 | ) 12 | -------------------------------------------------------------------------------- /x/evm/types/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" 6 | ) 7 | 8 | // AccountKeeper defines the expected account keeper interface 9 | type AccountKeeper interface { 10 | NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authexported.Account 11 | GetAllAccounts(ctx sdk.Context) (accounts []authexported.Account) 12 | GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account 13 | SetAccount(ctx sdk.Context, account authexported.Account) 14 | RemoveAccount(ctx sdk.Context, account authexported.Account) 15 | } 16 | -------------------------------------------------------------------------------- /x/evm/types/key.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | 6 | ethcmn "github.com/ethereum/go-ethereum/common" 7 | ) 8 | 9 | const ( 10 | // ModuleName string name of module 11 | ModuleName = "evm" 12 | 13 | // StoreKey key for ethereum storage data, account code (StateDB) or block 14 | // related data for Web3. 15 | // The EVM module should use a prefix store. 16 | StoreKey = ModuleName 17 | 18 | // RouterKey uses module name for routing 19 | RouterKey = ModuleName 20 | ) 21 | 22 | // KVStore key prefixes 23 | var ( 24 | KeyPrefixBlockHash = []byte{0x01} 25 | KeyPrefixBloom = []byte{0x02} 26 | KeyPrefixLogs = []byte{0x03} 27 | KeyPrefixCode = []byte{0x04} 28 | KeyPrefixStorage = []byte{0x05} 29 | KeyPrefixChainConfig = []byte{0x06} 30 | ) 31 | 32 | // BloomKey defines the store key for a block Bloom 33 | func BloomKey(height int64) []byte { 34 | return sdk.Uint64ToBigEndian(uint64(height)) 35 | } 36 | 37 | // AddressStoragePrefix returns a prefix to iterate over a given account storage. 38 | func AddressStoragePrefix(address ethcmn.Address) []byte { 39 | return append(KeyPrefixStorage, address.Bytes()...) 40 | } 41 | -------------------------------------------------------------------------------- /x/evm/types/logs.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | 8 | ethcmn "github.com/ethereum/go-ethereum/common" 9 | ethtypes "github.com/ethereum/go-ethereum/core/types" 10 | ) 11 | 12 | // TransactionLogs define the logs generated from a transaction execution 13 | // with a given hash. It it used for import/export data as transactions are not persisted 14 | // on blockchain state after an upgrade. 15 | type TransactionLogs struct { 16 | Hash ethcmn.Hash `json:"hash"` 17 | Logs []*ethtypes.Log `json:"logs"` 18 | } 19 | 20 | // NewTransactionLogs creates a new NewTransactionLogs instance. 21 | func NewTransactionLogs(hash ethcmn.Hash, logs []*ethtypes.Log) TransactionLogs { 22 | return TransactionLogs{ 23 | Hash: hash, 24 | Logs: logs, 25 | } 26 | } 27 | 28 | // MarshalLogs encodes an array of logs using amino 29 | func MarshalLogs(logs []*ethtypes.Log) ([]byte, error) { 30 | return ModuleCdc.MarshalBinaryLengthPrefixed(logs) 31 | } 32 | 33 | // UnmarshalLogs decodes an amino-encoded byte array into an array of logs 34 | func UnmarshalLogs(in []byte) ([]*ethtypes.Log, error) { 35 | logs := []*ethtypes.Log{} 36 | err := ModuleCdc.UnmarshalBinaryLengthPrefixed(in, &logs) 37 | return logs, err 38 | } 39 | 40 | // Validate performs a basic validation of a GenesisAccount fields. 41 | func (tx TransactionLogs) Validate() error { 42 | if bytes.Equal(tx.Hash.Bytes(), ethcmn.Hash{}.Bytes()) { 43 | return fmt.Errorf("hash cannot be the empty %s", tx.Hash.String()) 44 | } 45 | 46 | for i, log := range tx.Logs { 47 | if err := ValidateLog(log); err != nil { 48 | return fmt.Errorf("invalid log %d: %w", i, err) 49 | } 50 | if !bytes.Equal(log.TxHash.Bytes(), tx.Hash.Bytes()) { 51 | return fmt.Errorf("log tx hash mismatch (%s ≠ %s)", log.TxHash.String(), tx.Hash.String()) 52 | } 53 | } 54 | return nil 55 | } 56 | 57 | // ValidateLog performs a basic validation of an ethereum Log fields. 58 | func ValidateLog(log *ethtypes.Log) error { 59 | if log == nil { 60 | return errors.New("log cannot be nil") 61 | } 62 | if bytes.Equal(log.Address.Bytes(), ethcmn.Address{}.Bytes()) { 63 | return fmt.Errorf("log address cannot be empty %s", log.Address.String()) 64 | } 65 | if bytes.Equal(log.BlockHash.Bytes(), ethcmn.Hash{}.Bytes()) { 66 | return fmt.Errorf("block hash cannot be the empty %s", log.BlockHash.String()) 67 | } 68 | if log.BlockNumber == 0 { 69 | return errors.New("block number cannot be zero") 70 | } 71 | if bytes.Equal(log.TxHash.Bytes(), ethcmn.Hash{}.Bytes()) { 72 | return fmt.Errorf("tx hash cannot be the empty %s", log.TxHash.String()) 73 | } 74 | return nil 75 | } 76 | -------------------------------------------------------------------------------- /x/evm/types/params.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | 6 | "gopkg.in/yaml.v2" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | "github.com/cosmos/cosmos-sdk/x/params" 10 | 11 | ethermint "github.com/cosmos/ethermint/types" 12 | ) 13 | 14 | const ( 15 | // DefaultParamspace for params keeper 16 | DefaultParamspace = ModuleName 17 | ) 18 | 19 | // Parameter keys 20 | var ( 21 | ParamStoreKeyEVMDenom = []byte("EVMDenom") 22 | ) 23 | 24 | // ParamKeyTable returns the parameter key table. 25 | func ParamKeyTable() params.KeyTable { 26 | return params.NewKeyTable().RegisterParamSet(&Params{}) 27 | } 28 | 29 | // Params defines the EVM module parameters 30 | type Params struct { 31 | EvmDenom string `json:"evm_denom" yaml:"evm_denom"` 32 | } 33 | 34 | // NewParams creates a new Params instance 35 | func NewParams(evmDenom string) Params { 36 | return Params{ 37 | EvmDenom: evmDenom, 38 | } 39 | } 40 | 41 | // DefaultParams returns default evm parameters 42 | func DefaultParams() Params { 43 | return Params{ 44 | EvmDenom: ethermint.AttoPhoton, 45 | } 46 | } 47 | 48 | // String implements the fmt.Stringer interface 49 | func (p Params) String() string { 50 | out, _ := yaml.Marshal(p) 51 | return string(out) 52 | } 53 | 54 | // ParamSetPairs returns the parameter set pairs. 55 | func (p *Params) ParamSetPairs() params.ParamSetPairs { 56 | return params.ParamSetPairs{ 57 | params.NewParamSetPair(ParamStoreKeyEVMDenom, &p.EvmDenom, validateEVMDenom), 58 | } 59 | } 60 | 61 | // Validate performs basic validation on evm parameters. 62 | func (p Params) Validate() error { 63 | return sdk.ValidateDenom(p.EvmDenom) 64 | } 65 | 66 | func validateEVMDenom(i interface{}) error { 67 | denom, ok := i.(string) 68 | if !ok { 69 | return fmt.Errorf("invalid parameter type: %T", i) 70 | } 71 | 72 | return sdk.ValidateDenom(denom) 73 | } 74 | -------------------------------------------------------------------------------- /x/evm/types/params_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | ) 8 | 9 | func TestParamsValidate(t *testing.T) { 10 | testCases := []struct { 11 | name string 12 | params Params 13 | expError bool 14 | }{ 15 | {"default", DefaultParams(), false}, 16 | { 17 | "valid", 18 | NewParams("ara"), 19 | false, 20 | }, 21 | { 22 | "empty", 23 | Params{}, 24 | true, 25 | }, 26 | { 27 | "invalid evm denom", 28 | Params{ 29 | EvmDenom: "@!#!@$!@5^32", 30 | }, 31 | true, 32 | }, 33 | } 34 | 35 | for _, tc := range testCases { 36 | err := tc.params.Validate() 37 | 38 | if tc.expError { 39 | require.Error(t, err, tc.name) 40 | } else { 41 | require.NoError(t, err, tc.name) 42 | } 43 | } 44 | } 45 | 46 | func TestParamsValidatePriv(t *testing.T) { 47 | require.Error(t, validateEVMDenom(false)) 48 | } 49 | 50 | func TestParams_String(t *testing.T) { 51 | require.Equal(t, "evm_denom: aphoton\n", DefaultParams().String()) 52 | } 53 | -------------------------------------------------------------------------------- /x/evm/types/storage.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | 7 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 8 | 9 | ethcmn "github.com/ethereum/go-ethereum/common" 10 | ) 11 | 12 | // Storage represents the account Storage map as a slice of single key value 13 | // State pairs. This is to prevent non determinism at genesis initialization or export. 14 | type Storage []State 15 | 16 | // Validate performs a basic validation of the Storage fields. 17 | func (s Storage) Validate() error { 18 | seenStorage := make(map[string]bool) 19 | for i, state := range s { 20 | if seenStorage[state.Key.String()] { 21 | return sdkerrors.Wrapf(ErrInvalidState, "duplicate state key %d", i) 22 | } 23 | 24 | if err := state.Validate(); err != nil { 25 | return err 26 | } 27 | 28 | seenStorage[state.Key.String()] = true 29 | } 30 | return nil 31 | } 32 | 33 | // String implements the stringer interface 34 | func (s Storage) String() string { 35 | var str string 36 | for _, state := range s { 37 | str += fmt.Sprintf("%s: %s\n", state.Key.String(), state.Value.String()) 38 | } 39 | 40 | return str 41 | } 42 | 43 | // Copy returns a copy of storage. 44 | func (s Storage) Copy() Storage { 45 | cpy := make(Storage, len(s)) 46 | copy(cpy, s) 47 | 48 | return cpy 49 | } 50 | 51 | // State represents a single Storage key value pair item. 52 | type State struct { 53 | Key ethcmn.Hash `json:"key"` 54 | Value ethcmn.Hash `json:"value"` 55 | } 56 | 57 | // Validate performs a basic validation of the State fields. 58 | func (s State) Validate() error { 59 | if bytes.Equal(s.Key.Bytes(), ethcmn.Hash{}.Bytes()) { 60 | return sdkerrors.Wrap(ErrInvalidState, "state key hash cannot be empty") 61 | } 62 | // NOTE: state value can be empty 63 | return nil 64 | } 65 | 66 | // NewState creates a new State instance 67 | func NewState(key, value ethcmn.Hash) State { 68 | return State{ 69 | Key: key, 70 | Value: value, 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /x/evm/types/storage_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "testing" 5 | 6 | ethcmn "github.com/ethereum/go-ethereum/common" 7 | "github.com/stretchr/testify/require" 8 | ) 9 | 10 | func TestStorageValidate(t *testing.T) { 11 | testCases := []struct { 12 | name string 13 | storage Storage 14 | expPass bool 15 | }{ 16 | { 17 | "valid storage", 18 | Storage{ 19 | NewState(ethcmn.BytesToHash([]byte{1, 2, 3}), ethcmn.BytesToHash([]byte{1, 2, 3})), 20 | }, 21 | true, 22 | }, 23 | { 24 | "empty storage key bytes", 25 | Storage{ 26 | {Key: ethcmn.Hash{}}, 27 | }, 28 | false, 29 | }, 30 | { 31 | "duplicated storage key", 32 | Storage{ 33 | {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, 34 | {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, 35 | }, 36 | false, 37 | }, 38 | } 39 | 40 | for _, tc := range testCases { 41 | tc := tc 42 | err := tc.storage.Validate() 43 | if tc.expPass { 44 | require.NoError(t, err, tc.name) 45 | } else { 46 | require.Error(t, err, tc.name) 47 | } 48 | } 49 | } 50 | 51 | func TestStorageCopy(t *testing.T) { 52 | testCases := []struct { 53 | name string 54 | storage Storage 55 | }{ 56 | { 57 | "single storage", 58 | Storage{ 59 | NewState(ethcmn.BytesToHash([]byte{1, 2, 3}), ethcmn.BytesToHash([]byte{1, 2, 3})), 60 | }, 61 | }, 62 | { 63 | "empty storage key value bytes", 64 | Storage{ 65 | {Key: ethcmn.Hash{}, Value: ethcmn.Hash{}}, 66 | }, 67 | }, 68 | { 69 | "empty storage", 70 | Storage{}, 71 | }, 72 | } 73 | 74 | for _, tc := range testCases { 75 | tc := tc 76 | require.Equal(t, tc.storage, tc.storage.Copy(), tc.name) 77 | } 78 | } 79 | 80 | func TestStorageString(t *testing.T) { 81 | storage := Storage{NewState(ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value")))} 82 | str := "0x00000000000000000000000000000000000000000000000000000000006b6579: 0x00000000000000000000000000000000000000000000000000000076616c7565\n" 83 | require.Equal(t, str, storage.String()) 84 | } 85 | -------------------------------------------------------------------------------- /x/evm/types/tx_data_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | ethcmn "github.com/ethereum/go-ethereum/common" 10 | ) 11 | 12 | func TestMarshalAndUnmarshalData(t *testing.T) { 13 | addr := GenerateEthAddress() 14 | hash := ethcmn.BigToHash(big.NewInt(2)) 15 | 16 | txData := TxData{ 17 | AccountNonce: 2, 18 | Price: big.NewInt(3), 19 | GasLimit: 1, 20 | Recipient: &addr, 21 | Amount: big.NewInt(4), 22 | Payload: []byte("test"), 23 | V: big.NewInt(5), 24 | R: big.NewInt(6), 25 | S: big.NewInt(7), 26 | Hash: &hash, 27 | } 28 | 29 | bz, err := txData.MarshalAmino() 30 | require.NoError(t, err) 31 | require.NotNil(t, bz) 32 | 33 | var txData2 TxData 34 | err = txData2.UnmarshalAmino(bz) 35 | require.NoError(t, err) 36 | 37 | require.Equal(t, txData, txData2) 38 | } 39 | 40 | func TestMsgEthereumTxAmino(t *testing.T) { 41 | addr := GenerateEthAddress() 42 | msg := NewMsgEthereumTx(5, &addr, big.NewInt(1), 100000, big.NewInt(3), []byte("test")) 43 | 44 | msg.Data.V = big.NewInt(1) 45 | msg.Data.R = big.NewInt(2) 46 | msg.Data.S = big.NewInt(3) 47 | 48 | raw, err := ModuleCdc.MarshalBinaryBare(msg) 49 | require.NoError(t, err) 50 | 51 | var msg2 MsgEthereumTx 52 | 53 | err = ModuleCdc.UnmarshalBinaryBare(raw, &msg2) 54 | require.NoError(t, err) 55 | require.Equal(t, msg, msg2) 56 | } 57 | -------------------------------------------------------------------------------- /x/evm/types/utils_test.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/require" 7 | 8 | ethcmn "github.com/ethereum/go-ethereum/common" 9 | ethtypes "github.com/ethereum/go-ethereum/core/types" 10 | ) 11 | 12 | func TestEvmDataEncoding(t *testing.T) { 13 | addr := ethcmn.HexToAddress("0x5dE8a020088a2D6d0a23c204FFbeD02790466B49") 14 | bloom := ethtypes.BytesToBloom([]byte{0x1, 0x3}) 15 | ret := []byte{0x5, 0x8} 16 | 17 | data := ResultData{ 18 | ContractAddress: addr, 19 | Bloom: bloom, 20 | Logs: []*ethtypes.Log{{ 21 | Data: []byte{1, 2, 3, 4}, 22 | BlockNumber: 17, 23 | }}, 24 | Ret: ret, 25 | } 26 | 27 | enc, err := EncodeResultData(data) 28 | require.NoError(t, err) 29 | 30 | res, err := DecodeResultData(enc) 31 | require.NoError(t, err) 32 | require.Equal(t, addr, res.ContractAddress) 33 | require.Equal(t, bloom, res.Bloom) 34 | require.Equal(t, data.Logs, res.Logs) 35 | require.Equal(t, ret, res.Ret) 36 | } 37 | -------------------------------------------------------------------------------- /x/faucet/alias.go: -------------------------------------------------------------------------------- 1 | package faucet 2 | 3 | import ( 4 | "github.com/cosmos/ethermint/x/faucet/keeper" 5 | "github.com/cosmos/ethermint/x/faucet/types" 6 | ) 7 | 8 | const ( 9 | ModuleName = types.ModuleName 10 | RouterKey = types.RouterKey 11 | StoreKey = types.StoreKey 12 | QuerierRoute = types.QuerierRoute 13 | ) 14 | 15 | var ( 16 | NewKeeper = keeper.NewKeeper 17 | NewQuerier = keeper.NewQuerier 18 | ModuleCdc = types.ModuleCdc 19 | RegisterCodec = types.RegisterCodec 20 | ) 21 | 22 | type ( 23 | Keeper = keeper.Keeper 24 | ) 25 | -------------------------------------------------------------------------------- /x/faucet/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/context" 10 | "github.com/cosmos/cosmos-sdk/client/flags" 11 | "github.com/cosmos/cosmos-sdk/codec" 12 | 13 | "github.com/cosmos/ethermint/x/faucet/types" 14 | 15 | sdk "github.com/cosmos/cosmos-sdk/types" 16 | ) 17 | 18 | // GetQueryCmd defines evm module queries through the cli 19 | func GetQueryCmd(cdc *codec.Codec) *cobra.Command { 20 | faucetQueryCmd := &cobra.Command{ 21 | Use: types.ModuleName, 22 | Short: fmt.Sprintf("Querying commands for the %s module", types.ModuleName), 23 | DisableFlagParsing: true, 24 | SuggestionsMinimumDistance: 2, 25 | RunE: client.ValidateCmd, 26 | } 27 | faucetQueryCmd.AddCommand(flags.GetCommands( 28 | GetCmdFunded(cdc), 29 | )...) 30 | return faucetQueryCmd 31 | } 32 | 33 | // GetCmdFunded queries the total amount funded by the faucet. 34 | func GetCmdFunded(cdc *codec.Codec) *cobra.Command { 35 | return &cobra.Command{ 36 | Use: "funded", 37 | Short: "Gets storage for an account at a given key", 38 | Args: cobra.NoArgs, 39 | RunE: func(cmd *cobra.Command, args []string) error { 40 | cliCtx := context.NewCLIContext().WithCodec(cdc) 41 | 42 | res, height, err := cliCtx.Query(fmt.Sprintf("custom/%s/%s", types.QuerierRoute, types.QueryFunded)) 43 | if err != nil { 44 | return err 45 | } 46 | 47 | var out sdk.Coins 48 | cdc.MustUnmarshalJSON(res, &out) 49 | cliCtx = cliCtx.WithHeight(height) 50 | return cliCtx.PrintOutput(out) 51 | }, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /x/faucet/client/cli/tx.go: -------------------------------------------------------------------------------- 1 | package cli 2 | 3 | import ( 4 | "bufio" 5 | 6 | "github.com/spf13/cobra" 7 | 8 | "github.com/cosmos/cosmos-sdk/client" 9 | "github.com/cosmos/cosmos-sdk/client/context" 10 | "github.com/cosmos/cosmos-sdk/client/flags" 11 | "github.com/cosmos/cosmos-sdk/codec" 12 | sdk "github.com/cosmos/cosmos-sdk/types" 13 | "github.com/cosmos/cosmos-sdk/x/auth" 14 | authclient "github.com/cosmos/cosmos-sdk/x/auth/client/utils" 15 | 16 | "github.com/cosmos/ethermint/x/faucet/types" 17 | ) 18 | 19 | // GetTxCmd return faucet sub-command for tx 20 | func GetTxCmd(cdc *codec.Codec) *cobra.Command { 21 | faucetTxCmd := &cobra.Command{ 22 | Use: types.ModuleName, 23 | Short: "faucet transaction subcommands", 24 | DisableFlagParsing: true, 25 | SuggestionsMinimumDistance: 2, 26 | RunE: client.ValidateCmd, 27 | } 28 | 29 | faucetTxCmd.AddCommand(flags.PostCommands( 30 | GetCmdRequest(cdc), 31 | )...) 32 | 33 | return faucetTxCmd 34 | } 35 | 36 | // GetCmdRequest is the CLI command to fund an address with the requested coins 37 | func GetCmdRequest(cdc *codec.Codec) *cobra.Command { 38 | return &cobra.Command{ 39 | Use: "request [amount] [other-recipient (optional)]", 40 | Short: "request an address with the requested coins", 41 | Args: cobra.RangeArgs(1, 2), 42 | RunE: func(cmd *cobra.Command, args []string) error { 43 | inBuf := bufio.NewReader(cmd.InOrStdin()) 44 | cliCtx := context.NewCLIContext().WithCodec(cdc) 45 | txBldr := auth.NewTxBuilderFromCLI(inBuf).WithTxEncoder(authclient.GetTxEncoder(cdc)) 46 | 47 | amount, err := sdk.ParseCoins(args[0]) 48 | if err != nil { 49 | return err 50 | } 51 | 52 | var recipient sdk.AccAddress 53 | if len(args) == 1 { 54 | recipient = cliCtx.GetFromAddress() 55 | } else { 56 | recipient, err = sdk.AccAddressFromBech32(args[1]) 57 | } 58 | 59 | if err != nil { 60 | return err 61 | } 62 | 63 | msg := types.NewMsgFund(amount, cliCtx.GetFromAddress(), recipient) 64 | if err := msg.ValidateBasic(); err != nil { 65 | return err 66 | } 67 | 68 | return authclient.GenerateOrBroadcastMsgs(cliCtx, txBldr, []sdk.Msg{msg}) 69 | }, 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /x/faucet/genesis.go: -------------------------------------------------------------------------------- 1 | package faucet 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/cosmos/ethermint/x/faucet/types" 7 | 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | 10 | abci "github.com/tendermint/tendermint/abci/types" 11 | ) 12 | 13 | // InitGenesis initializes genesis state based on exported genesis 14 | func InitGenesis(ctx sdk.Context, k Keeper, data types.GenesisState) []abci.ValidatorUpdate { 15 | if acc := k.GetFaucetAccount(ctx); acc == nil { 16 | panic(fmt.Sprintf("%s module account has not been set", ModuleName)) 17 | } 18 | 19 | k.SetEnabled(ctx, data.EnableFaucet) 20 | k.SetTimout(ctx, data.Timeout) 21 | k.SetCap(ctx, data.FaucetCap) 22 | k.SetMaxPerRequest(ctx, data.MaxAmountPerRequest) 23 | 24 | return []abci.ValidatorUpdate{} 25 | } 26 | 27 | // ExportGenesis exports genesis state 28 | func ExportGenesis(ctx sdk.Context, k Keeper) types.GenesisState { 29 | return types.GenesisState{ 30 | EnableFaucet: k.IsEnabled(ctx), 31 | Timeout: k.GetTimeout(ctx), 32 | FaucetCap: k.GetCap(ctx), 33 | MaxAmountPerRequest: k.GetMaxPerRequest(ctx), 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /x/faucet/handler.go: -------------------------------------------------------------------------------- 1 | package faucet 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 6 | 7 | "github.com/cosmos/ethermint/x/faucet/types" 8 | ) 9 | 10 | // NewHandler returns a handler for faucet messages. 11 | func NewHandler(keeper Keeper) sdk.Handler { 12 | return func(ctx sdk.Context, msg sdk.Msg) (*sdk.Result, error) { 13 | ctx = ctx.WithEventManager(sdk.NewEventManager()) 14 | switch msg := msg.(type) { 15 | case types.MsgFund: 16 | return handleMsgFund(ctx, keeper, msg) 17 | default: 18 | return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "unrecognized %s message type: %T", ModuleName, msg) 19 | } 20 | } 21 | } 22 | 23 | // handleMsgFund handles a message to fund an address 24 | func handleMsgFund(ctx sdk.Context, keeper Keeper, msg types.MsgFund) (*sdk.Result, error) { 25 | err := keeper.Fund(ctx, msg.Amount, msg.Recipient) 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | ctx.EventManager().EmitEvent( 31 | sdk.NewEvent( 32 | sdk.EventTypeMessage, 33 | sdk.NewAttribute(sdk.AttributeKeyModule, types.ModuleName), 34 | sdk.NewAttribute(sdk.AttributeKeySender, msg.Sender.String()), 35 | sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Amount.String()), 36 | sdk.NewAttribute(types.AttributeRecipient, msg.Recipient.String()), 37 | ), 38 | ) 39 | 40 | return &sdk.Result{ 41 | Events: ctx.EventManager().Events(), 42 | }, nil 43 | } 44 | -------------------------------------------------------------------------------- /x/faucet/keeper/querier.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | abci "github.com/tendermint/tendermint/abci/types" 5 | 6 | "github.com/cosmos/ethermint/x/faucet/types" 7 | 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 11 | ) 12 | 13 | // NewQuerier is the module level router for state queries 14 | func NewQuerier(k Keeper) sdk.Querier { 15 | return func(ctx sdk.Context, path []string, req abci.RequestQuery) (res []byte, err error) { 16 | switch path[0] { 17 | case types.QueryFunded: 18 | return queryFunded(ctx, req, k) 19 | default: 20 | return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query endpoint") 21 | } 22 | } 23 | } 24 | 25 | func queryFunded(ctx sdk.Context, _ abci.RequestQuery, k Keeper) ([]byte, error) { 26 | funded := k.GetFunded(ctx) 27 | 28 | bz, err := codec.MarshalJSONIndent(k.cdc, funded) 29 | if err != nil { 30 | return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) 31 | } 32 | 33 | return bz, nil 34 | } 35 | -------------------------------------------------------------------------------- /x/faucet/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | ) 6 | 7 | // ModuleCdc is the codec for the module 8 | var ModuleCdc = codec.New() 9 | 10 | func init() { 11 | RegisterCodec(ModuleCdc) 12 | } 13 | 14 | // RegisterCodec registers concrete types on the Amino codec 15 | func RegisterCodec(cdc *codec.Codec) { 16 | cdc.RegisterConcrete(MsgFund{}, "ethermint/MsgFund", nil) 17 | } 18 | -------------------------------------------------------------------------------- /x/faucet/types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 5 | ) 6 | 7 | var ( 8 | // ErrWithdrawTooOften withdraw too often 9 | ErrWithdrawTooOften = sdkerrors.Register(ModuleName, 2, "each address can withdraw only once") 10 | ) 11 | -------------------------------------------------------------------------------- /x/faucet/types/events.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // Faucet module events 4 | const ( 5 | AttributeRecipient string = "recipient" 6 | ) 7 | 8 | // Supported endpoints 9 | const ( 10 | QueryFunded = "funded" 11 | ) 12 | -------------------------------------------------------------------------------- /x/faucet/types/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | supplyexported "github.com/cosmos/cosmos-sdk/x/supply/exported" 6 | ) 7 | 8 | // SupplyKeeper is required for mining coin 9 | type SupplyKeeper interface { 10 | MintCoins(ctx sdk.Context, moduleName string, amt sdk.Coins) error 11 | SendCoinsFromModuleToAccount( 12 | ctx sdk.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins, 13 | ) error 14 | GetModuleAccount(ctx sdk.Context, moduleName string) supplyexported.ModuleAccountI 15 | } 16 | 17 | // StakingKeeper is required for getting Denom 18 | type StakingKeeper interface { 19 | BondDenom(ctx sdk.Context) string 20 | } 21 | -------------------------------------------------------------------------------- /x/faucet/types/genesis.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | ) 9 | 10 | // GenesisState defines the application's genesis state. It contains all the 11 | // information required and accounts to initialize the blockchain. 12 | type GenesisState struct { 13 | // enable faucet funding 14 | EnableFaucet bool `json:"enable_faucet" yaml:"enable_faucet"` 15 | // addresses can send requests every duration 16 | Timeout time.Duration `json:"timeout" yaml:"timeout"` 17 | // max total amount to be funded by the faucet 18 | FaucetCap sdk.Int `json:"faucet_cap" yaml:"faucet_cap"` 19 | // max amount per request (i.e sum of all requested coin amounts). 20 | MaxAmountPerRequest sdk.Int `json:"max_amount_per_request" yaml:"max_amount_per_request"` 21 | } 22 | 23 | // Validate performs a basic validation of the GenesisState fields. 24 | func (gs GenesisState) Validate() error { 25 | if gs.Timeout < 0 { 26 | return fmt.Errorf("timeout cannot be negative: %s", gs.Timeout) 27 | } 28 | if gs.FaucetCap.IsNegative() { 29 | return fmt.Errorf("faucet cap cannot be negative: %d", gs.FaucetCap) 30 | } 31 | if gs.MaxAmountPerRequest.IsNegative() { 32 | return fmt.Errorf("max amount per request cannot be negative: %d", gs.MaxAmountPerRequest) 33 | } 34 | return nil 35 | } 36 | 37 | // DefaultGenesisState sets default evm genesis config 38 | func DefaultGenesisState() GenesisState { 39 | return GenesisState{ 40 | EnableFaucet: false, 41 | Timeout: 20 * time.Minute, 42 | FaucetCap: sdk.NewInt(1000000000), 43 | MaxAmountPerRequest: sdk.NewInt(1000), 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /x/faucet/types/key.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // ModuleName is the name of the module 5 | ModuleName = "faucet" 6 | 7 | // StoreKey to be used when creating the KVStore 8 | StoreKey = ModuleName 9 | 10 | // RouterKey uses module name for tx routing 11 | RouterKey = ModuleName 12 | 13 | // QuerierRoute uses module name for query routing 14 | QuerierRoute = ModuleName 15 | ) 16 | 17 | var ( 18 | EnableFaucetKey = []byte{0x01} 19 | TimeoutKey = []byte{0x02} 20 | CapKey = []byte{0x03} 21 | MaxPerRequestKey = []byte{0x04} 22 | FundedKey = []byte{0x05} 23 | ) 24 | -------------------------------------------------------------------------------- /x/faucet/types/msgs.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | sdk "github.com/cosmos/cosmos-sdk/types" 5 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 6 | ) 7 | 8 | // MsgFund funds a recipient address 9 | type MsgFund struct { 10 | Amount sdk.Coins `json:"amount" yaml:"amount"` 11 | Sender sdk.AccAddress `json:"sender" yaml:"sender"` 12 | Recipient sdk.AccAddress `json:"receipient" yaml:"receipient"` 13 | } 14 | 15 | // NewMsgFund is a constructor function for NewMsgFund 16 | func NewMsgFund(amount sdk.Coins, sender, recipient sdk.AccAddress) MsgFund { 17 | return MsgFund{ 18 | Amount: amount, 19 | Sender: sender, 20 | Recipient: recipient, 21 | } 22 | } 23 | 24 | // Route should return the name of the module 25 | func (msg MsgFund) Route() string { return RouterKey } 26 | 27 | // Type should return the action 28 | func (msg MsgFund) Type() string { return "fund" } 29 | 30 | // ValidateBasic runs stateless checks on the message 31 | func (msg MsgFund) ValidateBasic() error { 32 | if !msg.Amount.IsValid() { 33 | return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, msg.Amount.String()) 34 | } 35 | if msg.Sender.Empty() { 36 | return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "sender %s", msg.Sender.String()) 37 | } 38 | if msg.Recipient.Empty() { 39 | return sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "recipient %s", msg.Recipient.String()) 40 | } 41 | return nil 42 | } 43 | 44 | // GetSignBytes encodes the message for signing 45 | func (msg MsgFund) GetSignBytes() []byte { 46 | return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(msg)) 47 | } 48 | 49 | // GetSigners defines whose signature is required 50 | func (msg MsgFund) GetSigners() []sdk.AccAddress { 51 | return []sdk.AccAddress{msg.Sender} 52 | } 53 | --------------------------------------------------------------------------------