├── .codecov.yml ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md ├── config │ └── .codespellignore └── workflows │ ├── build-and-upload.yml │ ├── build-darwin-amd64.yml │ ├── build-darwin-arm64.yml │ ├── build-linux-amd64.yml │ ├── build-linux-arm64.yml │ ├── docker.yml │ ├── lint.yml │ ├── spellcheck.yml │ └── test.yml ├── .gitignore ├── Dockerfile ├── Dockerfile.arm64 ├── LICENSE ├── LICENSE.header ├── Makefile ├── README.md ├── api └── miniwasm │ └── tokenfactory │ └── v1 │ ├── authority_metadata.pulsar.go │ ├── genesis.pulsar.go │ ├── params.pulsar.go │ ├── query.pulsar.go │ ├── query_grpc.pb.go │ ├── tx.pulsar.go │ └── tx_grpc.pb.go ├── app ├── ante │ └── ante.go ├── app.go ├── app_test.go ├── blocksdk.go ├── const.go ├── encoding.go ├── executor_change.go ├── export.go ├── genesis.go ├── ibc-hooks │ ├── README.md │ ├── ack.go │ ├── ack_test.go │ ├── common_test.go │ ├── contracts │ │ ├── .cargo │ │ │ └── config │ │ ├── .editorconfig │ │ ├── .gitignore │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── NOTICE │ │ ├── README.md │ │ ├── artifacts │ │ │ ├── checksums.txt │ │ │ ├── checksums_intermediate.txt │ │ │ ├── counter-aarch64.wasm │ │ │ └── counter.wasm │ │ ├── examples │ │ │ └── schema.rs │ │ ├── rustfmt.toml │ │ ├── schema │ │ │ ├── counter.json │ │ │ └── raw │ │ │ │ ├── execute.json │ │ │ │ ├── instantiate.json │ │ │ │ ├── query.json │ │ │ │ └── response_to_get.json │ │ └── src │ │ │ ├── contract.rs │ │ │ ├── lib.rs │ │ │ ├── msg.rs │ │ │ └── state.rs │ ├── hooks.go │ ├── message.go │ ├── receive.go │ ├── receive_test.go │ ├── timeout.go │ ├── timeout_test.go │ ├── util.go │ └── util_test.go ├── indexer.go ├── keepers │ ├── community_pool.go │ ├── keepers.go │ └── keys.go ├── modules.go ├── test_helpers.go ├── upgrade.go └── wasmtesting │ ├── common_test.go │ ├── contracts │ ├── .cargo │ │ └── config │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── artifacts │ │ └── connect.wasm │ ├── build.rs │ ├── mod.rs │ └── src │ │ ├── connect.rs │ │ ├── execute.rs │ │ ├── lib.rs │ │ ├── mod.rs │ │ ├── msgs.rs │ │ ├── protos │ │ └── connect.proto │ │ ├── query.rs │ │ └── state.rs │ └── stargate_test.go ├── client └── docs │ ├── config.json │ ├── statik │ ├── init.go │ └── statik.go │ └── swagger-ui │ ├── .!5159!favicon-16x16.png │ ├── .!5242!favicon-16x16.png │ ├── .!5261!favicon-16x16.png │ ├── .!5276!favicon-16x16.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── index.html │ ├── oauth2-redirect.html │ ├── swagger-ui-bundle.js │ ├── swagger-ui-bundle.js.map │ ├── swagger-ui-standalone-preset.js │ ├── swagger-ui-standalone-preset.js.map │ ├── swagger-ui.css │ ├── swagger-ui.css.map │ ├── swagger-ui.js │ ├── swagger-ui.js.map │ └── swagger.yaml ├── cmd └── minitiad │ ├── config.go │ ├── init.go │ ├── launch.go │ ├── main.go │ ├── rollback.go │ └── root.go ├── contrib ├── devtools │ ├── Makefile │ └── dockerfile ├── embed.go └── wasm │ ├── cw721_metadata_onchain.wasm │ └── ics721_base.wasm ├── go.mod ├── go.sum ├── proto ├── README.md ├── buf.gen.gogo.yaml ├── buf.gen.pulsar.yaml ├── buf.gen.swagger.yaml ├── buf.lock ├── buf.md ├── buf.yaml └── miniwasm │ └── tokenfactory │ └── v1 │ ├── authority_metadata.proto │ ├── genesis.proto │ ├── params.proto │ ├── query.proto │ └── tx.proto ├── scripts ├── protoc-swagger-gen.sh ├── protocgen-pulsar.sh └── protocgen.sh ├── shared.Dockerfile ├── types └── const.go └── x ├── bank ├── keeper │ ├── keeper.go │ └── msg_server.go └── module.go └── tokenfactory ├── README.md ├── autocli.go ├── client └── cli │ └── tx.go ├── images ├── Burn.png ├── ChangeAdmin.png ├── CreateDenom.png ├── Mint.png └── SetDenomMetadata.png ├── keeper ├── admins.go ├── admins_test.go ├── bankactions.go ├── before_send.go ├── before_send_test.go ├── common_test.go ├── createdenom.go ├── createdenom_test.go ├── creators.go ├── genesis.go ├── genesis_test.go ├── grpc_query.go ├── keeper.go ├── msg_server.go ├── msg_server_test.go ├── params.go ├── testcontracts │ ├── .cargo │ │ └── config │ ├── .gitignore │ ├── Beaker.toml │ ├── Cargo.toml │ └── contracts │ │ ├── .gitkeep │ │ └── infinite-track-beforesend │ │ ├── .cargo │ │ └── config │ │ ├── .editorconfig │ │ ├── .github │ │ └── workflows │ │ │ ├── Basic.yml │ │ │ └── Release.yml │ │ ├── .gitignore │ │ ├── Cargo.toml │ │ ├── LICENSE │ │ ├── NOTICE │ │ ├── README.md │ │ └── src │ │ ├── contract.rs │ │ ├── error.rs │ │ ├── lib.rs │ │ └── msg.rs └── testdata │ ├── infinite_track_beforesend.wasm │ └── no100.wasm ├── module.go └── types ├── authority_metadata.go ├── authority_metadata.pb.go ├── before_send.go ├── codec.go ├── constants.go ├── denoms.go ├── denoms_test.go ├── errors.go ├── events.go ├── expected_keepers.go ├── genesis.go ├── genesis.pb.go ├── genesis_test.go ├── keys.go ├── msgs.go ├── msgs_test.go ├── params.go ├── params.pb.go ├── query.pb.go ├── query.pb.gw.go └── tx.pb.go /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | precision: 2 3 | round: down 4 | range: 70...100 5 | 6 | status: 7 | project: 8 | default: 9 | threshold: 1% # allow this much decrease on project 10 | changes: false 11 | 12 | comment: 13 | layout: "reach, diff, files" 14 | behavior: default # update if exists else create new 15 | 16 | ignore: 17 | - "api/" 18 | - "*.md" 19 | - "*.rst" 20 | - "cmd/" 21 | - "contrib/" 22 | - "docs/" 23 | - "networks/" 24 | - "proto/" 25 | - "scripts/" 26 | - "thrid_party/" 27 | - "docker/" 28 | - "client/" 29 | - "custom/" 30 | - "**/cli" 31 | - "**/rest" 32 | - "**/*.pb.go" 33 | - "**/*.pulsar.go" 34 | - "**/*.pb.gw.go" 35 | - "**/test_utils.go" 36 | - "**/module.go" 37 | - "x/ibc/testing" 38 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | client/docs/* linguist-vendored=true 2 | client/docs/swagger-ui/* linguist-vendored=true 3 | docs/* linguist-documentation=true 4 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @initia-labs/core 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Additional context** 24 | Add any other context about the problem here. 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Closes: #XXXX 4 | 5 | 7 | 8 | --- 9 | 10 | ## Author Checklist 11 | 12 | _All items are required. Please add a note to the item if the item is not applicable and 13 | please add links to any relevant follow up issues._ 14 | 15 | I have... 16 | 17 | - [ ] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title, you can find examples of the prefixes below: 18 | 29 | - [ ] confirmed `!` in the type prefix if API or client breaking change 30 | - [ ] targeted the correct branch 31 | - [ ] provided a link to the relevant issue or specification 32 | - [ ] reviewed "Files changed" and left comments if necessary 33 | - [ ] included the necessary unit and integration tests 34 | - [ ] updated the relevant documentation or specification, including comments for [documenting Go code](https://blog.golang.org/godoc) 35 | - [ ] confirmed all CI checks have passed 36 | 37 | ## Reviewers Checklist 38 | 39 | _All items are required. Please add a note if the item is not applicable and please add 40 | your handle next to the items reviewed if you only reviewed selected items._ 41 | 42 | I have... 43 | 44 | - [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title 45 | - [ ] confirmed all author checklist items have been addressed 46 | - [ ] reviewed state machine logic, API design and naming, documentation is accurate, tests and test coverage 47 | -------------------------------------------------------------------------------- /.github/config/.codespellignore: -------------------------------------------------------------------------------- 1 | cips 2 | pullrequest 3 | keypair 4 | pastTime 5 | hasTables 6 | Nam 7 | EyT 8 | upTo 9 | initia 10 | minitia 11 | expRes 12 | crate 13 | totalIn 14 | totalOut 15 | -------------------------------------------------------------------------------- /.github/workflows/build-and-upload.yml: -------------------------------------------------------------------------------- 1 | name: Build and Upload to releases 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | jobs: 9 | build-linux-amd64: 10 | uses: ./.github/workflows/build-linux-amd64.yml 11 | 12 | build-linux-arm64: 13 | uses: ./.github/workflows/build-linux-arm64.yml 14 | 15 | build-darwin-amd64: 16 | uses: ./.github/workflows/build-darwin-amd64.yml 17 | 18 | build-darwin-arm64: 19 | uses: ./.github/workflows/build-darwin-arm64.yml 20 | 21 | release: 22 | name: Release build artifacts 23 | needs: 24 | - build-linux-amd64 25 | - build-linux-arm64 26 | - build-darwin-amd64 27 | - build-darwin-arm64 28 | runs-on: ubuntu-22.04 29 | permissions: 30 | contents: write 31 | steps: 32 | - name: Download linux amd64 artifact 33 | uses: actions/download-artifact@v4 34 | with: 35 | name: miniwasm-linux-amd64 36 | 37 | - name: Download linux arm64 artifact 38 | uses: actions/download-artifact@v4 39 | with: 40 | name: miniwasm-linux-arm64 41 | 42 | - name: Download darwin amd64 artifact 43 | uses: actions/download-artifact@v4 44 | with: 45 | name: miniwasm-darwin-amd64 46 | 47 | - name: Download darwin arm64 artifact 48 | uses: actions/download-artifact@v4 49 | with: 50 | name: miniwasm-darwin-arm64 51 | 52 | - name: List downloaded files 53 | run: ls -la *.tar.gz 54 | 55 | - name: Release 56 | uses: softprops/action-gh-release@v2 57 | with: 58 | files: | 59 | *.tar.gz 60 | -------------------------------------------------------------------------------- /.github/workflows/build-darwin-amd64.yml: -------------------------------------------------------------------------------- 1 | name: Build Darwin AMD64 2 | 3 | on: 4 | workflow_call: 5 | 6 | jobs: 7 | build: 8 | runs-on: macos-13 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | 15 | - name: Set up Go 16 | uses: actions/setup-go@v4 17 | with: 18 | go-version: "1.23" 19 | 20 | - name: Set environment variables 21 | run: | 22 | MINIWASM_NETWORK_NAME="miniwasm-1" 23 | echo "MINIWASM_NETWORK_NAME=${MINIWASM_NETWORK_NAME}" >> $GITHUB_ENV 24 | echo "GOARCH=amd64" >> $GITHUB_ENV 25 | echo "GOOS=darwin" >> $GITHUB_ENV 26 | if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then 27 | VERSION=${GITHUB_REF#refs/tags/} 28 | else 29 | VERSION="v0.0.0-${GITHUB_SHA::8}" 30 | fi 31 | echo "VERSION=${VERSION}" >> $GITHUB_ENV 32 | echo "ARCH_NAME=x86_64" >> $GITHUB_ENV 33 | WASMVM_VERSION=$(go list -m github.com/CosmWasm/wasmvm/v2 | awk '{print $2}') 34 | echo "WASMVM_VERSION=${WASMVM_VERSION}" >> $GITHUB_ENV 35 | 36 | - name: Ensure dependencies 37 | run: | 38 | go mod tidy 39 | go get github.com/CosmWasm/wasmvm/v2@${WASMVM_VERSION} 40 | 41 | - name: Print environment variables 42 | run: | 43 | echo "GOARCH=${GOARCH}" 44 | echo "GOOS=${GOOS}" 45 | echo "VERSION=${VERSION}" 46 | echo "ARCH_NAME=${ARCH_NAME}" 47 | echo "WASMVM_VERSION=${WASMVM_VERSION}" 48 | echo "MINIWASM_NETWORK_NAME=${MINIWASM_NETWORK_NAME}" 49 | 50 | - name: Build and Package for Darwin AMD64 51 | run: | 52 | make build \ 53 | && cd ./build \ 54 | && cp ~/go/pkg/mod/github.com/\!cosm\!wasm/wasmvm/v2@${WASMVM_VERSION}/internal/api/libwasmvm.dylib ./ \ 55 | && tar -czvf miniwasm_"$VERSION"_Darwin_"$ARCH_NAME".tar.gz minitiad libwasmvm.dylib \ 56 | && mv ./miniwasm_"$VERSION"_Darwin_"$ARCH_NAME".tar.gz $GITHUB_WORKSPACE/ \ 57 | && rm -rf ./libwasmvm.dylib ./minitiad 58 | 59 | - name: Upload artifact 60 | uses: actions/upload-artifact@v4 61 | with: 62 | name: miniwasm-darwin-amd64 63 | path: miniwasm_${{ env.VERSION }}_Darwin_${{ env.ARCH_NAME }}.tar.gz 64 | retention-days: 1 65 | env: 66 | VERSION: ${{ env.VERSION }} 67 | ARCH_NAME: ${{ env.ARCH_NAME }} 68 | -------------------------------------------------------------------------------- /.github/workflows/build-darwin-arm64.yml: -------------------------------------------------------------------------------- 1 | name: Build Darwin ARM64 2 | 3 | on: 4 | workflow_call: 5 | 6 | jobs: 7 | build: 8 | runs-on: macos-latest 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | - name: Set up Go 15 | uses: actions/setup-go@v4 16 | with: 17 | go-version: "1.23" 18 | - name: Set environment variables 19 | run: | 20 | MINIWASM_NETWORK_NAME="miniwasm-1" 21 | echo "MINIWASM_NETWORK_NAME=${MINIWASM_NETWORK_NAME}" >> $GITHUB_ENV 22 | WASMVM_VERSION=$(go list -m github.com/CosmWasm/wasmvm/v2 | awk '{print $2}') 23 | echo "WASMVM_VERSION=${WASMVM_VERSION}" >> $GITHUB_ENV 24 | echo "GOARCH=arm64" >> $GITHUB_ENV 25 | echo "GOOS=darwin" >> $GITHUB_ENV 26 | if [[ "${{ github.ref }}" == "refs/tags/"* ]]; then 27 | VERSION=${GITHUB_REF#refs/tags/} 28 | else 29 | VERSION="v0.0.0-${GITHUB_SHA::8}" 30 | fi 31 | echo "VERSION=${VERSION}" >> $GITHUB_ENV 32 | echo "ARCH_NAME=aarch64" >> $GITHUB_ENV 33 | 34 | - name: Ensure dependencies 35 | run: | 36 | go mod tidy 37 | go get github.com/CosmWasm/wasmvm/v2@${WASMVM_VERSION} 38 | 39 | - name: Print environment variables 40 | run: | 41 | echo "GOARCH=${GOARCH}" 42 | echo "GOOS=${GOOS}" 43 | echo "VERSION=${VERSION}" 44 | echo "ARCH_NAME=${ARCH_NAME}" 45 | echo "MINIWASM_NETWORK_NAME=${MINIWASM_NETWORK_NAME}" 46 | 47 | - name: Build and Package for Darwin ARM64 48 | run: | 49 | make build \ 50 | && cd ./build \ 51 | && cp ~/go/pkg/mod/github.com/\!cosm\!wasm/wasmvm/v2@${WASMVM_VERSION}/internal/api/libwasmvm.dylib ./ \ 52 | && tar -czvf miniwasm_"$VERSION"_Darwin_"$ARCH_NAME".tar.gz minitiad libwasmvm.dylib \ 53 | && mv ./miniwasm_"$VERSION"_Darwin_"$ARCH_NAME".tar.gz $GITHUB_WORKSPACE/ \ 54 | && rm -rf ./libwasmvm.dylib ./minitiad 55 | 56 | - name: Upload artifact 57 | uses: actions/upload-artifact@v4 58 | with: 59 | name: miniwasm-darwin-arm64 60 | path: miniwasm_${{ env.VERSION }}_Darwin_${{ env.ARCH_NAME }}.tar.gz 61 | retention-days: 1 62 | env: 63 | VERSION: ${{ env.VERSION }} 64 | ARCH_NAME: ${{ env.ARCH_NAME }} 65 | -------------------------------------------------------------------------------- /.github/workflows/build-linux-amd64.yml: -------------------------------------------------------------------------------- 1 | name: Build Linux AMD64 2 | 3 | on: workflow_call 4 | 5 | jobs: 6 | build: 7 | runs-on: ubuntu-22.04 8 | steps: 9 | - name: Checkout code 10 | uses: actions/checkout@v4 11 | 12 | - name: Set up Go 13 | uses: actions/setup-go@v4 14 | with: 15 | go-version: "1.23" 16 | 17 | - name: Set environment variables 18 | run: | 19 | echo "GOARCH=amd64" >> $GITHUB_ENV 20 | echo "GOOS=linux" >> $GITHUB_ENV 21 | if [[ "${GITHUB_REF}" == refs/tags/* ]]; then 22 | VERSION=${GITHUB_REF#refs/tags/} 23 | else 24 | VERSION="v0.0.0-${GITHUB_SHA::8}" 25 | fi 26 | echo "VERSION=${VERSION}" >> $GITHUB_ENV 27 | echo "ARCH_NAME=x86_64" >> $GITHUB_ENV 28 | 29 | - name: Print environment variables 30 | run: | 31 | echo "GOARCH=${GOARCH}" 32 | echo "GOOS=${GOOS}" 33 | echo "VERSION=${VERSION}" 34 | 35 | - name: Build for Linux AMD64 36 | run: | 37 | export GOARCH=${GOARCH} 38 | export GOOS=${GOOS} 39 | make build-linux-with-shared-library \ 40 | && cd ./build \ 41 | && mv libwasmvm.so libwasmvm.${ARCH_NAME}.so \ 42 | && tar -czvf miniwasm_${VERSION}_Linux_${ARCH_NAME}.tar.gz minitiad libwasmvm.${ARCH_NAME}.so \ 43 | && mv miniwasm_${VERSION}_Linux_${ARCH_NAME}.tar.gz $GITHUB_WORKSPACE/ \ 44 | && rm -rf ./minitiad ./libwasmvm.${ARCH_NAME}.so 45 | 46 | - name: Upload artifact 47 | uses: actions/upload-artifact@v4 48 | with: 49 | name: miniwasm-linux-amd64 50 | path: miniwasm_${{ env.VERSION }}_Linux_${{ env.ARCH_NAME }}.tar.gz 51 | retention-days: 1 52 | env: 53 | VERSION: ${{ env.VERSION }} 54 | ARCH_NAME: ${{ env.ARCH_NAME }} 55 | -------------------------------------------------------------------------------- /.github/workflows/build-linux-arm64.yml: -------------------------------------------------------------------------------- 1 | name: Build Linux ARM64 2 | 3 | on: 4 | workflow_call: 5 | 6 | jobs: 7 | build: 8 | runs-on: ubuntu-22.04 9 | steps: 10 | - name: Checkout code 11 | uses: actions/checkout@v4 12 | with: 13 | fetch-depth: 0 14 | 15 | - name: Set up QEMU 16 | uses: docker/setup-qemu-action@v3 17 | with: 18 | platforms: arm64 19 | 20 | - name: Set up Docker Buildx 21 | uses: docker/setup-buildx-action@v3 22 | 23 | - name: Set environment variables 24 | run: | 25 | echo "GOARCH=arm64" >> $GITHUB_ENV 26 | echo "GOOS=linux" >> $GITHUB_ENV 27 | if [[ "${GITHUB_REF}" == refs/tags/* ]]; then 28 | VERSION=${GITHUB_REF#refs/tags/} 29 | else 30 | VERSION="v0.0.0-${GITHUB_SHA::8}" 31 | fi 32 | echo "VERSION=${VERSION}" >> $GITHUB_ENV 33 | echo "ARCH_NAME=aarch64" >> $GITHUB_ENV 34 | 35 | - name: Build for ARM64 36 | env: 37 | DOCKER_BUILDKIT: 1 38 | run: | 39 | # Activate BuildKit and create a new builder 40 | docker buildx create --use --name arm64-builder --platform linux/arm64 41 | docker buildx inspect --bootstrap 42 | 43 | # Building images for ARM64 44 | docker buildx build --platform linux/arm64 --load --tag minitia/minitiad-shared:arm64 . -f Dockerfile.arm64 45 | 46 | # Extract build output using ARM64 images 47 | mkdir -p ./build 48 | docker create --name temp minitia/minitiad-shared:arm64 49 | docker cp temp:/usr/local/bin/minitiad ./build/ 50 | docker cp temp:/lib/libwasmvm.so ./build/ 51 | docker rm temp 52 | 53 | # Packaging of results 54 | cd ./build \ 55 | && mv libwasmvm.so libwasmvm.${ARCH_NAME}.so \ 56 | && tar -czvf miniwasm_${VERSION}_Linux_${ARCH_NAME}.tar.gz minitiad libwasmvm.${ARCH_NAME}.so \ 57 | && mv miniwasm_${VERSION}_Linux_${ARCH_NAME}.tar.gz $GITHUB_WORKSPACE/ \ 58 | && rm -rf ./minitiad ./libwasmvm.${ARCH_NAME}.so 59 | 60 | # Check build results 61 | cd .. 62 | ls -l 63 | file miniwasm_${VERSION}_Linux_${ARCH_NAME}.tar.gz 64 | 65 | # Remove builder 66 | docker buildx rm arm64-builder 67 | 68 | - name: List files 69 | run: ls -l 70 | 71 | - name: Upload artifact 72 | uses: actions/upload-artifact@v4 73 | with: 74 | name: miniwasm-linux-arm64 75 | path: miniwasm_${{ env.VERSION }}_Linux_${{ env.ARCH_NAME }}.tar.gz 76 | retention-days: 1 77 | env: 78 | VERSION: ${{ env.VERSION }} 79 | ARCH_NAME: ${{ env.ARCH_NAME }} 80 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: Docker 2 | 3 | on: 4 | workflow_dispatch: 5 | push: 6 | branches: 7 | - "main" 8 | tags: 9 | - "v*" 10 | paths: 11 | - "**.go" 12 | - "go.mod" 13 | - "go.sum" 14 | 15 | env: 16 | REGISTRY: ghcr.io 17 | IMAGE_NAME: miniwasm 18 | 19 | jobs: 20 | build-image: 21 | name: Build Docker image 22 | runs-on: ubuntu-22.04 23 | permissions: 24 | contents: read 25 | packages: write 26 | 27 | steps: 28 | - name: Checkout 29 | uses: actions/checkout@v4 30 | 31 | - name: Log in to the Container registry 32 | uses: docker/login-action@v3 33 | with: 34 | registry: ${{ env.REGISTRY }} 35 | username: ${{ github.actor }} 36 | password: ${{ secrets.GITHUB_TOKEN }} 37 | 38 | - name: Extract metadata (tags, labels) for Docker 39 | id: meta 40 | uses: docker/metadata-action@v5 41 | with: 42 | images: ${{ env.REGISTRY }}/${{ github.repository_owner }}/${{ env.IMAGE_NAME }} 43 | 44 | - name: Build and push 45 | uses: docker/build-push-action@v6 46 | with: 47 | file: Dockerfile 48 | push: ${{ startsWith(github.ref, 'refs/tags') }} 49 | tags: ${{ steps.meta.outputs.tags }} 50 | labels: ${{ steps.meta.outputs.labels }} 51 | no-cache: true 52 | build-args: | 53 | VERSION=${{ github.ref_name }} 54 | COMMIT=${{ github.sha }} 55 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: Lint 2 | # This workflow is run on every pull request and push to master 3 | # The `golangci` will pass without running if no *.{go, mod, sum} files have been changed. 4 | on: 5 | pull_request: 6 | paths: 7 | - "**.go" 8 | - "go.mod" 9 | - "go.sum" 10 | push: 11 | branches: 12 | - main 13 | - "release/*" 14 | paths: 15 | - "**.go" 16 | - "go.mod" 17 | - "go.sum" 18 | 19 | concurrency: 20 | group: ${{ github.workflow }}-${{ github.ref }} 21 | cancel-in-progress: true 22 | 23 | jobs: 24 | golangci: 25 | env: 26 | GOLANGCI_LINT_VERSION: v1.61.0 27 | name: golangci-lint 28 | runs-on: ubuntu-22.04 29 | steps: 30 | - uses: actions/checkout@v4 31 | - uses: actions/setup-go@v5 32 | with: 33 | go-version: 1.23 34 | check-latest: true 35 | - uses: technote-space/get-diff-action@v6.1.2 36 | id: git_diff 37 | with: 38 | PATTERNS: | 39 | **/**.go 40 | go.mod 41 | go.sum 42 | # install golangci-lint 43 | - run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@${{ env.GOLANGCI_LINT_VERSION }} 44 | - name: run go linters (long) 45 | if: env.GIT_DIFF 46 | id: lint_long 47 | run: | 48 | make lint 49 | env: 50 | GIT_DIFF: ${{ env.GIT_DIFF }} 51 | - uses: technote-space/get-diff-action@v6.1.2 52 | if: steps.lint_long.outcome == 'skipped' 53 | with: 54 | PATTERNS: | 55 | **/**.go 56 | go.mod 57 | go.sum 58 | - name: run go linters (short) 59 | if: steps.lint_long.outcome == 'skipped' && env.GIT_DIFF 60 | run: | 61 | make lint 62 | env: 63 | GIT_DIFF: ${{ env.GIT_DIFF }} 64 | LINT_DIFF: 1 65 | # Use --check or --exit-code when available (Go 1.19?) 66 | # https://github.com/golang/go/issues/27005 67 | tidy: 68 | runs-on: ubuntu-22.04 69 | name: tidy 70 | steps: 71 | - uses: actions/checkout@v4 72 | - name: Setup go 73 | uses: actions/setup-go@v5 74 | with: 75 | go-version: 1.23 76 | check-latest: true 77 | - run: | 78 | go mod tidy 79 | CHANGES_IN_REPO=$(git status --porcelain) 80 | if [[ -n "$CHANGES_IN_REPO" ]]; then 81 | echo "Repository is dirty. Showing 'git status' and 'git --no-pager diff' for debugging now:" 82 | git status && git --no-pager diff 83 | exit 1 84 | fi 85 | -------------------------------------------------------------------------------- /.github/workflows/spellcheck.yml: -------------------------------------------------------------------------------- 1 | name: Spell Check 2 | 3 | on: 4 | pull_request: 5 | 6 | jobs: 7 | spellcheck: 8 | runs-on: ubuntu-22.04 9 | 10 | steps: 11 | - uses: actions/checkout@v4 12 | - name: Run codespell 13 | continue-on-error: true 14 | run: | 15 | sudo apt-get install codespell -y 16 | codespell -w --skip="*.pulsar.go,*.pb.go,*.pb.gw.go,*.cosmos_orm.go,*.json,*.git,*.js,crypto/keys,fuzz,*.h,proto/tendermint,*.bin,go.sum,go.work.sum,go.mod,statik.go,*.map,swagger.yaml" --ignore-words=.github/config/.codespellignore 17 | - uses: peter-evans/create-pull-request@v7.0.5 18 | if: github.event_name != 'pull_request' 19 | with: 20 | token: ${{ secrets.GITHUB_TOKEN }} 21 | commit-message: "chore: fix typos" 22 | title: "chore: fix typos" 23 | branch: "chore/fix-typos" 24 | delete-branch: true 25 | body: | 26 | This PR fixes typos in the codebase. 27 | Please review it, and merge if everything is fine. 28 | If there are proto changes, run `make proto-gen` and commit the changes. 29 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Build & Test 2 | on: 3 | pull_request: 4 | paths: 5 | - "**.go" 6 | - "**.mv" 7 | - "**.move" 8 | push: 9 | branches: 10 | - main 11 | - "release/*" 12 | paths: 13 | - "**.go" 14 | - "**.mv" 15 | - "**.move" 16 | 17 | concurrency: 18 | group: ${{ github.workflow }}-${{ github.ref }} 19 | cancel-in-progress: true 20 | 21 | jobs: 22 | test-coverage-upload: 23 | name: Run test and upload codecov 24 | env: 25 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 26 | runs-on: ubuntu-22.04 27 | steps: 28 | - uses: actions/setup-go@v3 29 | with: 30 | go-version: 1.23 31 | - name: Install openssl 32 | run: sudo apt-get install libssl-dev 33 | - uses: actions/checkout@v3 34 | - uses: technote-space/get-diff-action@v4 35 | with: 36 | PATTERNS: | 37 | **/**.go 38 | go.mod 39 | go.sum 40 | - name: build 41 | run: | 42 | make build 43 | - name: test & coverage report creation 44 | run: | 45 | go test ./... -mod=readonly -timeout 12m -race -coverprofile=coverage.txt -covermode=atomic -tags='ledger test_ledger_mock' 46 | if: env.GIT_DIFF 47 | env: 48 | GIT_DIFF: ${{ env.GIT_DIFF }} 49 | # - name: filter out DONTCOVER 50 | # run: | 51 | # excludelist="$(find ./ -type f -name '*.go' | xargs grep -l 'DONTCOVER')" 52 | # excludelist+=" $(find ./ -type f -name '*.pb.go')" 53 | # for filename in ${excludelist}; do 54 | # filename=$(echo $filename | sed 's/^./github.com\/initia-labs\/initia/g') 55 | # echo "Excluding ${filename} from coverage report..." 56 | # sed -i.bak "/$(echo $filename | sed 's/\//\\\//g')/d" coverage.txt 57 | # done 58 | # if: env.GIT_DIFF 59 | - uses: codecov/codecov-action@v3 60 | with: 61 | token: ${{ secrets.CODECOV_TOKEN }} 62 | files: ./coverage.txt 63 | fail_ci_if_error: true 64 | if: env.GIT_DIFF 65 | env: 66 | GIT_DIFF: ${{ env.GIT_DIFF }} 67 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS 2 | .DS_Store 3 | *.swp 4 | *.swo 5 | *.swl 6 | *.swm 7 | *.swn 8 | .vscode 9 | .idea 10 | *.code-workspace 11 | 12 | # Build 13 | bin 14 | build 15 | vendor 16 | .vendor-new 17 | build 18 | dist 19 | tools/bin/* 20 | examples/build/* 21 | docs/_build 22 | tools-stamp 23 | 24 | # Data - ideally these don't exist 25 | examples/basecoin/app/data 26 | baseapp/data/* 27 | client/lcd/keys/* 28 | remote/ansible/testnets 29 | mytestnet 30 | 31 | # Testing 32 | coverage.txt 33 | profile.out 34 | sim_log_file 35 | 36 | # Vagrant 37 | .vagrant/ 38 | *.box 39 | *.log 40 | vagrant 41 | 42 | # IDE 43 | .idea/ 44 | *.iml 45 | 46 | # Graphviz 47 | dependency-graph.png 48 | 49 | # Latex 50 | *.aux 51 | *.out 52 | *.synctex.gz 53 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.23-alpine AS go-builder 2 | #ARG arch=x86_64 3 | 4 | ARG VERSION 5 | ARG COMMIT 6 | 7 | # See https://github.com/CosmWasm/wasmvm/releases 8 | ENV LIBWASMVM_VERSION=v2.1.4 9 | ENV MIMALLOC_VERSION=v2.2.2 10 | 11 | # this comes from standard alpine nightly file 12 | # https://github.com/rust-lang/docker-rust-nightly/blob/master/alpine3.12/Dockerfile 13 | # with some changes to support our toolchain, etc 14 | RUN set -eux; apk add --no-cache ca-certificates build-base; 15 | 16 | RUN apk add git cmake 17 | # NOTE: add these to run with LEDGER_ENABLED=true 18 | # RUN apk add libusb-dev linux-headers 19 | 20 | WORKDIR /code 21 | COPY . /code/ 22 | 23 | # Install mimalloc 24 | RUN git clone -b ${MIMALLOC_VERSION} --depth 1 https://github.com/microsoft/mimalloc; cd mimalloc; mkdir build; cd build; cmake ..; make -j$(nproc); make install 25 | ENV MIMALLOC_RESERVE_HUGE_OS_PAGES=4 26 | 27 | # See https://github.com/\!cosm\!wasm/wasmvm/releases 28 | ADD https://github.com/CosmWasm/wasmvm/releases/download/${LIBWASMVM_VERSION}/libwasmvm_muslc.aarch64.a /lib/libwasmvm_muslc.aarch64.a 29 | ADD https://github.com/CosmWasm/wasmvm/releases/download/${LIBWASMVM_VERSION}/libwasmvm_muslc.x86_64.a /lib/libwasmvm_muslc.x86_64.a 30 | 31 | # Highly recommend to verify the version hash 32 | # RUN sha256sum /lib/libwasmvm_muslc.aarch64.a | grep a5e63292ec67f5bdefab51b42c3fbc3fa307c6aefeb6b409d971f1df909c3927 33 | # RUN sha256sum /lib/libwasmvm_muslc.x86_64.a | grep 762307147bf8f550bd5324b7f7c4f17ee20805ff93dc06cc073ffbd909438320 34 | # Copy the library you want to the final location that will be found by the linker flag `-linitia_muslc` 35 | 36 | RUN cp /lib/libwasmvm_muslc.`uname -m`.a /lib/libwasmvm_muslc.a 37 | 38 | # force it to use static lib (from above) not standard libwasmvm.so file 39 | RUN VERSION=${VERSION} COMMIT=${COMMIT} LEDGER_ENABLED=false BUILD_TAGS=muslc LDFLAGS="-linkmode=external -extldflags \"-L/code/mimalloc/build -lmimalloc -Wl,-z,muldefs -static\"" make build 40 | 41 | FROM alpine:3.18 42 | 43 | RUN addgroup minitia \ 44 | && adduser -G minitia -D -h /minitia minitia 45 | 46 | WORKDIR /minitia 47 | 48 | COPY --from=go-builder /code/build/minitiad /usr/local/bin/minitiad 49 | 50 | # for new-metric setup 51 | COPY --from=go-builder /code/contrib /minitia/contrib 52 | 53 | USER minitia 54 | 55 | # rest server 56 | EXPOSE 1317 57 | # grpc 58 | EXPOSE 9090 59 | # tendermint p2p 60 | EXPOSE 26656 61 | # tendermint rpc 62 | EXPOSE 26657 63 | 64 | CMD ["/usr/local/bin/minitiad", "version"] 65 | -------------------------------------------------------------------------------- /Dockerfile.arm64: -------------------------------------------------------------------------------- 1 | FROM arm64v8/golang:1.23-bullseye AS go-builder 2 | 3 | # Install minimum necessary dependencies, build Cosmos SDK, remove packages 4 | RUN apt update 5 | RUN apt install -y curl git build-essential 6 | # debug: for live editing in the image 7 | RUN apt install -y vim 8 | 9 | WORKDIR /code 10 | COPY . /code/ 11 | 12 | RUN VERSION=${VERSION} LEDGER_ENABLED=false make build 13 | 14 | RUN cp /go/pkg/mod/github.com/\!cosm\!wasm/wasmvm/v2@v*/internal/api/libwasmvm.`uname -m`.so /lib/libwasmvm.so 15 | 16 | FROM arm64v8/ubuntu:20.04 17 | 18 | WORKDIR /root 19 | 20 | COPY --from=go-builder /code/build/minitiad /usr/local/bin/minitiad 21 | COPY --from=go-builder /lib/libwasmvm.so /lib/libwasmvm.so 22 | 23 | # for new-metric setup 24 | COPY --from=go-builder /code/contrib /root/contrib 25 | 26 | # rest server 27 | EXPOSE 1317 28 | # grpc 29 | EXPOSE 9090 30 | # tendermint p2p 31 | EXPOSE 26656 32 | # tendermint rpc 33 | EXPOSE 26657 34 | 35 | CMD ["/usr/local/bin/minitiad", "version"] 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Business Source License 1.1 2 | 3 | License text copyright © 2023 MariaDB plc, All Rights Reserved. 4 | “Business Source License” is a trademark of MariaDB plc. 5 | 6 | ----------------------------------------------------------------------------- 7 | 8 | Parameters 9 | 10 | Licensor: Initia Foundation 11 | 12 | Licensed Work: Miniwasm 13 | The Licensed Work is (c) 2023 Initia Foundation 14 | 15 | Additional Use Grant: None 16 | 17 | Change Date: 2028-01-01 18 | 19 | Change License: Apache License, Version 2.0 20 | 21 | For information about alternative licensing arrangements for the Software, 22 | please visit: 23 | 24 | ----------------------------------------------------------------------------- 25 | 26 | Terms 27 | 28 | The Licensor hereby grants you the right to copy, modify, create derivative 29 | works, redistribute, and make non-production use of the Licensed Work. The 30 | Licensor may make an Additional Use Grant, above, permitting limited 31 | production use. 32 | 33 | Effective on the Change Date, or the fourth anniversary of the first publicly 34 | available distribution of a specific version of the Licensed Work under this 35 | License, whichever comes first, the Licensor hereby grants you rights under 36 | the terms of the Change License, and the rights granted in the paragraph 37 | above terminate. 38 | 39 | If your use of the Licensed Work does not comply with the requirements 40 | currently in effect as described in this License, you must purchase a 41 | commercial license from the Licensor, its affiliated entities, or authorized 42 | resellers, or you must refrain from using the Licensed Work. 43 | 44 | All copies of the original and modified Licensed Work, and derivative works 45 | of the Licensed Work, are subject to this License. This License applies 46 | separately for each version of the Licensed Work and the Change Date may vary 47 | for each version of the Licensed Work released by Licensor. 48 | 49 | You must conspicuously display this License on each original or modified copy 50 | of the Licensed Work. If you receive the Licensed Work in original or 51 | modified form from a third party, the terms and conditions set forth in this 52 | License apply to your use of that work. 53 | 54 | Any use of the Licensed Work in violation of this License will automatically 55 | terminate your rights under this License for the current and all other 56 | versions of the Licensed Work. 57 | 58 | This License does not grant you any right in any trademark or logo of 59 | Licensor or its affiliates (provided that you may use a trademark or logo of 60 | Licensor as expressly required by this License). 61 | 62 | TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON 63 | AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, 64 | EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF 65 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND 66 | TITLE. 67 | 68 | MariaDB hereby grants you permission to use this License’s text to license 69 | your works, and to refer to it using the trademark “Business Source License”, 70 | as long as you comply with the Covenants of Licensor below. 71 | 72 | ----------------------------------------------------------------------------- 73 | 74 | Covenants of Licensor 75 | 76 | In consideration of the right to use this License’s text and the “Business 77 | Source License” name and trademark, Licensor covenants to MariaDB, and to all 78 | other recipients of the licensed work to be provided by Licensor: 79 | 80 | 1. To specify as the Change License the GPL Version 2.0 or any later version, 81 | or a license that is compatible with GPL Version 2.0 or a later version, 82 | where “compatible” means that software provided under the Change License can 83 | be included in a program with software provided under GPL Version 2.0 or a 84 | later version. Licensor may specify additional Change Licenses without 85 | limitation. 86 | 87 | 2. To either: (a) specify an additional grant of rights to use that does not 88 | impose any additional restriction on the right granted in this License, as 89 | the Additional Use Grant; or (b) insert the text “None”. 90 | 91 | 3. To specify a Change Date. 92 | 93 | 4. Not to modify this License in any other way. 94 | 95 | ----------------------------------------------------------------------------- 96 | 97 | Notice 98 | 99 | The Business Source License (this document, or the “License”) is not an Open 100 | Source license. However, the Licensed Work will eventually be made available 101 | under an Open Source License, as stated in this License. 102 | 103 | For more information on the use of the Business Source License for MariaDB 104 | products, please visit the MariaDB Business Source License FAQ at 105 | . 106 | 107 | For more information on the use of the Business Source License generally, 108 | please visit the Adopting and Developing Business Source License FAQ at 109 | . 110 | -------------------------------------------------------------------------------- /LICENSE.header: -------------------------------------------------------------------------------- 1 | SPDX-License-Identifier: BUSL-1.1 2 | 3 | Copyright (C) 2023, Initia Foundation. All rights reserved. 4 | Use of this software is governed by the Business Source License included 5 | in the LICENSE file of this repository and at www.mariadb.com/bsl11. 6 | 7 | ANY USE OF THE LICENSED WORK IN VIOLATION OF THIS LICENSE WILL AUTOMATICALLY 8 | TERMINATE YOUR RIGHTS UNDER THIS LICENSE FOR THE CURRENT AND ALL OTHER 9 | VERSIONS OF THE LICENSED WORK. 10 | 11 | THIS LICENSE DOES NOT GRANT YOU ANY RIGHT IN ANY TRADEMARK OR LOGO OF 12 | LICENSOR OR ITS AFFILIATES (PROVIDED THAT YOU MAY USE A TRADEMARK OR LOGO OF 13 | LICENSOR AS EXPRESSLY REQUIRED BY THIS LICENSE). 14 | 15 | TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE LICENSED WORK IS PROVIDED ON 16 | AN “AS IS” BASIS. LICENSOR HEREBY DISCLAIMS ALL WARRANTIES AND CONDITIONS, 17 | EXPRESS OR IMPLIED, INCLUDING (WITHOUT LIMITATION) WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, AND 19 | TITLE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MiniWasm 2 | 3 | MiniWasm is an optimistic rollup consumer chain powered by WasmVM, designed to simplify the process of bootstrapping an L2 network. The main advantage of using MiniWasm is that the users can leverage the OPinit stack for enhanced security and utilize all the Initia ecosystem tooling from day one, without the need to prepare a validator group or build the users' own ecosystem tools. 4 | 5 | ## Prerequisites 6 | 7 | - Go v1.23.3+ 8 | 9 | ## Getting Started 10 | 11 | To get started with L2, please visit the [documentation](https://initia.gitbook.io/initia-docs-v2/). 12 | 13 | ## Features 14 | 15 | - Powered by WasmVM, MiniWasm acts as an optimistic rollup consumer chain. 16 | - Simplifies the network bootstrapping process, making it faster and more efficient. 17 | - Eliminates the need for setting up a validator group or creating custom ecosystem tools. 18 | - Integrates seamlessly with the OPinit stack, enhancing security. 19 | - Provides immediate access to the full suite of Initia ecosystem tools right from the start. 20 | 21 | ## Contributing 22 | 23 | Contributions are welcome! If you have any ideas, suggestions, or bug reports, please open an issue or submit a pull request. 24 | 25 | ## License 26 | 27 | This project is licensed under the [BSL License](LICENSE). 28 | -------------------------------------------------------------------------------- /app/app_test.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "os" 7 | "testing" 8 | 9 | "github.com/golang/mock/gomock" 10 | "github.com/stretchr/testify/require" 11 | 12 | "cosmossdk.io/log" 13 | cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" 14 | dbm "github.com/cosmos/cosmos-db" 15 | 16 | "github.com/cosmos/ibc-go/modules/capability" 17 | capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" 18 | 19 | "github.com/cosmos/cosmos-sdk/testutil/mock" 20 | sdk "github.com/cosmos/cosmos-sdk/types" 21 | "github.com/cosmos/cosmos-sdk/types/module" 22 | "github.com/cosmos/cosmos-sdk/x/auth" 23 | authzmodule "github.com/cosmos/cosmos-sdk/x/authz/module" 24 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 25 | "github.com/cosmos/cosmos-sdk/x/consensus" 26 | groupmodule "github.com/cosmos/cosmos-sdk/x/group/module" 27 | 28 | "cosmossdk.io/x/upgrade" 29 | 30 | "github.com/cosmos/cosmos-sdk/x/bank" 31 | 32 | ica "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts" 33 | "github.com/cosmos/ibc-go/v8/modules/apps/transfer" 34 | ibc "github.com/cosmos/ibc-go/v8/modules/core" 35 | 36 | opchild "github.com/initia-labs/OPinit/x/opchild" 37 | 38 | "github.com/CosmWasm/wasmd/x/wasm" 39 | wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" 40 | ) 41 | 42 | func TestSimAppExportAndBlockedAddrs(t *testing.T) { 43 | app := SetupWithGenesisAccounts(t.TempDir(), nil, nil) 44 | 45 | // BlockedAddresses returns a map of addresses in app v1 and a map of modules name in app v2. 46 | for acc := range app.BlockedModuleAccountAddrs(app.ModuleAccountAddrs()) { 47 | var addr sdk.AccAddress 48 | if modAddr, err := sdk.AccAddressFromBech32(acc); err == nil { 49 | addr = modAddr 50 | } else { 51 | addr = app.AccountKeeper.GetModuleAddress(acc) 52 | } 53 | 54 | require.True( 55 | t, 56 | app.BankKeeper.BlockedAddr(addr), 57 | fmt.Sprintf("ensure that blocked addresses are properly set in bank keeper: %s should be blocked", acc), 58 | ) 59 | } 60 | } 61 | 62 | func TestGetMaccPerms(t *testing.T) { 63 | dup := GetMaccPerms() 64 | require.Equal(t, maccPerms, dup, "duplicated module account permissions differed from actual module account permissions") 65 | } 66 | 67 | func TestInitGenesisOnMigration(t *testing.T) { 68 | db := dbm.NewMemDB() 69 | logger := log.NewLogger(os.Stdout) 70 | app := NewMinitiaApp( 71 | logger, db, getOrCreateMemDB(nil), nil, true, []wasmkeeper.Option{}, EmptyAppOptions{homeDir: t.TempDir()}) 72 | ctx := app.NewContextLegacy(true, cmtproto.Header{Height: app.LastBlockHeight()}) 73 | 74 | // Create a mock module. This module will serve as the new module we're 75 | // adding during a migration. 76 | mockCtrl := gomock.NewController(t) 77 | t.Cleanup(mockCtrl.Finish) 78 | mockModule := mock.NewMockAppModuleWithAllExtensions(mockCtrl) 79 | mockDefaultGenesis := json.RawMessage(`{"key": "value"}`) 80 | mockModule.EXPECT().DefaultGenesis(gomock.Eq(app.appCodec)).Times(1).Return(mockDefaultGenesis) 81 | mockModule.EXPECT().InitGenesis(gomock.Eq(ctx), gomock.Eq(app.appCodec), gomock.Eq(mockDefaultGenesis)).Times(1) 82 | mockModule.EXPECT().ConsensusVersion().Times(1).Return(uint64(0)) 83 | 84 | app.ModuleManager.Modules["mock"] = mockModule 85 | app.ModuleManager.OrderMigrations = []string{"mock"} 86 | 87 | // Run migrations only for "mock" module. We exclude it from 88 | // the VersionMap to simulate upgrading with a new module. 89 | _, err := app.ModuleManager.RunMigrations(ctx, app.configurator, 90 | module.VersionMap{ 91 | "bank": bank.AppModule{}.ConsensusVersion(), 92 | "auth": auth.AppModule{}.ConsensusVersion(), 93 | "authz": authzmodule.AppModule{}.ConsensusVersion(), 94 | "upgrade": upgrade.AppModule{}.ConsensusVersion(), 95 | "capability": capability.AppModule{}.ConsensusVersion(), 96 | "group": groupmodule.AppModule{}.ConsensusVersion(), 97 | "consensus": consensus.AppModule{}.ConsensusVersion(), 98 | "ibc": ibc.AppModule{}.ConsensusVersion(), 99 | "transfer": transfer.AppModule{}.ConsensusVersion(), 100 | "interchainaccounts": ica.AppModule{}.ConsensusVersion(), 101 | "wasm": wasm.AppModule{}.ConsensusVersion(), 102 | "opchild": opchild.AppModule{}.ConsensusVersion(), 103 | }, 104 | ) 105 | require.NoError(t, err) 106 | } 107 | 108 | func TestUpgradeStateOnGenesis(t *testing.T) { 109 | app := SetupWithGenesisAccounts(t.TempDir(), nil, nil) 110 | 111 | // make sure the upgrade keeper has version map in state 112 | ctx := app.NewContext(true) 113 | vm, err := app.UpgradeKeeper.GetModuleVersionMap(ctx) 114 | require.NoError(t, err) 115 | 116 | for v, i := range app.ModuleManager.Modules { 117 | if i, ok := i.(module.HasConsensusVersion); ok { 118 | require.Equal(t, vm[v], i.ConsensusVersion()) 119 | } 120 | } 121 | } 122 | 123 | func TestGetKey(t *testing.T) { 124 | db := dbm.NewMemDB() 125 | app := NewMinitiaApp( 126 | log.NewLogger(os.Stdout), 127 | db, dbm.NewMemDB(), nil, true, []wasmkeeper.Option{}, EmptyAppOptions{homeDir: t.TempDir()}) 128 | 129 | require.NotEmpty(t, app.GetKey(banktypes.StoreKey)) 130 | require.NotEmpty(t, app.GetMemKey(capabilitytypes.MemStoreKey)) 131 | } 132 | -------------------------------------------------------------------------------- /app/blocksdk.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "cosmossdk.io/math" 5 | storetypes "cosmossdk.io/store/types" 6 | 7 | "github.com/cosmos/cosmos-sdk/runtime" 8 | sdk "github.com/cosmos/cosmos-sdk/types" 9 | "github.com/cosmos/cosmos-sdk/types/mempool" 10 | cosmosante "github.com/cosmos/cosmos-sdk/x/auth/ante" 11 | 12 | opchildlanes "github.com/initia-labs/OPinit/x/opchild/lanes" 13 | initialanes "github.com/initia-labs/initia/app/lanes" 14 | 15 | blockabci "github.com/skip-mev/block-sdk/v2/abci" 16 | blockchecktx "github.com/skip-mev/block-sdk/v2/abci/checktx" 17 | signer_extraction "github.com/skip-mev/block-sdk/v2/adapters/signer_extraction_adapter" 18 | "github.com/skip-mev/block-sdk/v2/block" 19 | blockbase "github.com/skip-mev/block-sdk/v2/block/base" 20 | mevlane "github.com/skip-mev/block-sdk/v2/lanes/mev" 21 | 22 | appante "github.com/initia-labs/miniwasm/app/ante" 23 | 24 | wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" 25 | ) 26 | 27 | func setupBlockSDK( 28 | app *MinitiaApp, 29 | mempoolMaxTxs int, 30 | wasmConfig wasmtypes.WasmConfig, 31 | txCounterStoreKey *storetypes.KVStoreKey, 32 | ) ( 33 | mempool.Mempool, 34 | sdk.AnteHandler, 35 | blockchecktx.CheckTx, 36 | sdk.PrepareProposalHandler, 37 | sdk.ProcessProposalHandler, 38 | error, 39 | ) { 40 | 41 | // initialize and set the InitiaApp mempool. The current mempool will be the 42 | // x/auction module's mempool which will extract the top bid from the current block's auction 43 | // and insert the txs at the top of the block spots. 44 | signerExtractor := signer_extraction.NewDefaultAdapter() 45 | 46 | systemLane := initialanes.NewSystemLane(blockbase.LaneConfig{ 47 | Logger: app.Logger(), 48 | TxEncoder: app.txConfig.TxEncoder(), 49 | TxDecoder: app.txConfig.TxDecoder(), 50 | MaxBlockSpace: math.LegacyMustNewDecFromStr("0.1"), 51 | MaxTxs: 1, 52 | SignerExtractor: signerExtractor, 53 | }, opchildlanes.SystemLaneMatchHandler()) 54 | 55 | factory := mevlane.NewDefaultAuctionFactory(app.txConfig.TxDecoder(), signerExtractor) 56 | mevLane := initialanes.NewMEVLane(blockbase.LaneConfig{ 57 | Logger: app.Logger(), 58 | TxEncoder: app.txConfig.TxEncoder(), 59 | TxDecoder: app.txConfig.TxDecoder(), 60 | MaxBlockSpace: math.LegacyMustNewDecFromStr("0.1"), 61 | MaxTxs: 100, 62 | SignerExtractor: signerExtractor, 63 | }, factory, factory.MatchHandler()) 64 | 65 | freeLane := initialanes.NewFreeLane(blockbase.LaneConfig{ 66 | Logger: app.Logger(), 67 | TxEncoder: app.txConfig.TxEncoder(), 68 | TxDecoder: app.txConfig.TxDecoder(), 69 | MaxBlockSpace: math.LegacyMustNewDecFromStr("0.1"), 70 | MaxTxs: 100, 71 | SignerExtractor: signerExtractor, 72 | }, opchildlanes.NewFreeLaneMatchHandler(app.ac, app.OPChildKeeper).MatchHandler()) 73 | 74 | defaultLane := initialanes.NewDefaultLane(blockbase.LaneConfig{ 75 | Logger: app.Logger(), 76 | TxEncoder: app.txConfig.TxEncoder(), 77 | TxDecoder: app.txConfig.TxDecoder(), 78 | MaxBlockSpace: math.LegacyMustNewDecFromStr("0.7"), 79 | MaxTxs: mempoolMaxTxs, 80 | SignerExtractor: signerExtractor, 81 | }) 82 | 83 | lanes := []block.Lane{systemLane, mevLane, freeLane, defaultLane} 84 | mempool, err := block.NewLanedMempool(app.Logger(), lanes) 85 | if err != nil { 86 | return nil, nil, nil, nil, nil, err 87 | } 88 | 89 | anteHandler, err := appante.NewAnteHandler( 90 | appante.HandlerOptions{ 91 | HandlerOptions: cosmosante.HandlerOptions{ 92 | AccountKeeper: app.AccountKeeper, 93 | BankKeeper: app.BankKeeper, 94 | FeegrantKeeper: app.FeeGrantKeeper, 95 | SignModeHandler: app.txConfig.SignModeHandler(), 96 | }, 97 | IBCkeeper: app.IBCKeeper, 98 | Codec: app.appCodec, 99 | OPChildKeeper: app.OPChildKeeper, 100 | TxEncoder: app.txConfig.TxEncoder(), 101 | AuctionKeeper: app.AuctionKeeper, 102 | MevLane: mevLane, 103 | FreeLane: freeLane, 104 | WasmKeeper: app.WasmKeeper, 105 | WasmConfig: &wasmConfig, 106 | TXCounterStoreService: runtime.NewKVStoreService(txCounterStoreKey), 107 | }, 108 | ) 109 | if err != nil { 110 | return nil, nil, nil, nil, nil, err 111 | } 112 | 113 | // set ante handler to lanes 114 | opt := []blockbase.LaneOption{ 115 | blockbase.WithAnteHandler(anteHandler), 116 | } 117 | for _, lane := range lanes { 118 | if blane, ok := lane.(*blockbase.BaseLane); ok { 119 | blane.WithOptions(opt...) 120 | } else if mlane, ok := lane.(*mevlane.MEVLane); ok { 121 | mlane.WithOptions(opt...) 122 | } 123 | } 124 | 125 | mevCheckTx := blockchecktx.NewMEVCheckTxHandler( 126 | app.BaseApp, 127 | app.txConfig.TxDecoder(), 128 | mevLane, 129 | anteHandler, 130 | app.BaseApp.CheckTx, 131 | ) 132 | checkTxHandler := blockchecktx.NewMempoolParityCheckTx( 133 | app.Logger(), 134 | mempool, 135 | app.txConfig.TxDecoder(), 136 | mevCheckTx.CheckTx(), 137 | app.BaseApp, 138 | ) 139 | checkTx := checkTxHandler.CheckTx() 140 | 141 | proposalHandler := blockabci.New( 142 | app.Logger(), 143 | app.txConfig.TxDecoder(), 144 | app.txConfig.TxEncoder(), 145 | mempool, 146 | true, 147 | ) 148 | 149 | prepareProposalHandler := proposalHandler.PrepareProposalHandler() 150 | processProposalHandler := proposalHandler.ProcessProposalHandler() 151 | 152 | return mempool, anteHandler, checkTx, prepareProposalHandler, processProposalHandler, nil 153 | } 154 | -------------------------------------------------------------------------------- /app/const.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | const ( 4 | // FeeDeductionGasAmount is a estimated gas amount of fee payment 5 | FeeDeductionGasAmount = 50_000 6 | 7 | // AccountAddressPrefix is the prefix of bech32 encoded address 8 | AccountAddressPrefix = "init" 9 | 10 | // AppName is the application name 11 | AppName = "minitia" 12 | 13 | // EnvPrefix is environment variable prefix for the app 14 | EnvPrefix = "MINITIA" 15 | 16 | // CoinType is the Cosmos Chain's coin type as defined in SLIP44 (https://github.com/satoshilabs/slips/blob/master/slip-0044.md) 17 | CoinType = 60 18 | 19 | authzMsgExec = "/cosmos.authz.v1beta1.MsgExec" 20 | authzMsgGrant = "/cosmos.authz.v1beta1.MsgGrant" 21 | authzMsgRevoke = "/cosmos.authz.v1beta1.MsgRevoke" 22 | bankMsgSend = "/cosmos.bank.v1beta1.MsgSend" 23 | bankMsgMultiSend = "/cosmos.bank.v1beta1.MsgMultiSend" 24 | feegrantMsgGrantAllowance = "/cosmos.feegrant.v1beta1.MsgGrantAllowance" 25 | feegrantMsgRevokeAllowance = "/cosmos.feegrant.v1beta1.MsgRevokeAllowance" 26 | groupCreateGroup = "/cosmos.group.v1.MsgCreateGroup" 27 | groupUpdateGroupMember = "/cosmos.group.v1.MsgUpdateGroupMember" 28 | groupUpdateGroupAdmin = "/cosmos.group.v1.MsgUpdateGroupAdmin" 29 | groupUpdateGroupMetadata = "/cosmos.group.v1.MsgUpdateGroupMetadata" 30 | groupCreateGroupPolicy = "/cosmos.group.v1.MsgCreateGroupPolicy" 31 | groupUpdateGroupPolicyAdmin = "/cosmos.group.v1.MsgUpdateGroupPolicyAdmin" 32 | groupUpdateGroupPolicyDecisionPolicy = "/cosmos.group.v1.MsgUpdateGroupPolicyDecisionPolicy" 33 | groupSubmitProposal = "/cosmos.group.v1.MsgSubmitProposal" 34 | groupWithdrawProposal = "/cosmos.group.v1.MsgWithdrawProposal" 35 | groupVote = "/cosmos.group.v1.MsgVote" 36 | groupExec = "/cosmos.group.v1.MsgExec" 37 | groupLeaveGroup = "/cosmos.group.v1.MsgLeaveGroup" 38 | transferMsgTransfer = "/ibc.applications.transfer.v1.MsgTransfer" 39 | nftTransferMsgTransfer = "/ibc.applications.nft_transfer.v1.MsgNftTransfer" 40 | sftTransferMsgTransfer = "/ibc.applications.sft_transfer.v1.MsgSftTransfer" 41 | moveMsgPublishModuleBundle = "/initia.move.v1.MsgPublish" 42 | moveMsgExecuteEntryFunction = "/initia.move.v1.MsgExecute" 43 | moveMsgExecuteScript = "/initia.move.v1.MsgScript" 44 | 45 | // UpgradeName gov proposal name 46 | UpgradeName = "0.0.0" 47 | ) 48 | -------------------------------------------------------------------------------- /app/encoding.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "math/rand" 5 | "os" 6 | "path/filepath" 7 | "strconv" 8 | 9 | "cosmossdk.io/client/v2/autocli" 10 | "cosmossdk.io/core/appmodule" 11 | "cosmossdk.io/log" 12 | dbm "github.com/cosmos/cosmos-db" 13 | 14 | "github.com/cosmos/cosmos-sdk/client/flags" 15 | runtimeservices "github.com/cosmos/cosmos-sdk/runtime/services" 16 | sdk "github.com/cosmos/cosmos-sdk/types" 17 | "github.com/cosmos/cosmos-sdk/types/module" 18 | authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" 19 | 20 | wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" 21 | 22 | "github.com/initia-labs/initia/app/params" 23 | ) 24 | 25 | func tempApp() *MinitiaApp { 26 | return NewMinitiaApp( 27 | log.NewNopLogger(), 28 | dbm.NewMemDB(), 29 | dbm.NewMemDB(), 30 | nil, 31 | false, 32 | []wasmkeeper.Option{}, 33 | NewEmptyAppOptions(), 34 | ) 35 | } 36 | 37 | // MakeEncodingConfig creates an EncodingConfig for testing 38 | func MakeEncodingConfig() params.EncodingConfig { 39 | tempApp := tempApp() 40 | encodingConfig := params.EncodingConfig{ 41 | InterfaceRegistry: tempApp.InterfaceRegistry(), 42 | Codec: tempApp.AppCodec(), 43 | TxConfig: tempApp.TxConfig(), 44 | Amino: tempApp.LegacyAmino(), 45 | } 46 | 47 | return encodingConfig 48 | } 49 | 50 | func AutoCliOpts() autocli.AppOptions { 51 | tempApp := tempApp() 52 | modules := make(map[string]appmodule.AppModule, 0) 53 | for _, m := range tempApp.ModuleManager.Modules { 54 | if moduleWithName, ok := m.(module.HasName); ok { 55 | moduleName := moduleWithName.Name() 56 | if appModule, ok := moduleWithName.(appmodule.AppModule); ok { 57 | modules[moduleName] = appModule 58 | } 59 | } 60 | } 61 | 62 | return autocli.AppOptions{ 63 | Modules: modules, 64 | ModuleOptions: runtimeservices.ExtractAutoCLIOptions(tempApp.ModuleManager.Modules), 65 | AddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()), 66 | ValidatorAddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ValidatorAddrPrefix()), 67 | ConsensusAddressCodec: authcodec.NewBech32Codec(sdk.GetConfig().GetBech32ConsensusAddrPrefix()), 68 | } 69 | } 70 | 71 | func BasicManager() module.BasicManager { 72 | tempApp := tempApp() 73 | return tempApp.BasicModuleManager 74 | } 75 | 76 | // EmptyAppOptions is a stub implementing AppOptions 77 | type EmptyAppOptions struct { 78 | homeDir string 79 | } 80 | 81 | func NewEmptyAppOptions() EmptyAppOptions { 82 | return EmptyAppOptions{ 83 | homeDir: filepath.Join(os.TempDir(), strconv.Itoa(rand.Int())), 84 | } 85 | } 86 | 87 | // Get implements AppOptions 88 | func (ao EmptyAppOptions) Get(o string) interface{} { 89 | if o == flags.FlagHome { 90 | if ao.homeDir == "" { 91 | return DefaultNodeHome 92 | } 93 | 94 | return ao.homeDir 95 | } 96 | 97 | return nil 98 | } 99 | -------------------------------------------------------------------------------- /app/executor_change.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | // Executor change plans 4 | 5 | func (app *MinitiaApp) RegisterExecutorChangePlans() error { 6 | // err := app.OPChildKeeper.RegisterExecutorChangePlan( 7 | // 1, 8 | // 361, 9 | // "initvaloper158x0dpu5f4x703fhtseg5kpytsj02hw04qyw0t", 10 | // "init158x0dpu5f4x703fhtseg5kpytsj02hw0p9ahpm", 11 | // "op2", 12 | // "{\"@type\":\"/cosmos.crypto.ed25519.PubKey\",\"key\":\"GTJEksmVK7gkzPXdj+YIJxipfJ+yYUlc6jzIuh9s2t0=\"}", 13 | // "testestestestsetsetestsetest", 14 | // ) 15 | 16 | // if err != nil { 17 | // return err 18 | // } 19 | 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /app/export.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/initia-labs/OPinit/x/opchild" 7 | 8 | servertypes "github.com/cosmos/cosmos-sdk/server/types" 9 | ) 10 | 11 | // ExportAppStateAndValidators exports the state of the application for a genesis 12 | // file. 13 | func (app *MinitiaApp) ExportAppStateAndValidators( 14 | forZeroHeight bool, modulesToExport []string, 15 | ) (servertypes.ExportedApp, error) { 16 | // as if they could withdraw from the start of the next block 17 | ctx := app.NewContext(true) 18 | 19 | // We export at last height + 1, because that's the height at which 20 | // Tendermint will start InitChain. 21 | height := app.LastBlockHeight() + 1 22 | if forZeroHeight { 23 | height = 0 24 | } 25 | 26 | genState, err := app.ModuleManager.ExportGenesisForModules(ctx, app.appCodec, modulesToExport) 27 | if err != nil { 28 | return servertypes.ExportedApp{}, err 29 | } 30 | appState, err := json.MarshalIndent(genState, "", " ") 31 | if err != nil { 32 | return servertypes.ExportedApp{}, err 33 | } 34 | 35 | validators, err := opchild.WriteValidators(ctx, app.OPChildKeeper) 36 | if err != nil { 37 | return servertypes.ExportedApp{}, err 38 | } 39 | 40 | return servertypes.ExportedApp{ 41 | AppState: appState, 42 | Validators: validators, 43 | Height: height, 44 | ConsensusParams: app.BaseApp.GetConsensusParams(ctx), 45 | }, nil 46 | } 47 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --release --target wasm32-unknown-unknown" 3 | unit-test = "test --lib" 4 | schema = "run --example schema" 5 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.rs] 11 | indent_size = 4 12 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/.gitignore: -------------------------------------------------------------------------------- 1 | # Build results 2 | /target 3 | 4 | # Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327) 5 | .cargo-ok 6 | 7 | # Text file backups 8 | **/*.rs.bk 9 | 10 | # macOS 11 | .DS_Store 12 | 13 | # IDEs 14 | *.iml 15 | .idea 16 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "counter" 3 | version = "0.1.0" 4 | authors = ["beer-1@codingcats.xyz"] 5 | edition = "2018" 6 | 7 | exclude = [ 8 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 9 | "contract.wasm", 10 | "hash.txt", 11 | ] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [lib] 16 | crate-type = ["cdylib", "rlib"] 17 | 18 | [profile.release] 19 | opt-level = 3 20 | debug = false 21 | rpath = false 22 | lto = true 23 | debug-assertions = false 24 | codegen-units = 1 25 | panic = 'abort' 26 | incremental = false 27 | overflow-checks = true 28 | 29 | 30 | [dependencies] 31 | cosmwasm-std = "2.0.4" 32 | cosmwasm-schema = "2.0.4" 33 | cw-storage-plus = "2.0.0" 34 | 35 | [dev-dependencies] 36 | 37 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2020 orkunkl 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/README.md: -------------------------------------------------------------------------------- 1 | # Counter 2 | 3 | This contract is a simple implementation of a counter using the IBC Hooks library. It allows you to increment and decrement the counter value. 4 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/artifacts/checksums.txt: -------------------------------------------------------------------------------- 1 | c132532b9641facc9afe6c4ad81bebd1237292c26f50ec782426b51cee4cdc90 counter-aarch64.wasm 2 | c7ef9d37456ef0e7754f0e7df70f6f2488a185ea2a1acdee464daf65428d66c3 counter.wasm 3 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/artifacts/checksums_intermediate.txt: -------------------------------------------------------------------------------- 1 | 9069c93a592c79de64adf23be85f0f8345819c00df1bebaa57705e4d7d1779b7 ./target/wasm32-unknown-unknown/release/counter.wasm 2 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/artifacts/counter-aarch64.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/app/ibc-hooks/contracts/artifacts/counter-aarch64.wasm -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/artifacts/counter.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/app/ibc-hooks/contracts/artifacts/counter.wasm -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/examples/schema.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::write_api; 2 | 3 | use counter::msg::{ExecuteMsg, InstantiateMsg, QueryMsg}; 4 | 5 | fn main() { 6 | write_api! { 7 | instantiate: InstantiateMsg, 8 | query: QueryMsg, 9 | execute: ExecuteMsg, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/rustfmt.toml: -------------------------------------------------------------------------------- 1 | # stable 2 | newline_style = "unix" 3 | hard_tabs = false 4 | tab_spaces = 4 5 | 6 | # unstable... should we require `rustup run nightly cargo fmt` ? 7 | # or just update the style guide when they are stable? 8 | #fn_single_line = true 9 | #format_code_in_doc_comments = true 10 | #overflow_delimited_expr = true 11 | #reorder_impl_items = true 12 | #struct_field_align_threshold = 20 13 | #struct_lit_single_line = true 14 | #report_todo = "Always" 15 | 16 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/schema/counter.json: -------------------------------------------------------------------------------- 1 | { 2 | "contract_name": "counter", 3 | "contract_version": "0.1.0", 4 | "idl_version": "1.0.0", 5 | "instantiate": { 6 | "$schema": "http://json-schema.org/draft-07/schema#", 7 | "title": "InstantiateMsg", 8 | "type": "object", 9 | "additionalProperties": false 10 | }, 11 | "execute": { 12 | "$schema": "http://json-schema.org/draft-07/schema#", 13 | "title": "ExecuteMsg", 14 | "oneOf": [ 15 | { 16 | "type": "object", 17 | "required": [ 18 | "increase" 19 | ], 20 | "properties": { 21 | "increase": { 22 | "type": "object", 23 | "additionalProperties": false 24 | } 25 | }, 26 | "additionalProperties": false 27 | } 28 | ] 29 | }, 30 | "query": { 31 | "$schema": "http://json-schema.org/draft-07/schema#", 32 | "title": "QueryMsg", 33 | "oneOf": [ 34 | { 35 | "type": "object", 36 | "required": [ 37 | "get" 38 | ], 39 | "properties": { 40 | "get": { 41 | "type": "object", 42 | "additionalProperties": false 43 | } 44 | }, 45 | "additionalProperties": false 46 | } 47 | ] 48 | }, 49 | "migrate": null, 50 | "sudo": null, 51 | "responses": { 52 | "get": { 53 | "$schema": "http://json-schema.org/draft-07/schema#", 54 | "title": "uint64", 55 | "type": "integer", 56 | "format": "uint64", 57 | "minimum": 0.0 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/schema/raw/execute.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "ExecuteMsg", 4 | "oneOf": [ 5 | { 6 | "type": "object", 7 | "required": [ 8 | "increase" 9 | ], 10 | "properties": { 11 | "increase": { 12 | "type": "object", 13 | "additionalProperties": false 14 | } 15 | }, 16 | "additionalProperties": false 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/schema/raw/instantiate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "InstantiateMsg", 4 | "type": "object", 5 | "additionalProperties": false 6 | } 7 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/schema/raw/query.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "QueryMsg", 4 | "oneOf": [ 5 | { 6 | "type": "object", 7 | "required": [ 8 | "get" 9 | ], 10 | "properties": { 11 | "get": { 12 | "type": "object", 13 | "additionalProperties": false 14 | } 15 | }, 16 | "additionalProperties": false 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/schema/raw/response_to_get.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-07/schema#", 3 | "title": "uint64", 4 | "type": "integer", 5 | "format": "uint64", 6 | "minimum": 0.0 7 | } 8 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/src/contract.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{ 2 | to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdError, StdResult, 3 | }; 4 | 5 | use crate::msg::{ExecuteMsg, IBCLifecycleComplete, InstantiateMsg, QueryMsg, SudoMsg}; 6 | use crate::state::{Count, COUNT}; 7 | 8 | #[cfg(not(feature = "library"))] 9 | use cosmwasm_std::entry_point; 10 | 11 | // Note, you can use StdResult in some functions where you do not 12 | // make use of the custom errors 13 | #[cfg_attr(not(feature = "library"), entry_point)] 14 | pub fn instantiate( 15 | deps: DepsMut, 16 | _env: Env, 17 | _info: MessageInfo, 18 | _msg: InstantiateMsg, 19 | ) -> Result { 20 | let count = Count { val: 0 }; 21 | 22 | COUNT.save(deps.storage, &count)?; 23 | 24 | Ok(Response::default()) 25 | } 26 | 27 | // And declare a custom Error variant for the ones where you will want to make use of it 28 | #[cfg_attr(not(feature = "library"), entry_point)] 29 | pub fn execute( 30 | deps: DepsMut, 31 | env: Env, 32 | info: MessageInfo, 33 | msg: ExecuteMsg, 34 | ) -> Result { 35 | match msg { 36 | ExecuteMsg::Increase {} => execute_increase(deps, env, info), 37 | } 38 | } 39 | 40 | pub fn execute_increase( 41 | deps: DepsMut, 42 | _env: Env, 43 | _info: MessageInfo, 44 | ) -> Result { 45 | let mut count = COUNT.load(deps.storage)?; 46 | count.val += 1; 47 | COUNT.save(deps.storage, &count)?; 48 | Ok(Response::new()) 49 | } 50 | 51 | #[cfg_attr(not(feature = "library"), entry_point)] 52 | pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { 53 | match msg { 54 | QueryMsg::Get {} => to_json_binary(&query_get(deps)?), 55 | } 56 | } 57 | 58 | fn query_get(deps: Deps) -> StdResult { 59 | Ok(COUNT.load(deps.storage)?.val) 60 | } 61 | 62 | #[cfg_attr(not(feature = "library"), entry_point)] 63 | pub fn sudo(deps: DepsMut, _env: Env, msg: SudoMsg) -> Result { 64 | match msg { 65 | SudoMsg::IBCLifecycleComplete(inner_msg) => match inner_msg { 66 | IBCLifecycleComplete::IBCAck { 67 | channel: _, 68 | ack: _, 69 | sequence, 70 | success, 71 | } => { 72 | let mut count = COUNT.load(deps.storage)?; 73 | if success { 74 | count.val += sequence; 75 | } else { 76 | count.val += 1; 77 | } 78 | 79 | COUNT.save(deps.storage, &count)?; 80 | Ok(Response::new()) 81 | } 82 | IBCLifecycleComplete::IBCTimeout { 83 | channel: _, 84 | sequence, 85 | } => { 86 | let mut count = COUNT.load(deps.storage)?; 87 | count.val += sequence; 88 | 89 | COUNT.save(deps.storage, &count)?; 90 | Ok(Response::new()) 91 | } 92 | }, 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | pub mod msg; 3 | pub mod state; 4 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::{cw_serde, QueryResponses}; 2 | 3 | #[cw_serde] 4 | pub struct InstantiateMsg {} 5 | 6 | #[cw_serde] 7 | pub enum ExecuteMsg { 8 | Increase {}, 9 | } 10 | 11 | #[cw_serde] 12 | #[derive(QueryResponses)] 13 | pub enum QueryMsg { 14 | #[returns(u64)] 15 | Get {}, 16 | } 17 | 18 | 19 | #[cw_serde] 20 | pub enum IBCLifecycleComplete { 21 | #[serde(rename = "ibc_ack")] 22 | IBCAck { 23 | /// The source channel (miniwasm side) of the IBC packet 24 | channel: String, 25 | /// The sequence number that the packet was sent with 26 | sequence: u64, 27 | /// String encoded version of the ack as seen by OnAcknowledgementPacket(..) 28 | ack: String, 29 | /// Weather an ack is a success of failure according to the transfer spec 30 | success: bool, 31 | }, 32 | #[serde(rename = "ibc_timeout")] 33 | IBCTimeout { 34 | /// The source channel (miniwasm side) of the IBC packet 35 | channel: String, 36 | /// The sequence number that the packet was sent with 37 | sequence: u64, 38 | }, 39 | } 40 | 41 | /// Message type for `sudo` entry_point 42 | #[cw_serde] 43 | pub enum SudoMsg { 44 | #[serde(rename = "ibc_lifecycle_complete")] 45 | IBCLifecycleComplete(IBCLifecycleComplete), 46 | } 47 | -------------------------------------------------------------------------------- /app/ibc-hooks/contracts/src/state.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::cw_serde; 2 | use cw_storage_plus::Item; 3 | 4 | #[cw_serde] 5 | pub struct Count { 6 | pub val: u64, 7 | } 8 | 9 | pub const COUNT: Item = Item::new("count"); 10 | -------------------------------------------------------------------------------- /app/ibc-hooks/hooks.go: -------------------------------------------------------------------------------- 1 | package wasm_hooks 2 | 3 | import ( 4 | "cosmossdk.io/core/address" 5 | channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" 6 | ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" 7 | 8 | "github.com/cosmos/cosmos-sdk/codec" 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | 11 | wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" 12 | ibchooks "github.com/initia-labs/initia/x/ibc-hooks" 13 | ) 14 | 15 | var ( 16 | _ ibchooks.OnRecvPacketOverrideHooks = WasmHooks{} 17 | _ ibchooks.OnAcknowledgementPacketOverrideHooks = WasmHooks{} 18 | _ ibchooks.OnTimeoutPacketOverrideHooks = WasmHooks{} 19 | ) 20 | 21 | type WasmHooks struct { 22 | codec codec.Codec 23 | ac address.Codec 24 | wasmKeeper *wasmkeeper.Keeper 25 | } 26 | 27 | func NewWasmHooks(codec codec.Codec, ac address.Codec, wasmKeeper *wasmkeeper.Keeper) *WasmHooks { 28 | return &WasmHooks{ 29 | codec: codec, 30 | ac: ac, 31 | wasmKeeper: wasmKeeper, 32 | } 33 | } 34 | 35 | func (h WasmHooks) OnRecvPacketOverride(im ibchooks.IBCMiddleware, ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) ibcexported.Acknowledgement { 36 | if isIcs20, ics20Data := isIcs20Packet(packet.GetData()); isIcs20 { 37 | return h.onRecvIcs20Packet(ctx, im, packet, relayer, ics20Data) 38 | } 39 | 40 | if isIcs721, ics721Data := isIcs721Packet(packet.GetData()); isIcs721 { 41 | return h.onRecvIcs721Packet(ctx, im, packet, relayer, ics721Data) 42 | } 43 | 44 | return im.App.OnRecvPacket(ctx, packet, relayer) 45 | } 46 | 47 | func (h WasmHooks) OnAcknowledgementPacketOverride(im ibchooks.IBCMiddleware, ctx sdk.Context, packet channeltypes.Packet, acknowledgement []byte, relayer sdk.AccAddress) error { 48 | if isIcs20, ics20Data := isIcs20Packet(packet.GetData()); isIcs20 { 49 | return h.onAckIcs20Packet(ctx, im, packet, acknowledgement, relayer, ics20Data) 50 | } 51 | 52 | if isIcs721, ics721Data := isIcs721Packet(packet.GetData()); isIcs721 { 53 | return h.onAckIcs721Packet(ctx, im, packet, acknowledgement, relayer, ics721Data) 54 | } 55 | 56 | return im.App.OnAcknowledgementPacket(ctx, packet, acknowledgement, relayer) 57 | } 58 | 59 | func (h WasmHooks) OnTimeoutPacketOverride(im ibchooks.IBCMiddleware, ctx sdk.Context, packet channeltypes.Packet, relayer sdk.AccAddress) error { 60 | if isIcs20, ics20Data := isIcs20Packet(packet.GetData()); isIcs20 { 61 | return h.onTimeoutIcs20Packet(ctx, im, packet, relayer, ics20Data) 62 | } 63 | 64 | if isIcs721, ics721Data := isIcs721Packet(packet.GetData()); isIcs721 { 65 | return h.onTimeoutIcs721Packet(ctx, im, packet, relayer, ics721Data) 66 | } 67 | 68 | return im.App.OnTimeoutPacket(ctx, packet, relayer) 69 | } 70 | 71 | func (h WasmHooks) checkACL(im ibchooks.IBCMiddleware, ctx sdk.Context, addrStr string) (bool, error) { 72 | addr, err := h.ac.StringToBytes(addrStr) 73 | if err != nil { 74 | return false, err 75 | } 76 | 77 | return im.HooksKeeper.GetAllowed(ctx, addr) 78 | } 79 | -------------------------------------------------------------------------------- /app/ibc-hooks/message.go: -------------------------------------------------------------------------------- 1 | package wasm_hooks 2 | 3 | import ( 4 | wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" 5 | ) 6 | 7 | const ( 8 | // The memo key is used to parse ics-20 or ics-712 memo fields. 9 | wasmHookMemoKey = "wasm" 10 | ) 11 | 12 | // HookData defines a wrapper for wasm execute message 13 | // and async callback. 14 | type HookData struct { 15 | // Message is a wasm execute message which will be executed 16 | // at `OnRecvPacket` of receiver chain. 17 | Message *wasmtypes.MsgExecuteContract `json:"message,omitempty"` 18 | 19 | // AsyncCallback is a contract address 20 | AsyncCallback string `json:"async_callback,omitempty"` 21 | } 22 | -------------------------------------------------------------------------------- /app/ibc-hooks/timeout.go: -------------------------------------------------------------------------------- 1 | package wasm_hooks 2 | 3 | import ( 4 | "fmt" 5 | 6 | transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" 7 | channeltypes "github.com/cosmos/ibc-go/v8/modules/core/04-channel/types" 8 | 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | 11 | ibchooks "github.com/initia-labs/initia/x/ibc-hooks" 12 | "github.com/initia-labs/initia/x/ibc-hooks/types" 13 | nfttransfertypes "github.com/initia-labs/initia/x/ibc/nft-transfer/types" 14 | ) 15 | 16 | func (h WasmHooks) onTimeoutIcs20Packet( 17 | ctx sdk.Context, 18 | im ibchooks.IBCMiddleware, 19 | packet channeltypes.Packet, 20 | relayer sdk.AccAddress, 21 | data transfertypes.FungibleTokenPacketData, 22 | ) error { 23 | if err := im.App.OnTimeoutPacket(ctx, packet, relayer); err != nil { 24 | return err 25 | } 26 | 27 | isWasmRouted, hookData, err := validateAndParseMemo(data.GetMemo()) 28 | if !isWasmRouted || hookData.AsyncCallback == "" { 29 | return nil 30 | } else if err != nil { 31 | h.wasmKeeper.Logger(ctx).Error("failed to parse memo", "error", err) 32 | return nil 33 | } 34 | 35 | // create a new cache context to ignore errors during 36 | // the execution of the callback 37 | cacheCtx, write := ctx.CacheContext() 38 | 39 | callback := hookData.AsyncCallback 40 | if allowed, err := h.checkACL(im, cacheCtx, callback); err != nil { 41 | h.wasmKeeper.Logger(cacheCtx).Error("failed to check ACL", "error", err) 42 | ctx.EventManager().EmitEvent(sdk.NewEvent( 43 | types.EventTypeHookFailed, 44 | sdk.NewAttribute(types.AttributeKeyReason, "failed to check ACL"), 45 | sdk.NewAttribute(types.AttributeKeyError, err.Error()), 46 | )) 47 | 48 | return nil 49 | } else if !allowed { 50 | h.wasmKeeper.Logger(cacheCtx).Error("failed to check ACL", "not allowed") 51 | ctx.EventManager().EmitEvent(sdk.NewEvent( 52 | types.EventTypeHookFailed, 53 | sdk.NewAttribute(types.AttributeKeyReason, "failed to check ACL"), 54 | sdk.NewAttribute(types.AttributeKeyError, "not allowed"), 55 | )) 56 | 57 | return nil 58 | } 59 | 60 | contractAddr, err := h.ac.StringToBytes(callback) 61 | if err != nil { 62 | h.wasmKeeper.Logger(cacheCtx).Error("invalid contract address", "error", err) 63 | return nil 64 | } 65 | 66 | sudoMsg := []byte(fmt.Sprintf( 67 | `{"ibc_lifecycle_complete": {"ibc_timeout": {"channel": "%s", "sequence": %d}}}`, 68 | packet.SourceChannel, packet.Sequence)) 69 | _, err = h.wasmKeeper.Sudo(cacheCtx, contractAddr, sudoMsg) 70 | if err != nil { 71 | h.wasmKeeper.Logger(cacheCtx).Error("failed to execute callback", "error", err) 72 | ctx.EventManager().EmitEvent(sdk.NewEvent( 73 | types.EventTypeHookFailed, 74 | sdk.NewAttribute(types.AttributeKeyReason, "failed to execute callback"), 75 | sdk.NewAttribute(types.AttributeKeyError, err.Error()), 76 | )) 77 | 78 | return nil 79 | } 80 | 81 | // write the cache context only if the callback execution was successful 82 | write() 83 | 84 | return nil 85 | } 86 | 87 | func (h WasmHooks) onTimeoutIcs721Packet( 88 | ctx sdk.Context, 89 | im ibchooks.IBCMiddleware, 90 | packet channeltypes.Packet, 91 | relayer sdk.AccAddress, 92 | data nfttransfertypes.NonFungibleTokenPacketData, 93 | ) error { 94 | if err := im.App.OnTimeoutPacket(ctx, packet, relayer); err != nil { 95 | return err 96 | } 97 | 98 | isWasmRouted, hookData, err := validateAndParseMemo(data.GetMemo()) 99 | if !isWasmRouted || hookData.AsyncCallback == "" { 100 | return nil 101 | } else if err != nil { 102 | h.wasmKeeper.Logger(ctx).Error("failed to parse memo", "error", err) 103 | return nil 104 | } 105 | 106 | // create a new cache context to ignore errors during 107 | // the execution of the callback 108 | cacheCtx, write := ctx.CacheContext() 109 | 110 | callback := hookData.AsyncCallback 111 | if allowed, err := h.checkACL(im, cacheCtx, callback); err != nil { 112 | h.wasmKeeper.Logger(cacheCtx).Error("failed to check ACL", "error", err) 113 | ctx.EventManager().EmitEvent(sdk.NewEvent( 114 | types.EventTypeHookFailed, 115 | sdk.NewAttribute(types.AttributeKeyReason, "failed to check ACL"), 116 | sdk.NewAttribute(types.AttributeKeyError, err.Error()), 117 | )) 118 | 119 | return nil 120 | } else if !allowed { 121 | h.wasmKeeper.Logger(cacheCtx).Error("failed to check ACL", "not allowed") 122 | ctx.EventManager().EmitEvent(sdk.NewEvent( 123 | types.EventTypeHookFailed, 124 | sdk.NewAttribute(types.AttributeKeyReason, "failed to check ACL"), 125 | sdk.NewAttribute(types.AttributeKeyError, "not allowed"), 126 | )) 127 | 128 | return nil 129 | } 130 | 131 | contractAddr, err := h.ac.StringToBytes(callback) 132 | if err != nil { 133 | h.wasmKeeper.Logger(cacheCtx).Error("invalid contract address", "error", err) 134 | return nil 135 | } 136 | 137 | sudoMsg := []byte(fmt.Sprintf( 138 | `{"ibc_lifecycle_complete": {"ibc_timeout": {"channel": "%s", "sequence": %d}}}`, 139 | packet.SourceChannel, packet.Sequence)) 140 | _, err = h.wasmKeeper.Sudo(cacheCtx, contractAddr, sudoMsg) 141 | if err != nil { 142 | h.wasmKeeper.Logger(cacheCtx).Error("failed to execute callback", "error", err) 143 | ctx.EventManager().EmitEvent(sdk.NewEvent( 144 | types.EventTypeHookFailed, 145 | sdk.NewAttribute(types.AttributeKeyReason, "failed to execute callback"), 146 | sdk.NewAttribute(types.AttributeKeyError, err.Error()), 147 | )) 148 | 149 | return nil 150 | } 151 | 152 | // write the cache context only if the callback execution was successful 153 | write() 154 | 155 | return nil 156 | } 157 | -------------------------------------------------------------------------------- /app/ibc-hooks/util_test.go: -------------------------------------------------------------------------------- 1 | package wasm_hooks 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | "cosmossdk.io/math" 10 | transfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" 11 | 12 | sdk "github.com/cosmos/cosmos-sdk/types" 13 | 14 | nfttransfertypes "github.com/initia-labs/initia/x/ibc/nft-transfer/types" 15 | 16 | wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" 17 | ) 18 | 19 | func Test_isIcs20Packet(t *testing.T) { 20 | transferMsg := transfertypes.NewFungibleTokenPacketData("denom", "1000000", "0x1", "0x2", "memo") 21 | bz, err := json.Marshal(transferMsg) 22 | require.NoError(t, err) 23 | 24 | ok, _transferMsg := isIcs20Packet(bz) 25 | require.True(t, ok) 26 | require.Equal(t, transferMsg, _transferMsg) 27 | 28 | nftTransferMsg := nfttransfertypes.NewNonFungibleTokenPacketData("class_id", "uri", "data", []string{"1", "2", "3"}, []string{"uri1", "uri2", "uri3"}, []string{"data1", "data2", "data3"}, "sender", "receiver", "memo") 29 | bz, err = json.Marshal(nftTransferMsg) 30 | require.NoError(t, err) 31 | 32 | ok, _ = isIcs20Packet(bz) 33 | require.False(t, ok) 34 | } 35 | 36 | func Test_isIcs721Packet(t *testing.T) { 37 | nftTransferMsg := nfttransfertypes.NewNonFungibleTokenPacketData("class_id", "uri", "data", []string{"1", "2", "3"}, []string{"uri1", "uri2", "uri3"}, []string{"data1", "data2", "data3"}, "sender", "receiver", "memo") 38 | 39 | ok, _nftTransferMsg := isIcs721Packet(nftTransferMsg.GetBytes()) 40 | require.True(t, ok) 41 | require.Equal(t, nftTransferMsg, _nftTransferMsg) 42 | 43 | transferMsg := transfertypes.NewFungibleTokenPacketData("denom", "1000000", "0x1", "0x2", "memo") 44 | ok, _ = isIcs721Packet(transferMsg.GetBytes()) 45 | require.False(t, ok) 46 | } 47 | 48 | func Test_validateAndParseMemo_without_callback(t *testing.T) { 49 | memo := `{ 50 | "wasm" : { 51 | "message": { 52 | "sender": "init_addr", 53 | "contract": "contract_addr", 54 | "msg": {}, 55 | "funds": [{"denom":"foo","amount":"100"}] 56 | } 57 | } 58 | }` 59 | isWasmRouted, hookData, err := validateAndParseMemo(memo) 60 | require.True(t, isWasmRouted) 61 | require.NoError(t, err) 62 | require.Equal(t, HookData{ 63 | Message: &wasmtypes.MsgExecuteContract{ 64 | Sender: "init_addr", 65 | Contract: "contract_addr", 66 | Msg: []byte("{}"), 67 | Funds: sdk.Coins{{ 68 | Denom: "foo", 69 | Amount: math.NewInt(100), 70 | }}, 71 | }, 72 | AsyncCallback: "", 73 | }, hookData) 74 | require.NoError(t, validateReceiver(hookData.Message, "contract_addr")) 75 | 76 | // invalid receiver 77 | require.NoError(t, err) 78 | require.Error(t, validateReceiver(hookData.Message, "invalid_addr")) 79 | 80 | isWasmRouted, _, err = validateAndParseMemo("hihi") 81 | require.False(t, isWasmRouted) 82 | require.NoError(t, err) 83 | } 84 | 85 | func Test_validateAndParseMemo_with_callback(t *testing.T) { 86 | memo := `{ 87 | "wasm" : { 88 | "message": { 89 | "sender": "init_addr", 90 | "contract": "contract_addr", 91 | "msg": {}, 92 | "funds": [{"denom":"foo","amount":"100"}] 93 | }, 94 | "async_callback": "callback_addr" 95 | } 96 | }` 97 | isWasmRouted, hookData, err := validateAndParseMemo(memo) 98 | require.True(t, isWasmRouted) 99 | require.NoError(t, err) 100 | require.Equal(t, HookData{ 101 | Message: &wasmtypes.MsgExecuteContract{ 102 | Sender: "init_addr", 103 | Contract: "contract_addr", 104 | Msg: []byte("{}"), 105 | Funds: sdk.Coins{{ 106 | Denom: "foo", 107 | Amount: math.NewInt(100), 108 | }}, 109 | }, 110 | AsyncCallback: "callback_addr", 111 | }, hookData) 112 | require.NoError(t, validateReceiver(hookData.Message, "contract_addr")) 113 | } 114 | -------------------------------------------------------------------------------- /app/indexer.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | storetypes "cosmossdk.io/store/types" 5 | 6 | dbm "github.com/cosmos/cosmos-db" 7 | 8 | servertypes "github.com/cosmos/cosmos-sdk/server/types" 9 | 10 | // kvindexer 11 | "github.com/initia-labs/initia/app/params" 12 | kvindexer "github.com/initia-labs/kvindexer" 13 | kvindexerconfig "github.com/initia-labs/kvindexer/config" 14 | blocksubmodule "github.com/initia-labs/kvindexer/submodules/block" 15 | tx "github.com/initia-labs/kvindexer/submodules/tx" 16 | nft "github.com/initia-labs/kvindexer/submodules/wasm-nft" 17 | pair "github.com/initia-labs/kvindexer/submodules/wasm-pair" 18 | kvindexermodule "github.com/initia-labs/kvindexer/x/kvindexer" 19 | kvindexerkeeper "github.com/initia-labs/kvindexer/x/kvindexer/keeper" 20 | ) 21 | 22 | func setupIndexer( 23 | app *MinitiaApp, 24 | appOpts servertypes.AppOptions, 25 | encodingConfig params.EncodingConfig, 26 | kvindexerDB dbm.DB, 27 | ) (*kvindexerkeeper.Keeper, *kvindexermodule.AppModuleBasic, *storetypes.StreamingManager, error) { 28 | // initialize the indexer keeper 29 | kvindexerConfig, err := kvindexerconfig.NewConfig(appOpts) 30 | if err != nil { 31 | return nil, nil, nil, err 32 | } 33 | kvIndexerKeeper := kvindexerkeeper.NewKeeper( 34 | app.appCodec, 35 | "wasm", 36 | kvindexerDB, 37 | kvindexerConfig, 38 | app.ac, 39 | app.vc, 40 | ) 41 | 42 | smBlock, err := blocksubmodule.NewBlockSubmodule(encodingConfig, kvIndexerKeeper, app.OPChildKeeper) 43 | if err != nil { 44 | return nil, nil, nil, err 45 | } 46 | smTx, err := tx.NewTxSubmodule(encodingConfig, kvIndexerKeeper) 47 | if err != nil { 48 | return nil, nil, nil, err 49 | } 50 | smPair, err := pair.NewPairSubmodule(encodingConfig, kvIndexerKeeper, app.IBCKeeper.ChannelKeeper, app.TransferKeeper) 51 | if err != nil { 52 | return nil, nil, nil, err 53 | } 54 | smNft, err := nft.NewWasmNFTSubmodule(app.ac, encodingConfig, kvIndexerKeeper, app.WasmKeeper, smPair) 55 | if err != nil { 56 | return nil, nil, nil, err 57 | } 58 | 59 | // order of registration matters: smPair must be registered before smNft since smNft depends on smPair 60 | err = kvIndexerKeeper.RegisterSubmodules(smBlock, smTx, smPair, smNft) 61 | if err != nil { 62 | return nil, nil, nil, err 63 | } 64 | 65 | // Add your implementation here 66 | 67 | kvIndexer, err := kvindexer.NewIndexer(app.GetBaseApp().Logger(), kvIndexerKeeper) 68 | if err != nil { 69 | return nil, nil, nil, err 70 | } else if kvIndexer == nil { 71 | return nil, nil, nil, nil 72 | } 73 | 74 | if err = kvIndexer.Validate(); err != nil { 75 | return nil, nil, nil, err 76 | } 77 | 78 | if err = kvIndexer.Prepare(nil); err != nil { 79 | return nil, nil, nil, err 80 | } 81 | 82 | if err = kvIndexerKeeper.Seal(); err != nil { 83 | return nil, nil, nil, err 84 | } 85 | 86 | if err = kvIndexer.Start(nil); err != nil { 87 | return nil, nil, nil, err 88 | } 89 | 90 | kvIndexerModule := kvindexermodule.NewAppModuleBasic(kvIndexerKeeper) 91 | streamingManager := storetypes.StreamingManager{ 92 | ABCIListeners: []storetypes.ABCIListener{kvIndexer}, 93 | StopNodeOnErr: true, 94 | } 95 | 96 | return kvIndexerKeeper, &kvIndexerModule, &streamingManager, nil 97 | } 98 | -------------------------------------------------------------------------------- /app/keepers/community_pool.go: -------------------------------------------------------------------------------- 1 | package keepers 2 | 3 | import ( 4 | "context" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | type bankKeeperForCommunityPoolKeeper interface { 10 | SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error 11 | } 12 | 13 | type CommunityPoolKeeper struct { 14 | bk bankKeeperForCommunityPoolKeeper 15 | feeCollectorName string 16 | } 17 | 18 | func NewCommunityPoolKeeper(bk bankKeeperForCommunityPoolKeeper, feeCollectorName string) CommunityPoolKeeper { 19 | return CommunityPoolKeeper{ 20 | bk: bk, 21 | feeCollectorName: feeCollectorName, 22 | } 23 | } 24 | 25 | func (k CommunityPoolKeeper) FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error { 26 | return k.bk.SendCoinsFromAccountToModule(ctx, sender, k.feeCollectorName, amount) 27 | } 28 | -------------------------------------------------------------------------------- /app/keepers/keys.go: -------------------------------------------------------------------------------- 1 | package keepers 2 | 3 | import ( 4 | // cosmos mod imports 5 | storetypes "cosmossdk.io/store/types" 6 | "cosmossdk.io/x/feegrant" 7 | upgradetypes "cosmossdk.io/x/upgrade/types" 8 | 9 | // cosmos imports 10 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 11 | authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper" 12 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 13 | consensusparamtypes "github.com/cosmos/cosmos-sdk/x/consensus/types" 14 | crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types" 15 | "github.com/cosmos/cosmos-sdk/x/group" 16 | 17 | // ibc imports 18 | packetforwardtypes "github.com/cosmos/ibc-apps/middleware/packet-forward-middleware/v8/packetforward/types" 19 | ratelimittypes "github.com/cosmos/ibc-apps/modules/rate-limiting/v8/types" 20 | capabilitytypes "github.com/cosmos/ibc-go/modules/capability/types" 21 | icacontrollertypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/controller/types" 22 | icahosttypes "github.com/cosmos/ibc-go/v8/modules/apps/27-interchain-accounts/host/types" 23 | ibcfeetypes "github.com/cosmos/ibc-go/v8/modules/apps/29-fee/types" 24 | ibctransfertypes "github.com/cosmos/ibc-go/v8/modules/apps/transfer/types" 25 | ibcexported "github.com/cosmos/ibc-go/v8/modules/core/exported" 26 | 27 | // initia imports 28 | ibchookstypes "github.com/initia-labs/initia/x/ibc-hooks/types" 29 | icaauthtypes "github.com/initia-labs/initia/x/intertx/types" 30 | 31 | // OPinit imports 32 | opchildtypes "github.com/initia-labs/OPinit/x/opchild/types" 33 | 34 | // skip imports 35 | auctiontypes "github.com/skip-mev/block-sdk/v2/x/auction/types" 36 | marketmaptypes "github.com/skip-mev/connect/v2/x/marketmap/types" 37 | oracletypes "github.com/skip-mev/connect/v2/x/oracle/types" 38 | 39 | // CosmWasm imports 40 | wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" 41 | 42 | // local imports 43 | tokenfactorytypes "github.com/initia-labs/miniwasm/x/tokenfactory/types" 44 | 45 | // noble forwarding keeper 46 | forwardingtypes "github.com/noble-assets/forwarding/v2/types" 47 | ) 48 | 49 | func (appKeepers *AppKeepers) GenerateKeys() { 50 | // Define what keys will be used in the cosmos-sdk key/value store. 51 | // Cosmos-SDK modules each have a "key" that allows the application to reference what they've stored on the chain. 52 | appKeepers.keys = storetypes.NewKVStoreKeys( 53 | authtypes.StoreKey, banktypes.StoreKey, group.StoreKey, consensusparamtypes.StoreKey, 54 | crisistypes.StoreKey, ibcexported.StoreKey, upgradetypes.StoreKey, ibctransfertypes.StoreKey, 55 | capabilitytypes.StoreKey, authzkeeper.StoreKey, feegrant.StoreKey, 56 | icahosttypes.StoreKey, icacontrollertypes.StoreKey, icaauthtypes.StoreKey, 57 | ibcfeetypes.StoreKey, wasmtypes.StoreKey, opchildtypes.StoreKey, 58 | auctiontypes.StoreKey, packetforwardtypes.StoreKey, oracletypes.StoreKey, 59 | tokenfactorytypes.StoreKey, ibchookstypes.StoreKey, forwardingtypes.StoreKey, 60 | marketmaptypes.StoreKey, ratelimittypes.StoreKey, 61 | ) 62 | 63 | // Define transient store keys 64 | appKeepers.tkeys = storetypes.NewTransientStoreKeys(forwardingtypes.TransientStoreKey) 65 | 66 | // MemKeys are for information that is stored only in RAM. 67 | appKeepers.memKeys = storetypes.NewMemoryStoreKeys(capabilitytypes.MemStoreKey) 68 | } 69 | 70 | func (appKeepers *AppKeepers) GetKVStoreKey() map[string]*storetypes.KVStoreKey { 71 | return appKeepers.keys 72 | } 73 | 74 | func (appKeepers *AppKeepers) GetTransientStoreKey() map[string]*storetypes.TransientStoreKey { 75 | return appKeepers.tkeys 76 | } 77 | 78 | func (appKeepers *AppKeepers) GetMemoryStoreKey() map[string]*storetypes.MemoryStoreKey { 79 | return appKeepers.memKeys 80 | } 81 | 82 | // GetKey returns the KVStoreKey for the provided store key. 83 | // 84 | // NOTE: This is solely to be used for testing purposes. 85 | func (appKeepers *AppKeepers) GetKey(storeKey string) *storetypes.KVStoreKey { 86 | return appKeepers.keys[storeKey] 87 | } 88 | 89 | // GetTKey returns the TransientStoreKey for the provided store key. 90 | // 91 | // NOTE: This is solely to be used for testing purposes. 92 | func (appKeepers *AppKeepers) GetTKey(storeKey string) *storetypes.TransientStoreKey { 93 | return appKeepers.tkeys[storeKey] 94 | } 95 | 96 | // GetMemKey returns the MemStoreKey for the provided mem key. 97 | // 98 | // NOTE: This is solely used for testing purposes. 99 | func (appKeepers *AppKeepers) GetMemKey(storeKey string) *storetypes.MemoryStoreKey { 100 | return appKeepers.memKeys[storeKey] 101 | } 102 | -------------------------------------------------------------------------------- /app/test_helpers.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | // DONTCOVER 4 | 5 | import ( 6 | "encoding/json" 7 | "time" 8 | 9 | "cosmossdk.io/log" 10 | abci "github.com/cometbft/cometbft/abci/types" 11 | tmproto "github.com/cometbft/cometbft/proto/tendermint/types" 12 | tmtypes "github.com/cometbft/cometbft/types" 13 | dbm "github.com/cosmos/cosmos-db" 14 | 15 | codectypes "github.com/cosmos/cosmos-sdk/codec/types" 16 | cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" 17 | "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" 18 | "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" 19 | sdk "github.com/cosmos/cosmos-sdk/types" 20 | authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" 21 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 22 | 23 | opchildtypes "github.com/initia-labs/OPinit/x/opchild/types" 24 | "github.com/initia-labs/miniwasm/types" 25 | 26 | wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" 27 | ) 28 | 29 | // defaultConsensusParams defines the default Tendermint consensus params used in 30 | // MinitiaApp testing. 31 | var defaultConsensusParams = &tmproto.ConsensusParams{ 32 | Block: &tmproto.BlockParams{ 33 | MaxBytes: 8000000, 34 | MaxGas: 1234000000, 35 | }, 36 | Evidence: &tmproto.EvidenceParams{ 37 | MaxAgeNumBlocks: 302400, 38 | MaxAgeDuration: 504 * time.Hour, // 3 weeks is the max duration 39 | MaxBytes: 10000, 40 | }, 41 | Validator: &tmproto.ValidatorParams{ 42 | PubKeyTypes: []string{ 43 | tmtypes.ABCIPubKeyTypeEd25519, 44 | }, 45 | }, 46 | } 47 | 48 | func getOrCreateMemDB(db *dbm.DB) dbm.DB { 49 | if db != nil { 50 | return *db 51 | } 52 | 53 | return dbm.NewMemDB() 54 | } 55 | 56 | func setup(homeDir string, db *dbm.DB, withGenesis bool) (*MinitiaApp, GenesisState) { 57 | encCdc := MakeEncodingConfig() 58 | app := NewMinitiaApp( 59 | log.NewNopLogger(), 60 | getOrCreateMemDB(db), 61 | getOrCreateMemDB(nil), 62 | nil, 63 | true, 64 | []wasmkeeper.Option{}, 65 | EmptyAppOptions{homeDir: homeDir}, 66 | ) 67 | 68 | if withGenesis { 69 | return app, NewDefaultGenesisState(encCdc.Codec, app.BasicModuleManager, types.BaseDenom) 70 | } 71 | 72 | return app, GenesisState{} 73 | } 74 | 75 | // SetupWithGenesisAccounts setup initiaapp with genesis account 76 | func SetupWithGenesisAccounts( 77 | homeDir string, 78 | valSet *tmtypes.ValidatorSet, 79 | genAccs []authtypes.GenesisAccount, 80 | balances ...banktypes.Balance, 81 | ) *MinitiaApp { 82 | app, genesisState := setup(homeDir, nil, true) 83 | 84 | if len(genAccs) == 0 { 85 | privAcc := secp256k1.GenPrivKey() 86 | genAccs = []authtypes.GenesisAccount{ 87 | authtypes.NewBaseAccount(privAcc.PubKey().Address().Bytes(), privAcc.PubKey(), 0, 0), 88 | } 89 | } 90 | 91 | // set genesis accounts 92 | authGenesis := authtypes.NewGenesisState(authtypes.DefaultParams(), genAccs) 93 | genesisState[authtypes.ModuleName] = app.AppCodec().MustMarshalJSON(authGenesis) 94 | 95 | // allow empty validator 96 | if valSet == nil || len(valSet.Validators) == 0 { 97 | privVal := ed25519.GenPrivKey() 98 | pubKey, err := cryptocodec.ToTmPubKeyInterface(privVal.PubKey()) //nolint:staticcheck 99 | if err != nil { 100 | panic(err) 101 | } 102 | 103 | validator := tmtypes.NewValidator(pubKey, 1) 104 | valSet = tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) 105 | } 106 | 107 | validators := make([]opchildtypes.Validator, 0, len(valSet.Validators)) 108 | for _, val := range valSet.Validators { 109 | pk, err := cryptocodec.FromTmPubKeyInterface(val.PubKey) //nolint:staticcheck 110 | if err != nil { 111 | panic(err) 112 | } 113 | pkAny, err := codectypes.NewAnyWithValue(pk) 114 | if err != nil { 115 | panic(err) 116 | } 117 | 118 | validator := opchildtypes.Validator{ 119 | Moniker: "test-validator", 120 | OperatorAddress: sdk.ValAddress(val.Address).String(), 121 | ConsensusPubkey: pkAny, 122 | ConsPower: 1, 123 | } 124 | 125 | validators = append(validators, validator) 126 | } 127 | 128 | // set validators and delegations 129 | var opchildGenesis opchildtypes.GenesisState 130 | app.AppCodec().MustUnmarshalJSON(genesisState[opchildtypes.ModuleName], &opchildGenesis) 131 | opchildGenesis.Params.Admin = sdk.AccAddress(valSet.Validators[0].Address.Bytes()).String() 132 | opchildGenesis.Params.BridgeExecutors = []string{sdk.AccAddress(valSet.Validators[0].Address.Bytes()).String()} 133 | 134 | // set validators and delegations 135 | opchildGenesis = *opchildtypes.NewGenesisState(opchildGenesis.Params, validators, nil) 136 | genesisState[opchildtypes.ModuleName] = app.AppCodec().MustMarshalJSON(&opchildGenesis) 137 | 138 | // update total supply 139 | bankGenesis := banktypes.NewGenesisState(banktypes.DefaultGenesisState().Params, balances, sdk.NewCoins(), []banktypes.Metadata{}, []banktypes.SendEnabled{}) 140 | genesisState[banktypes.ModuleName] = app.AppCodec().MustMarshalJSON(bankGenesis) 141 | 142 | stateBytes, err := json.MarshalIndent(genesisState, "", " ") 143 | if err != nil { 144 | panic(err) 145 | } 146 | 147 | _, err = app.InitChain( 148 | &abci.RequestInitChain{ 149 | Validators: []abci.ValidatorUpdate{}, 150 | ConsensusParams: defaultConsensusParams, 151 | AppStateBytes: stateBytes, 152 | }, 153 | ) 154 | if err != nil { 155 | panic(err) 156 | } 157 | 158 | _, err = app.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1}) 159 | if err != nil { 160 | panic(err) 161 | } 162 | 163 | _, err = app.Commit() 164 | if err != nil { 165 | panic(err) 166 | } 167 | 168 | return app 169 | } 170 | -------------------------------------------------------------------------------- /app/upgrade.go: -------------------------------------------------------------------------------- 1 | package app 2 | 3 | import ( 4 | "context" 5 | 6 | upgradetypes "cosmossdk.io/x/upgrade/types" 7 | 8 | "github.com/cosmos/cosmos-sdk/types/module" 9 | 10 | opchildtypes "github.com/initia-labs/OPinit/x/opchild/types" 11 | ) 12 | 13 | const upgradeName = "v1.0.2" 14 | 15 | // RegisterUpgradeHandlers returns upgrade handlers 16 | func (app *MinitiaApp) RegisterUpgradeHandlers(cfg module.Configurator) { 17 | app.UpgradeKeeper.SetUpgradeHandler( 18 | upgradeName, 19 | func(ctx context.Context, _ upgradetypes.Plan, vm module.VersionMap) (module.VersionMap, error) { 20 | params, err := app.OPChildKeeper.GetParams(ctx) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | // set non-zero default values for new params 26 | if params.HookMaxGas == 0 { 27 | params.HookMaxGas = opchildtypes.DefaultHookMaxGas 28 | 29 | err = app.OPChildKeeper.SetParams(ctx, params) 30 | if err != nil { 31 | return nil, err 32 | } 33 | } 34 | 35 | return app.ModuleManager.RunMigrations(ctx, cfg, vm) 36 | }, 37 | ) 38 | } 39 | -------------------------------------------------------------------------------- /app/wasmtesting/contracts/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --release --target wasm32-unknown-unknown" 3 | wasm-debug = "build --target wasm32-unknown-unknown" 4 | unit-test = "test --lib" 5 | schema = "run --example schema" 6 | 7 | [build] 8 | rustflags = ["-C", "link-args=-s"] -------------------------------------------------------------------------------- /app/wasmtesting/contracts/.gitignore: -------------------------------------------------------------------------------- 1 | # macOS 2 | .DS_Store 3 | 4 | # Text file backups 5 | **/*.rs.bk 6 | 7 | # Build results 8 | target/ 9 | 10 | # IDEs 11 | .vscode/ 12 | .idea/ 13 | *.iml 14 | -------------------------------------------------------------------------------- /app/wasmtesting/contracts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "connect" 3 | version = "0.0.1" 4 | authors = ["alapc"] 5 | edition = "2021" 6 | description = "" 7 | license = "" 8 | repository = "" 9 | build = "build.rs" 10 | 11 | [lib] 12 | crate-type = ["cdylib", "rlib"] 13 | 14 | [features] 15 | # use library feature to disable all instantiate/execute/query exports 16 | library = [] 17 | 18 | [dependencies] 19 | cosmwasm-std = { version = "2.0.4", features = ["stargate"] } 20 | schemars = "0.8.1" 21 | serde = { version = "1.0.103", default-features = false, features = ["derive"] } 22 | thiserror = { version = "1.0.23" } 23 | protobuf = { version = "3.4", features = ["with-bytes"] } 24 | 25 | [dev-dependencies] 26 | cosmwasm-schema = { version = "2.0.4" } 27 | 28 | [build-dependencies] 29 | protobuf-codegen = "3.4" 30 | protoc-bin-vendored = "3" 31 | protobuf = { version = "3.4" } 32 | -------------------------------------------------------------------------------- /app/wasmtesting/contracts/README.md: -------------------------------------------------------------------------------- 1 | # Test wasm stargate 2 | -------------------------------------------------------------------------------- /app/wasmtesting/contracts/artifacts/connect.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/app/wasmtesting/contracts/artifacts/connect.wasm -------------------------------------------------------------------------------- /app/wasmtesting/contracts/build.rs: -------------------------------------------------------------------------------- 1 | use protobuf_codegen::{Codegen, Customize, CustomizeCallback}; 2 | use protoc_bin_vendored; 3 | use protobuf::descriptor::field_descriptor_proto::Type; 4 | use protobuf::reflect::FieldDescriptor; 5 | use protobuf::reflect::MessageDescriptor; 6 | 7 | fn main() { 8 | struct GenSerde; 9 | 10 | impl CustomizeCallback for GenSerde { 11 | fn message(&self, _message: &MessageDescriptor) -> Customize { 12 | Customize::default().before("#[derive(::serde::Serialize, ::serde::Deserialize)]") 13 | } 14 | 15 | fn field(&self, field: &FieldDescriptor) -> Customize { 16 | if field.proto().type_() == Type::TYPE_ENUM { 17 | // `EnumOrUnknown` is not a part of rust-protobuf, so external serializer is needed. 18 | Customize::default().before( 19 | "#[serde(serialize_with = \"crate::serialize_enum_or_unknown\", deserialize_with = \"crate::deserialize_enum_or_unknown\")]") 20 | } else { 21 | Customize::default() 22 | } 23 | } 24 | 25 | fn special_field(&self, _message: &MessageDescriptor, _field: &str) -> Customize { 26 | Customize::default().before("#[serde(skip)]") 27 | } 28 | } 29 | 30 | Codegen::new() 31 | // Use `protoc` parser, optional. 32 | .protoc() 33 | // Use `protoc-bin-vendored` bundled protoc command, optional. 34 | .protoc_path(&protoc_bin_vendored::protoc_bin_path().unwrap()) 35 | // All inputs and imports from the inputs must reside in `includes` directories. 36 | .includes(&["src/protos"]) 37 | // Inputs must reside in some of include paths. 38 | .input("src/protos/connect.proto") 39 | // Specify output directory relative to Cargo output directory. 40 | .out_dir("src/") 41 | .customize_callback(GenSerde) 42 | .run_from_script(); 43 | } 44 | -------------------------------------------------------------------------------- /app/wasmtesting/contracts/mod.rs: -------------------------------------------------------------------------------- 1 | // @generated 2 | 3 | pub mod connect; 4 | -------------------------------------------------------------------------------- /app/wasmtesting/contracts/src/execute.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, StdResult}; 2 | use crate::msgs::{InstantiateMsg, ExecuteMsg}; 3 | use crate::state::Contract; 4 | 5 | impl<'a> Contract { 6 | pub fn instantiate( 7 | &self, 8 | _deps: DepsMut, 9 | _env: Env, 10 | _info: MessageInfo, 11 | _msg: InstantiateMsg 12 | ) -> StdResult { 13 | Ok(Response::new()) 14 | } 15 | 16 | pub fn execute( 17 | &self, 18 | _deps: DepsMut, 19 | _env: Env, 20 | _info: MessageInfo, 21 | msg: ExecuteMsg 22 | ) -> StdResult { 23 | match msg { 24 | ExecuteMsg::Foo {} 25 | => Ok(Response::new()) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/wasmtesting/contracts/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod execute; 2 | mod state; 3 | mod msgs; 4 | mod query; 5 | mod connect; 6 | 7 | use crate::msgs::{InstantiateMsg, ExecuteMsg, 8 | // QueryMsg, MigrateMsg 9 | }; 10 | use crate::state::Contract; 11 | 12 | #[cfg(not(feature = "library"))] 13 | pub mod entry { 14 | use self::msgs::QueryMsg; 15 | 16 | use super::*; 17 | 18 | use cosmwasm_std::{entry_point, Binary, Deps, Empty}; 19 | use cosmwasm_std::{DepsMut, Env, MessageInfo, Response, StdResult}; 20 | 21 | 22 | #[entry_point] 23 | pub fn instantiate( 24 | deps: DepsMut, 25 | env: Env, 26 | info: MessageInfo, 27 | msg: InstantiateMsg, 28 | ) -> StdResult { 29 | let tract = Contract::default(); 30 | tract.instantiate(deps, env, info, msg) 31 | } 32 | 33 | #[entry_point] 34 | pub fn execute( 35 | deps: DepsMut, 36 | env: Env, 37 | info: MessageInfo, 38 | msg: ExecuteMsg, 39 | ) -> StdResult { 40 | let tract = Contract::default(); 41 | tract.execute(deps, env, info, msg) 42 | } 43 | 44 | #[entry_point] 45 | pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> StdResult { 46 | let tract = Contract::default(); 47 | tract.query(deps, env, msg) 48 | } 49 | 50 | #[entry_point] 51 | pub fn migrate(_deps: DepsMut, _env: Env, _msg: Empty) -> StdResult { 52 | Ok(Response::new()) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/wasmtesting/contracts/src/mod.rs: -------------------------------------------------------------------------------- 1 | // @generated 2 | 3 | pub mod connect; 4 | -------------------------------------------------------------------------------- /app/wasmtesting/contracts/src/msgs.rs: -------------------------------------------------------------------------------- 1 | use schemars::JsonSchema; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | 5 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 6 | pub struct InstantiateMsg {} 7 | 8 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 9 | #[serde(rename_all = "snake_case")] 10 | pub enum ExecuteMsg { 11 | Foo {}, 12 | } 13 | 14 | #[derive(Serialize, Deserialize, Clone, PartialEq, JsonSchema, Debug)] 15 | #[serde(rename_all = "snake_case")] 16 | pub enum QueryMsg { 17 | // GetPrice { 18 | // pair_id: String 19 | // }, 20 | GetAllCurrencyPairs {}, 21 | } 22 | -------------------------------------------------------------------------------- /app/wasmtesting/contracts/src/protos/connect.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | message GetAllCurrencyPairsRequest {} 4 | message GetAllCurrencyPairsResponse { 5 | repeated CurrencyPair currency_pairs = 1; 6 | } 7 | 8 | message CurrencyPair { 9 | string Base = 1; 10 | string Quote = 2; 11 | } 12 | -------------------------------------------------------------------------------- /app/wasmtesting/contracts/src/query.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::{to_json_binary, Binary, Deps, Empty, Env, QueryRequest, StdResult}; 2 | 3 | use crate::msgs::QueryMsg; 4 | use crate::connect::{GetAllCurrencyPairsRequest, GetAllCurrencyPairsResponse}; 5 | use crate::state::Contract; 6 | use protobuf::Message; 7 | 8 | impl<'a> Contract { 9 | fn get_all_currency_pairs( 10 | &self, 11 | deps: Deps, 12 | _env: Env, 13 | ) -> StdResult { 14 | let request = GetAllCurrencyPairsRequest { 15 | special_fields: ::protobuf::SpecialFields::new(), 16 | }; 17 | let bytes = request.write_to_bytes().unwrap(); 18 | 19 | let data = Binary::from(bytes); 20 | let request = QueryRequest::::Stargate { 21 | path: "/connect.oracle.v2.Query/GetAllCurrencyPairs".to_string(), 22 | data, 23 | }; 24 | let response: GetAllCurrencyPairsResponse = deps.querier.query(&request)?; 25 | Ok(response) 26 | } 27 | } 28 | 29 | impl<'a> Contract { 30 | pub fn query(&self, deps: Deps, env: Env, msg: QueryMsg) -> StdResult { 31 | match msg { 32 | // QueryMsg::GetPrice { pair_id } => to_json_binary(&self.get_price(deps, env, pair_id)?), 33 | // QueryMsg::GetPriceRaw { pair_id } => { 34 | // to_json_binary(&self.get_price_raw(deps, env, pair_id)?) 35 | // } 36 | QueryMsg::GetAllCurrencyPairs {} => { 37 | to_json_binary(&self.get_all_currency_pairs(deps, env)?) 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/wasmtesting/contracts/src/state.rs: -------------------------------------------------------------------------------- 1 | pub struct Contract {} 2 | 3 | impl Default for Contract { 4 | fn default() -> Self { 5 | Self::new() 6 | } 7 | } 8 | 9 | impl<'a> Contract { 10 | fn new() -> Self { 11 | Self {} 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/wasmtesting/stargate_test.go: -------------------------------------------------------------------------------- 1 | package wasm_hooks_test 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" 8 | wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" 9 | 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | 12 | "github.com/stretchr/testify/require" 13 | 14 | connecttypes "github.com/skip-mev/connect/v2/pkg/types" 15 | ) 16 | 17 | func Test_StargateQuery(t *testing.T) { 18 | ctx, input := createDefaultTestInput(t) 19 | _, _, addr := keyPubAddr() 20 | 21 | code, err := os.ReadFile("./contracts/artifacts/connect.wasm") 22 | require.NoError(t, err) 23 | 24 | wasmMsgServer := wasmkeeper.NewMsgServerImpl(&input.WasmKeeper) 25 | storeRes, err := wasmMsgServer.StoreCode(ctx, &wasmtypes.MsgStoreCode{ 26 | Sender: addr.String(), 27 | WASMByteCode: code, 28 | }) 29 | require.NoError(t, err) 30 | 31 | instantiateRes, err := wasmMsgServer.InstantiateContract(ctx, &wasmtypes.MsgInstantiateContract{ 32 | Sender: addr.String(), 33 | Admin: addr.String(), 34 | CodeID: storeRes.CodeID, 35 | Label: "connect", 36 | Msg: []byte("{}"), 37 | Funds: nil, 38 | }) 39 | require.NoError(t, err) 40 | 41 | contractAddrBech32 := instantiateRes.Address 42 | contractAddr, err := sdk.AccAddressFromBech32(contractAddrBech32) 43 | require.NoError(t, err) 44 | 45 | err = input.OracleKeeper.CreateCurrencyPair(ctx, connecttypes.CurrencyPair{ 46 | Base: "BITCOIN", 47 | Quote: "USD", 48 | }) 49 | require.NoError(t, err) 50 | 51 | res, err := input.WasmKeeper.QuerySmart(ctx, contractAddr, []byte(`{"get_all_currency_pairs": {}}`)) 52 | require.NoError(t, err) 53 | require.Equal(t, "{\"currency_pairs\":[{\"Base\":\"BITCOIN\",\"Quote\":\"USD\"}]}", string(res)) 54 | } 55 | -------------------------------------------------------------------------------- /client/docs/statik/init.go: -------------------------------------------------------------------------------- 1 | package statik 2 | 3 | //This just for fixing the error in importing empty github.com/cosmos/cosmos-sdk/client/lcd/statik 4 | -------------------------------------------------------------------------------- /client/docs/swagger-ui/.!5159!favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/client/docs/swagger-ui/.!5159!favicon-16x16.png -------------------------------------------------------------------------------- /client/docs/swagger-ui/.!5242!favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/client/docs/swagger-ui/.!5242!favicon-16x16.png -------------------------------------------------------------------------------- /client/docs/swagger-ui/.!5261!favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/client/docs/swagger-ui/.!5261!favicon-16x16.png -------------------------------------------------------------------------------- /client/docs/swagger-ui/.!5276!favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/client/docs/swagger-ui/.!5276!favicon-16x16.png -------------------------------------------------------------------------------- /client/docs/swagger-ui/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/client/docs/swagger-ui/favicon-16x16.png -------------------------------------------------------------------------------- /client/docs/swagger-ui/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/client/docs/swagger-ui/favicon-32x32.png -------------------------------------------------------------------------------- /client/docs/swagger-ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Swagger UI 7 | 8 | 9 | 10 | 31 | 32 | 33 | 34 |
35 | 36 | 37 | 38 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /client/docs/swagger-ui/oauth2-redirect.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 68 | -------------------------------------------------------------------------------- /client/docs/swagger-ui/swagger-ui.css.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":[],"names":[],"mappings":"","file":"swagger-ui.css","sourceRoot":""} -------------------------------------------------------------------------------- /cmd/minitiad/config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | 7 | wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" 8 | tmcfg "github.com/cometbft/cometbft/config" 9 | indexerconfig "github.com/initia-labs/kvindexer/config" 10 | 11 | serverconfig "github.com/cosmos/cosmos-sdk/server/config" 12 | 13 | "github.com/initia-labs/miniwasm/types" 14 | ) 15 | 16 | // minitiaAppConfig initia specify app config 17 | type minitiaAppConfig struct { 18 | serverconfig.Config 19 | WasmConfig wasmtypes.WasmConfig `mapstructure:"wasm"` 20 | IndexerConfig indexerconfig.IndexerConfig `mapstructure:"indexer"` 21 | } 22 | 23 | // initAppConfig helps to override default appConfig template and configs. 24 | // return "", nil if no custom configuration is required for the application. 25 | func initAppConfig() (string, interface{}) { 26 | // Optionally allow the chain developer to overwrite the SDK's default 27 | // server config. 28 | srvCfg := serverconfig.DefaultConfig() 29 | 30 | // The SDK's default minimum gas price is set to "" (empty value) inside 31 | // app.toml. If left empty by validators, the node will halt on startup. 32 | // However, the chain developer can set a default app.toml value for their 33 | // validators here. 34 | // 35 | // In summary: 36 | // - if you leave srvCfg.MinGasPrices = "", all validators MUST tweak their 37 | // own app.toml config, 38 | // - if you set srvCfg.MinGasPrices non-empty, validators CAN tweak their 39 | // own app.toml to override, or use this default value. 40 | // 41 | // In simapp, we set the min gas prices to 0. 42 | srvCfg.MinGasPrices = fmt.Sprintf("0%s", types.BaseDenom) 43 | srvCfg.Mempool.MaxTxs = 2000 44 | srvCfg.QueryGasLimit = 10_000_000 45 | srvCfg.InterBlockCache = false 46 | 47 | // Enable API and unsafe CORS (CORS allowed from any host) 48 | srvCfg.API.Enable = true 49 | srvCfg.API.Swagger = true 50 | srvCfg.API.EnableUnsafeCORS = true 51 | srvCfg.API.Address = "tcp://0.0.0.0:1317" 52 | 53 | srvCfg.GRPC.Enable = true 54 | srvCfg.GRPC.Address = "0.0.0.0:9090" 55 | 56 | wasmCfg := wasmtypes.DefaultWasmConfig() 57 | wasmCfg.SmartQueryGasLimit = 10_000_000 58 | wasmCfg.SimulationGasLimit = &wasmCfg.SmartQueryGasLimit 59 | 60 | minitiaAppConfig := minitiaAppConfig{ 61 | Config: *srvCfg, 62 | WasmConfig: wasmCfg, 63 | IndexerConfig: indexerconfig.DefaultConfig(), 64 | } 65 | 66 | minitiaAppTemplate := serverconfig.DefaultConfigTemplate + 67 | wasmtypes.DefaultConfigTemplate() + indexerconfig.DefaultConfigTemplate 68 | 69 | return minitiaAppTemplate, minitiaAppConfig 70 | } 71 | 72 | // initTendermintConfig helps to override default Tendermint Config values. 73 | // return tmcfg.DefaultConfig if no custom configuration is required for the application. 74 | func initTendermintConfig() *tmcfg.Config { 75 | cfg := tmcfg.DefaultConfig() 76 | 77 | // empty block configure 78 | cfg.Consensus.CreateEmptyBlocks = false 79 | cfg.Consensus.CreateEmptyBlocksInterval = time.Minute 80 | 81 | // rpc configure 82 | cfg.RPC.ListenAddress = "tcp://0.0.0.0:26657" 83 | cfg.RPC.CORSAllowedOrigins = []string{"*"} 84 | 85 | // performance turning configs 86 | cfg.P2P.SendRate = 20480000 87 | cfg.P2P.RecvRate = 20480000 88 | cfg.P2P.MaxPacketMsgPayloadSize = 1000000 // 1MB 89 | cfg.P2P.FlushThrottleTimeout = 10 * time.Millisecond 90 | cfg.Consensus.PeerGossipSleepDuration = 30 * time.Millisecond 91 | 92 | // mempool configs 93 | cfg.Mempool.Size = 1000 94 | cfg.Mempool.MaxTxsBytes = 10737418240 95 | cfg.Mempool.MaxTxBytes = 2048576 96 | 97 | // propose timeout to 1s 98 | cfg.Consensus.TimeoutPropose = 1 * time.Second 99 | cfg.Consensus.TimeoutProposeDelta = 500 * time.Millisecond 100 | 101 | // do not wait straggler for prevote and precommit on l2 102 | cfg.Consensus.TimeoutPrevote = 0 * time.Millisecond 103 | cfg.Consensus.TimeoutPrevoteDelta = 0 * time.Millisecond 104 | cfg.Consensus.TimeoutPrecommit = 0 * time.Millisecond 105 | cfg.Consensus.TimeoutPrecommitDelta = 0 * time.Millisecond 106 | 107 | // commit time to 0.5s 108 | cfg.Consensus.TimeoutCommit = 500 * time.Millisecond 109 | 110 | return cfg 111 | } 112 | -------------------------------------------------------------------------------- /cmd/minitiad/launch.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper" 7 | wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types" 8 | 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | "github.com/cosmos/cosmos-sdk/types/module" 11 | 12 | "github.com/initia-labs/OPinit/contrib/launchtools" 13 | "github.com/initia-labs/OPinit/contrib/launchtools/steps" 14 | 15 | "github.com/initia-labs/initia/app/params" 16 | minitiaapp "github.com/initia-labs/miniwasm/app" 17 | 18 | "github.com/initia-labs/miniwasm/contrib" 19 | 20 | "github.com/pkg/errors" 21 | "github.com/spf13/cobra" 22 | ) 23 | 24 | // DefaultLaunchStepFactories is a list of default launch step factories. 25 | var DefaultLaunchStepFactories = []launchtools.LauncherStepFuncFactory[*launchtools.Config]{ 26 | steps.InitializeConfig, 27 | steps.InitializeRPCHelpers, 28 | 29 | // Initialize genesis 30 | steps.InitializeGenesis, 31 | 32 | // Add system keys to the keyring 33 | steps.InitializeKeyring, 34 | 35 | // Run the app 36 | steps.RunApp, 37 | 38 | // MINIWASM: Store/Instantiate cw721 and ics721 contracts 39 | StoreAndInstantiateNFTContracts, 40 | 41 | // Establish IBC channels for fungible and NFT transfer 42 | // MINIWASM: Use wasm contract addresses for srcPort, dstPort, channelVersion 43 | steps.EstablishIBCChannelsWithNFTTransfer(func() (string, string, string) { 44 | return "wasm." + wasmkeeper.BuildContractAddressClassic(2, 1).String(), 45 | "nft-transfer", 46 | "ics721-1" 47 | }), 48 | 49 | // Create OP Bridge, using open channel states 50 | steps.InitializeOpBridge, 51 | 52 | // Set bridge info and update clients 53 | steps.SetBridgeInfo, 54 | 55 | // Get the L1 and L2 heights 56 | steps.GetL1Height, 57 | steps.GetL2Height, 58 | 59 | // Cleanup 60 | steps.StopApp, 61 | } 62 | 63 | func LaunchCommand(ac *appCreator, enc params.EncodingConfig, mbm module.BasicManager) *cobra.Command { 64 | return launchtools.LaunchCmd( 65 | ac, 66 | func(denom string) map[string]json.RawMessage { 67 | return minitiaapp.NewDefaultGenesisState(enc.Codec, mbm, denom) 68 | }, 69 | DefaultLaunchStepFactories, 70 | ) 71 | } 72 | 73 | // StoreAndInstantiateNFTContracts stores and instantiates cw721 and ics721 contracts 74 | func StoreAndInstantiateNFTContracts(input *launchtools.Config) launchtools.LauncherStepFunc { 75 | return func(ctx launchtools.Launcher) error { 76 | ctx.Logger().Info("Storing and instantiating cw721 and ics721 contracts") 77 | fs := contrib.FS() 78 | 79 | cw721, err := fs.ReadFile("wasm/cw721_metadata_onchain.wasm") 80 | if err != nil { 81 | return errors.Wrapf(err, "failed to read cw721_metadata_onchain.wasm") 82 | } 83 | 84 | ics721, err := fs.ReadFile("wasm/ics721_base.wasm") 85 | if err != nil { 86 | return errors.Wrapf(err, "failed to read ics721_base.wasm") 87 | } 88 | 89 | msgs := []sdk.Msg{ 90 | &wasmtypes.MsgStoreCode{ 91 | Sender: input.SystemKeys.Validator.L2Address, 92 | WASMByteCode: cw721, 93 | InstantiatePermission: nil, 94 | }, 95 | &wasmtypes.MsgStoreCode{ 96 | Sender: input.SystemKeys.Validator.L2Address, 97 | WASMByteCode: ics721, 98 | InstantiatePermission: nil, 99 | }, 100 | &wasmtypes.MsgInstantiateContract{ 101 | Sender: input.SystemKeys.Validator.L2Address, 102 | Admin: input.SystemKeys.Validator.L2Address, 103 | CodeID: 2, 104 | Label: "ics721", 105 | Msg: []byte(`{"cw721_base_code_id":1}`), 106 | Funds: nil, 107 | }, 108 | } 109 | 110 | for i, msg := range msgs { 111 | ctx.Logger().Info( 112 | "Broadcasting tx to store and instantiate cw721 and ics721 contracts", 113 | "step", i+1, 114 | ) 115 | 116 | res, err := ctx.GetRPCHelperL2().BroadcastTxAndWait( 117 | input.SystemKeys.Validator.L2Address, 118 | input.SystemKeys.Validator.Mnemonic, 119 | 10000000, 120 | sdk.NewCoins(), 121 | msg, 122 | ) 123 | 124 | if err != nil { 125 | return errors.Wrapf(err, "failed to store and instantiate nft contracts") 126 | } 127 | 128 | ctx.Logger().Info( 129 | "Successfully stored and instantiated cw721 and ics721 contracts", 130 | "tx_hash", res.Hash, 131 | ) 132 | } 133 | 134 | return nil 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /cmd/minitiad/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" 8 | 9 | minitiaapp "github.com/initia-labs/miniwasm/app" 10 | ) 11 | 12 | func main() { 13 | rootCmd, _ := NewRootCmd() 14 | 15 | if err := svrcmd.Execute(rootCmd, minitiaapp.EnvPrefix, minitiaapp.DefaultNodeHome); err != nil { 16 | fmt.Fprintln(rootCmd.OutOrStderr(), err) 17 | os.Exit(1) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /cmd/minitiad/rollback.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "strconv" 7 | 8 | cmtcmd "github.com/cometbft/cometbft/cmd/cometbft/commands" 9 | "github.com/spf13/cobra" 10 | 11 | dbm "github.com/cosmos/cosmos-db" 12 | 13 | "github.com/cosmos/cosmos-sdk/server" 14 | "github.com/cosmos/cosmos-sdk/server/types" 15 | ) 16 | 17 | // NewMultipleRollbackCmd creates a command to rollback CometBFT and multistore state by one height. 18 | func NewMultipleRollbackCmd(appCreator types.AppCreator) *cobra.Command { 19 | removeBlock := false 20 | cmd := &cobra.Command{ 21 | Use: "mrollback [height]", 22 | Short: "rollback Cosmos SDK and CometBFT state to the given height", 23 | Long: ` 24 | A state rollback is performed to recover from an incorrect application state transition, 25 | when CometBFT has persisted an incorrect app hash and is thus unable to make 26 | progress. Rollback overwrites a state with the state at the given height. All 27 | blocks after the given height are removed from the blockchain. 28 | `, 29 | Args: cobra.ExactArgs(1), 30 | RunE: func(cmd *cobra.Command, args []string) error { 31 | ctx := server.GetServerContextFromCmd(cmd) 32 | 33 | height, err := strconv.ParseInt(args[0], 10, 64) 34 | if err != nil { 35 | return err 36 | } 37 | if height <= 0 { 38 | return fmt.Errorf("height must be greater than 0") 39 | } 40 | 41 | dataDir := filepath.Join(ctx.Config.RootDir, "data") 42 | db, err := dbm.NewDB("application", server.GetAppDBBackend(ctx.Viper), dataDir) 43 | if err != nil { 44 | return err 45 | } 46 | app := appCreator(ctx.Logger, db, nil, ctx.Viper) 47 | if curHeight := app.CommitMultiStore().LatestVersion(); height >= curHeight { 48 | return fmt.Errorf("height must be less than the current height %d", curHeight) 49 | } 50 | 51 | // rollback CometBFT state 52 | height, hash, err := cmtcmd.RollbackStateTo(ctx.Config, height, removeBlock) 53 | if err != nil { 54 | return fmt.Errorf("failed to rollback CometBFT state: %w", err) 55 | } 56 | // rollback the multistore 57 | 58 | if err := app.CommitMultiStore().RollbackToVersion(height); err != nil { 59 | return fmt.Errorf("failed to rollback to version: %w", err) 60 | } 61 | 62 | fmt.Printf("Rolled back state to height %d and hash %X\n", height, hash) 63 | return nil 64 | }, 65 | } 66 | cmd.Flags().BoolVar(&removeBlock, "hard", false, "remove blocks as well as state") 67 | return cmd 68 | } 69 | -------------------------------------------------------------------------------- /contrib/devtools/Makefile: -------------------------------------------------------------------------------- 1 | ### 2 | # Find OS and Go environment 3 | # GO contains the Go binary 4 | # FS contains the OS file separator 5 | ### 6 | ifeq ($(OS),Windows_NT) 7 | GO := $(shell where go.exe 2> NUL) 8 | FS := "\\" 9 | else 10 | GO := $(shell command -v go 2> /dev/null) 11 | FS := "/" 12 | endif 13 | 14 | ifeq ($(GO),) 15 | $(error could not find go. Is it in PATH? $(GO)) 16 | endif 17 | 18 | ############################################################################### 19 | ### Functions ### 20 | ############################################################################### 21 | 22 | go_get = $(if $(findstring Windows_NT,$(OS)),\ 23 | IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS) ( mkdir $(GITHUBDIR)$(FS)$(1) ) else (cd .) &\ 24 | IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS)$(2)$(FS) ( cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2) ) else (cd .) &\ 25 | ,\ 26 | mkdir -p $(GITHUBDIR)$(FS)$(1) &&\ 27 | (test ! -d $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2)) || true &&\ 28 | )\ 29 | cd $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && git fetch origin && git checkout -q $(3) 30 | 31 | mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST))) 32 | mkfile_dir := $(shell cd $(shell dirname $(mkfile_path)); pwd) 33 | 34 | ############################################################################### 35 | ### Tools ### 36 | ############################################################################### 37 | 38 | PREFIX ?= /usr/local 39 | BIN ?= $(PREFIX)/bin 40 | UNAME_S ?= $(shell uname -s) 41 | UNAME_M ?= $(shell uname -m) 42 | 43 | GOPATH ?= $(shell $(GO) env GOPATH) 44 | GITHUBDIR := $(GOPATH)$(FS)src$(FS)github.com 45 | 46 | BUF_VERSION ?= 0.11.0 47 | 48 | TOOLS_DESTDIR ?= $(GOPATH)/bin 49 | STATIK = $(TOOLS_DESTDIR)/statik 50 | RUNSIM = $(TOOLS_DESTDIR)/runsim 51 | 52 | tools: tools-stamp 53 | tools-stamp: statik runsim 54 | # Create dummy file to satisfy dependency and avoid 55 | # rebuilding when this Makefile target is hit twice 56 | # in a row. 57 | touch $@ 58 | 59 | # Install the runsim binary with a temporary workaround of entering an outside 60 | # directory as the "go install" command ignores the -mod option and will pollute the 61 | # go.{mod, sum} files. 62 | # 63 | # ref: https://github.com/golang/go/issues/30515 64 | statik: $(STATIK) 65 | $(STATIK): 66 | @echo "Installing statik..." 67 | @(cd /tmp && go install github.com/rakyll/statik@v0.1.6) 68 | 69 | # Install the runsim binary with a temporary workaround of entering an outside 70 | # directory as the "go install" command ignores the -mod option and will pollute the 71 | # go.{mod, sum} files. 72 | # 73 | # ref: https://github.com/golang/go/issues/30515 74 | runsim: $(RUNSIM) 75 | $(RUNSIM): 76 | @echo "Installing runsim..." 77 | @(cd /tmp && go install github.com/cosmos/tools/cmd/runsim@v1.0.0) 78 | 79 | tools-clean: 80 | rm -f $(STATIK) $(RUNSIM) 81 | rm -f tools-stamp 82 | 83 | .PHONY: tools tools-clean statik runsim 84 | -------------------------------------------------------------------------------- /contrib/devtools/dockerfile: -------------------------------------------------------------------------------- 1 | FROM bufbuild/buf:latest as BUILDER 2 | 3 | FROM golang:alpine 4 | 5 | ENV GOLANG_PROTOBUF_VERSION=1.3.5 \ 6 | GOGO_PROTOBUF_VERSION=1.3.2 \ 7 | GRPC_GATEWAY_VERSION=1.14.7 8 | 9 | 10 | RUN GO111MODULE=on go get \ 11 | github.com/golang/protobuf/protoc-gen-go@v${GOLANG_PROTOBUF_VERSION} \ 12 | github.com/gogo/protobuf/protoc-gen-gogo@v${GOGO_PROTOBUF_VERSION} \ 13 | github.com/gogo/protobuf/protoc-gen-gogofast@v${GOGO_PROTOBUF_VERSION} \ 14 | github.com/gogo/protobuf/protoc-gen-gogofaster@v${GOGO_PROTOBUF_VERSION} \ 15 | github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway@v${GRPC_GATEWAY_VERSION} \ 16 | github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger@v${GRPC_GATEWAY_VERSION} \ 17 | github.com/regen-network/cosmos-proto/protoc-gen-gocosmos@latest 18 | 19 | RUN GO111MODULE=on go get -u github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc 20 | 21 | COPY --from=BUILDER /usr/local/bin /usr/local/bin 22 | -------------------------------------------------------------------------------- /contrib/embed.go: -------------------------------------------------------------------------------- 1 | package contrib 2 | 3 | import ( 4 | "embed" 5 | ) 6 | 7 | //go:embed wasm/* 8 | var fs embed.FS 9 | 10 | // FS returns the embedded filesystem for the contrib package. 11 | func FS() embed.FS { 12 | return fs 13 | } 14 | -------------------------------------------------------------------------------- /contrib/wasm/cw721_metadata_onchain.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/contrib/wasm/cw721_metadata_onchain.wasm -------------------------------------------------------------------------------- /contrib/wasm/ics721_base.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/contrib/wasm/ics721_base.wasm -------------------------------------------------------------------------------- /proto/README.md: -------------------------------------------------------------------------------- 1 | # Maintaining Initia Proto Files 2 | 3 | All of the Initia proto files are defined here. This folder should 4 | be synced regularly with buf.build/initia-labs/initia regularly by 5 | a maintainer by running `buf push` in this folder. 6 | 7 | User facing documentation should not be placed here but instead goes in 8 | `buf.md` and in each protobuf package following the guidelines in 9 | . 10 | 11 | ## Generate 12 | 13 | To get the Initia proto given a commit, run: 14 | 15 | ```bash 16 | buf export buf.build/initia-labs/initia:${commit} --output . 17 | ``` 18 | -------------------------------------------------------------------------------- /proto/buf.gen.gogo.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | plugins: 3 | - name: gocosmos 4 | out: .. 5 | opt: plugins=grpc,Mgoogle/protobuf/any.proto=github.com/cosmos/cosmos-sdk/codec/types 6 | - name: grpc-gateway 7 | out: .. 8 | opt: logtostderr=true,allow_colon_final_segments=true -------------------------------------------------------------------------------- /proto/buf.gen.pulsar.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | managed: 3 | enabled: true 4 | go_package_prefix: 5 | default: cosmossdk.io/api 6 | except: 7 | - buf.build/googleapis/googleapis 8 | - buf.build/cosmos/gogo-proto 9 | - buf.build/cosmos/cosmos-proto 10 | - buf.build/cosmos/ibc 11 | override: 12 | plugins: 13 | - name: go-pulsar 14 | out: ../api 15 | opt: paths=source_relative 16 | - name: go-grpc 17 | out: ../api 18 | opt: paths=source_relative 19 | -------------------------------------------------------------------------------- /proto/buf.gen.swagger.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | plugins: 3 | - name: swagger 4 | out: ../tmp-swagger-gen 5 | opt: logtostderr=true,fqn_for_swagger_name=true,simple_operation_ids=true 6 | -------------------------------------------------------------------------------- /proto/buf.lock: -------------------------------------------------------------------------------- 1 | # Generated by buf. DO NOT EDIT. 2 | version: v1 3 | deps: 4 | - remote: buf.build 5 | owner: cosmos 6 | repository: cosmos-proto 7 | commit: 1935555c206d4afb9e94615dfd0fad31 8 | digest: shake256:c74d91a3ac7ae07d579e90eee33abf9b29664047ac8816500cf22c081fec0d72d62c89ce0bebafc1f6fec7aa5315be72606717740ca95007248425102c365377 9 | - remote: buf.build 10 | owner: cosmos 11 | repository: cosmos-sdk 12 | commit: 9d547dbea90f47afbe1898388fcebffb 13 | digest: shake256:63237398fb2043153c81bbe91ce52a832bca02d4307334b62fcc9914ce6f12fea59388eb5102949255054973f7022f581e02f97ed1f69a6585d2d00fb1da5833 14 | - remote: buf.build 15 | owner: cosmos 16 | repository: gogo-proto 17 | commit: 5e5b9fdd01804356895f8f79a6f1ddc1 18 | digest: shake256:0b85da49e2e5f9ebc4806eae058e2f56096ff3b1c59d1fb7c190413dd15f45dd456f0b69ced9059341c80795d2b6c943de15b120a9e0308b499e43e4b5fc2952 19 | - remote: buf.build 20 | owner: cosmos 21 | repository: ibc 22 | commit: f559baa46fdb47b3b9fb206dec05da1d 23 | digest: shake256:712055a221430dd12ce858885c592b57e0e301dc1ed9c7d35e5e0fff8fef74499116dc2ba5f71ca6896576763c5f970bc3e970b025c7d17067d8d6a3f3b821b5 24 | - remote: buf.build 25 | owner: cosmos 26 | repository: ics23 27 | commit: 3c44d8daa8b44059ac744cd17d4a49d7 28 | digest: shake256:fed75bde09a652f2cbe6085d5e1afa8292f166a0f6314369fb60b71c189d34e893ee1bffde527373abd371c110bdc80e8ed1d534eaaf5da6bc62634903a6ec44 29 | - remote: buf.build 30 | owner: googleapis 31 | repository: googleapis 32 | commit: cc916c31859748a68fd229a3c8d7a2e8 33 | digest: shake256:469b049d0eb04203d5272062636c078decefc96fec69739159c25d85349c50c34c7706918a8b216c5c27f76939df48452148cff8c5c3ae77fa6ba5c25c1b8bf8 34 | -------------------------------------------------------------------------------- /proto/buf.md: -------------------------------------------------------------------------------- 1 | # Protobufs 2 | 3 | This is the public protocol buffers API for the [Miniwasm](https://github.com/initia-labs/miniwasm). 4 | -------------------------------------------------------------------------------- /proto/buf.yaml: -------------------------------------------------------------------------------- 1 | # This module represents buf.build/initia-labs/miniwasm 2 | version: v1 3 | name: buf.build/initia-labs/miniwasm 4 | deps: 5 | - buf.build/cosmos/cosmos-sdk 6 | - buf.build/cosmos/cosmos-proto 7 | - buf.build/cosmos/gogo-proto 8 | - buf.build/googleapis/googleapis 9 | - buf.build/cosmos/ics23 10 | - buf.build/cosmos/ibc 11 | 12 | breaking: 13 | use: 14 | - FILE 15 | lint: 16 | use: 17 | - DEFAULT 18 | - COMMENTS 19 | - FILE_LOWER_SNAKE_CASE 20 | except: 21 | - UNARY_RPC 22 | - COMMENT_FIELD 23 | - SERVICE_SUFFIX 24 | - PACKAGE_VERSION_SUFFIX 25 | - RPC_REQUEST_STANDARD_NAME 26 | -------------------------------------------------------------------------------- /proto/miniwasm/tokenfactory/v1/authority_metadata.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package miniwasm.tokenfactory.v1; 3 | 4 | import "gogoproto/gogo.proto"; 5 | 6 | option go_package = "github.com/initia-labs/miniwasm/x/tokenfactory/types"; 7 | 8 | // DenomAuthorityMetadata specifies metadata for addresses that have specific 9 | // capabilities over a token factory denom. Right now there is only one Admin 10 | // permission, but is planned to be extended to the future. 11 | message DenomAuthorityMetadata { 12 | option (gogoproto.equal) = true; 13 | 14 | // Can be empty for no admin, or a valid address 15 | string admin = 1 [ (gogoproto.moretags) = "yaml:\"admin\"" ]; 16 | } 17 | -------------------------------------------------------------------------------- /proto/miniwasm/tokenfactory/v1/genesis.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package miniwasm.tokenfactory.v1; 3 | 4 | import "gogoproto/gogo.proto"; 5 | import "miniwasm/tokenfactory/v1/authority_metadata.proto"; 6 | import "miniwasm/tokenfactory/v1/params.proto"; 7 | 8 | option go_package = "github.com/initia-labs/miniwasm/x/tokenfactory/types"; 9 | 10 | // GenesisState defines the tokenfactory module's genesis state. 11 | message GenesisState { 12 | // params defines the parameters of the module. 13 | Params params = 1 [ (gogoproto.nullable) = false ]; 14 | 15 | repeated GenesisDenom factory_denoms = 2 [ 16 | (gogoproto.moretags) = "yaml:\"factory_denoms\"", 17 | (gogoproto.nullable) = false 18 | ]; 19 | } 20 | 21 | // GenesisDenom defines a tokenfactory denom that is defined within genesis 22 | // state. The structure contains DenomAuthorityMetadata which defines the 23 | // denom's admin. 24 | message GenesisDenom { 25 | option (gogoproto.equal) = true; 26 | 27 | string denom = 1 [ (gogoproto.moretags) = "yaml:\"denom\"" ]; 28 | DenomAuthorityMetadata authority_metadata = 2 [ 29 | (gogoproto.moretags) = "yaml:\"authority_metadata\"", 30 | (gogoproto.nullable) = false 31 | ]; 32 | } 33 | -------------------------------------------------------------------------------- /proto/miniwasm/tokenfactory/v1/params.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package miniwasm.tokenfactory.v1; 3 | 4 | import "gogoproto/gogo.proto"; 5 | import "cosmos/base/v1beta1/coin.proto"; 6 | 7 | option go_package = "github.com/initia-labs/miniwasm/x/tokenfactory/types"; 8 | 9 | // Params defines the parameters for the tokenfactory module. 10 | message Params { 11 | // DenomCreationFee defines the fee to be charged on the creation of a new 12 | // denom. The fee is drawn from the MsgCreateDenom's sender account, and 13 | // transferred to the community pool. 14 | repeated cosmos.base.v1beta1.Coin denom_creation_fee = 1 [ 15 | (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins", 16 | (gogoproto.moretags) = "yaml:\"denom_creation_fee\"", 17 | (gogoproto.nullable) = false 18 | ]; 19 | 20 | // DenomCreationGasConsume defines the gas cost for creating a new denom. 21 | // This is intended as a spam deterrence mechanism. 22 | // 23 | // See: https://github.com/CosmWasm/token-factory/issues/11 24 | uint64 denom_creation_gas_consume = 2 [ 25 | (gogoproto.moretags) = "yaml:\"denom_creation_gas_consume\"", 26 | (gogoproto.nullable) = true 27 | ]; 28 | } 29 | -------------------------------------------------------------------------------- /proto/miniwasm/tokenfactory/v1/query.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package miniwasm.tokenfactory.v1; 3 | 4 | import "gogoproto/gogo.proto"; 5 | import "google/api/annotations.proto"; 6 | import "miniwasm/tokenfactory/v1/authority_metadata.proto"; 7 | import "miniwasm/tokenfactory/v1/params.proto"; 8 | 9 | option go_package = "github.com/initia-labs/miniwasm/x/tokenfactory/types"; 10 | 11 | // Query defines the gRPC querier service. 12 | service Query { 13 | // Params defines a gRPC query method that returns the tokenfactory module's 14 | // parameters. 15 | rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { 16 | option (google.api.http).get = "/miniwasm/tokenfactory/v1/params"; 17 | } 18 | 19 | // DenomAuthorityMetadata defines a gRPC query method for fetching 20 | // DenomAuthorityMetadata for a particular denom. 21 | rpc DenomAuthorityMetadata(QueryDenomAuthorityMetadataRequest) 22 | returns (QueryDenomAuthorityMetadataResponse) { 23 | option (google.api.http).get = 24 | "/miniwasm/tokenfactory/v1/denoms/{denom}/authority_metadata"; 25 | } 26 | 27 | // DenomsFromCreator defines a gRPC query method for fetching all 28 | // denominations created by a specific admin/creator. 29 | rpc DenomsFromCreator(QueryDenomsFromCreatorRequest) 30 | returns (QueryDenomsFromCreatorResponse) { 31 | option (google.api.http).get = 32 | "/miniwasm/tokenfactory/v1/denoms_from_creator/{creator}"; 33 | } 34 | 35 | // BeforeSendHookAddress defines a gRPC query method for 36 | // getting the address registered for the before send hook. 37 | rpc BeforeSendHookAddress(QueryBeforeSendHookAddressRequest) 38 | returns (QueryBeforeSendHookAddressResponse) { 39 | option (google.api.http).get = 40 | "/miniwasm/tokenfactory/v1/denoms/{denom}/before_send_hook"; 41 | } 42 | } 43 | 44 | // QueryParamsRequest is the request type for the Query/Params RPC method. 45 | message QueryParamsRequest {} 46 | 47 | // QueryParamsResponse is the response type for the Query/Params RPC method. 48 | message QueryParamsResponse { 49 | // params defines the parameters of the module. 50 | Params params = 1 [ (gogoproto.nullable) = false ]; 51 | } 52 | 53 | // QueryDenomAuthorityMetadataRequest defines the request structure for the 54 | // DenomAuthorityMetadata gRPC query. 55 | message QueryDenomAuthorityMetadataRequest { 56 | string denom = 1 [ (gogoproto.moretags) = "yaml:\"denom\"" ]; 57 | } 58 | 59 | // QueryDenomAuthorityMetadataResponse defines the response structure for the 60 | // DenomAuthorityMetadata gRPC query. 61 | message QueryDenomAuthorityMetadataResponse { 62 | DenomAuthorityMetadata authority_metadata = 1 [ 63 | (gogoproto.moretags) = "yaml:\"authority_metadata\"", 64 | (gogoproto.nullable) = false 65 | ]; 66 | } 67 | 68 | // QueryDenomsFromCreatorRequest defines the request structure for the 69 | // DenomsFromCreator gRPC query. 70 | message QueryDenomsFromCreatorRequest { 71 | string creator = 1 [ (gogoproto.moretags) = "yaml:\"creator\"" ]; 72 | } 73 | 74 | // QueryDenomsFromCreatorRequest defines the response structure for the 75 | // DenomsFromCreator gRPC query. 76 | message QueryDenomsFromCreatorResponse { 77 | repeated string denoms = 1 [ (gogoproto.moretags) = "yaml:\"denoms\"" ]; 78 | } 79 | 80 | // QueryBeforeSendHookAddressRequest defines the request structure for the 81 | // BeforeSendHookAddress gRPC query. 82 | message QueryBeforeSendHookAddressRequest { 83 | string denom = 1 [ (gogoproto.moretags) = "yaml:\"denom\"" ]; 84 | } 85 | 86 | // QueryBeforeSendHookAddressResponse defines the response structure for the 87 | // DenomBeforeSendHook gRPC query. 88 | message QueryBeforeSendHookAddressResponse { 89 | string cosmwasm_address = 1 90 | [ (gogoproto.moretags) = "yaml:\"cosmwasm_address\"" ]; 91 | } -------------------------------------------------------------------------------- /scripts/protoc-swagger-gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eo pipefail 4 | 5 | # clone dependency proto files 6 | COSMOS_URL=github.com/cosmos/cosmos-sdk 7 | IBC_URL=github.com/cosmos/ibc-go 8 | IBC_V=v8 9 | INITIA_URL=github.com/initia-labs/initia 10 | OPINIT_URL=github.com/initia-labs/OPinit 11 | COSMWASM_URL=github.com/CosmWasm/wasmd 12 | INDEXER_URL=github.com/initia-labs/kvindexer 13 | CONNECT_URL=github.com/skip-mev/connect 14 | CONNECT_V=v2 15 | BLOCK_SDK_URL=github.com/skip-mev/block-sdk 16 | BLOCK_SDK_V=v2 17 | 18 | COSMOS_SDK_VERSION=$(cat ./go.mod | grep "$COSMOS_URL v" | sed -n -e "s/^.* //p") 19 | IBC_VERSION=$(cat ./go.mod | grep "$IBC_URL/$IBC_V v" | sed -n -e "s/^.* //p") 20 | COSMWASM_VERSION=$(cat ./go.mod | grep "$COSMWASM_URL v" | sed -n -e "s/^.* //p") 21 | INITIA_VERSION=$(cat ./go.mod | grep "$INITIA_URL v" | sed -n -e "s/^.* //p") 22 | OPINIT_VERSION=$(cat ./go.mod | grep "$OPINIT_URL v" | sed -n -e "s/^.* //p") 23 | INDEXER_VERSION=$(cat ./go.mod | grep "$INDEXER_URL v" | sed -n -e "s/^.* //p") 24 | CONNECT_VERSION=$(cat ./go.mod | grep "$CONNECT_URL/$CONNECT_V v" | sed -n -e "s/^.* //p") 25 | BLOCK_SDK_VERSION=$(cat ./go.mod | grep "$BLOCK_SDK_URL/$BLOCK_SDK_V v" | sed -n -e "s/^.* //p") 26 | 27 | mkdir -p ./third_party 28 | cd third_party 29 | git clone -b $INITIA_VERSION https://$INITIA_URL 30 | git clone -b $OPINIT_VERSION https://$OPINIT_URL 31 | git clone -b $COSMOS_SDK_VERSION https://$COSMOS_URL 32 | git clone -b $IBC_VERSION https://$IBC_URL 33 | git clone -b $COSMWASM_VERSION https://$COSMWASM_URL 34 | git clone -b $INDEXER_VERSION https://$INDEXER_URL 35 | git clone -b $CONNECT_VERSION https://$CONNECT_URL 36 | git clone -b $BLOCK_SDK_VERSION https://$BLOCK_SDK_URL 37 | cd .. 38 | 39 | 40 | # start generating 41 | mkdir -p ./tmp-swagger-gen 42 | cd proto 43 | proto_dirs=$(find \ 44 | ./miniwasm \ 45 | ../third_party/cosmos-sdk/proto/cosmos \ 46 | ../third_party/ibc-go/proto/ibc \ 47 | ../third_party/initia/proto \ 48 | ../third_party/OPinit/proto \ 49 | ../third_party/wasmd/proto \ 50 | ../third_party/kvindexer/proto \ 51 | ../third_party/connect/proto \ 52 | ../third_party/block-sdk/proto \ 53 | -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) 54 | for dir in $proto_dirs; do 55 | # generate swagger files (filter query files) 56 | query_file=$(find "${dir}" -maxdepth 1 \( -name 'query.proto' -o -name 'service.proto' \)) 57 | if [[ ! -z "$query_file" ]]; then 58 | buf generate --template buf.gen.swagger.yaml $query_file 59 | fi 60 | done 61 | cd .. 62 | 63 | # combine swagger files 64 | # uses nodejs package `swagger-combine`. 65 | # all the individual swagger files need to be configured in `config.json` for merging 66 | swagger-combine ./client/docs/config.json -o ./client/docs/swagger-ui/swagger.yaml -f yaml --continueOnConflictingPaths true --includeDefinitions true 67 | 68 | # clean swagger files 69 | rm -rf ./tmp-swagger-gen 70 | 71 | # clean third party files 72 | rm -rf ./third_party 73 | -------------------------------------------------------------------------------- /scripts/protocgen-pulsar.sh: -------------------------------------------------------------------------------- 1 | # this script is for generating protobuf files for the new google.golang.org/protobuf API 2 | 3 | set -eo pipefail 4 | 5 | protoc_install_gopulsar() { 6 | go install github.com/cosmos/cosmos-proto/cmd/protoc-gen-go-pulsar@latest 7 | go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1 8 | } 9 | 10 | protoc_install_gopulsar 11 | 12 | mkdir -p ./api 13 | 14 | echo "Cleaning API directory" 15 | ( 16 | cd api 17 | find ./ -type f \( -iname \*.pulsar.go -o -iname \*.pb.go -o -iname \*.cosmos_orm.go -o -iname \*.pb.gw.go \) -delete 18 | find . -empty -type d -delete 19 | cd .. 20 | ) 21 | 22 | echo "Generating API module" 23 | 24 | # exclude ibc modules 25 | cd proto 26 | proto_dirs=$(find ./miniwasm -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) 27 | for dir in $proto_dirs; do 28 | for file in $(find "${dir}" -maxdepth 1 -name '*.proto'); do 29 | buf generate --template buf.gen.pulsar.yaml $file 30 | done 31 | done 32 | cd .. 33 | -------------------------------------------------------------------------------- /scripts/protocgen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # How to run manually: 4 | # docker build --pull --rm -f "contrib/devtools/Dockerfile" -t cosmossdk-proto:latest "contrib/devtools" 5 | # docker run --rm -v $(pwd):/workspace --workdir /workspace cosmossdk-proto sh ./scripts/protocgen.sh 6 | 7 | set -e 8 | echo "Generating gogo proto code" 9 | 10 | cd proto 11 | proto_dirs=$(find ./miniwasm -path -prune -o -name '*.proto' -print0 | xargs -0 -n1 dirname | sort | uniq) 12 | for dir in $proto_dirs; do 13 | for file in $(find "${dir}" -maxdepth 1 -name '*.proto'); do 14 | # this regex checks if a proto file has its go_package set to cosmossdk.io/api/... 15 | # gogo proto files SHOULD ONLY be generated if this is false 16 | # we don't want gogo proto to run for proto files which are natively built for google.golang.org/protobuf 17 | if grep -q "option go_package" "$file" && grep -H -o -c 'option go_package.*cosmossdk.io/api' "$file" | grep -q ':0$'; then 18 | buf generate --template buf.gen.gogo.yaml $file 19 | fi 20 | done 21 | done 22 | cd .. 23 | 24 | # move proto files to the right places 25 | cp -r github.com/initia-labs/miniwasm/* ./ 26 | rm -rf github.com 27 | -------------------------------------------------------------------------------- /shared.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.23-bullseye AS go-builder 2 | 3 | # Install minimum necessary dependencies, build Cosmos SDK, remove packages 4 | RUN apt update 5 | RUN apt install -y curl git build-essential 6 | # debug: for live editing in the image 7 | RUN apt install -y vim 8 | 9 | WORKDIR /code 10 | COPY . /code/ 11 | 12 | RUN VERSION=${VERSION} LEDGER_ENABLED=false make build 13 | 14 | RUN cp /go/pkg/mod/github.com/\!cosm\!wasm/wasmvm/v2@v*/internal/api/libwasmvm.`uname -m`.so /lib/libwasmvm.so 15 | 16 | FROM ubuntu:20.04 17 | 18 | WORKDIR /root 19 | 20 | COPY --from=go-builder /code/build/minitiad /usr/local/bin/minitiad 21 | COPY --from=go-builder /lib/libwasmvm.so /lib/libwasmvm.so 22 | 23 | # for new-metric setup 24 | COPY --from=go-builder /code/contrib /root/contrib 25 | 26 | # rest server 27 | EXPOSE 1317 28 | # grpc 29 | EXPOSE 9090 30 | # tendermint p2p 31 | EXPOSE 26656 32 | # tendermint rpc 33 | EXPOSE 26657 34 | 35 | CMD ["/usr/local/bin/minitiad", "version"] 36 | -------------------------------------------------------------------------------- /types/const.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // BaseDenom base denom for genesis boot 5 | BaseDenom = "umin" 6 | ) 7 | -------------------------------------------------------------------------------- /x/bank/keeper/keeper.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "cosmossdk.io/core/store" 8 | errorsmod "cosmossdk.io/errors" 9 | "cosmossdk.io/log" 10 | 11 | "github.com/cosmos/cosmos-sdk/codec" 12 | 13 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 14 | bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" 15 | "github.com/cosmos/cosmos-sdk/x/bank/types" 16 | 17 | sdk "github.com/cosmos/cosmos-sdk/types" 18 | ) 19 | 20 | type Keeper struct { 21 | bankkeeper.BaseKeeper 22 | 23 | ak types.AccountKeeper 24 | hooks BankHooks 25 | } 26 | 27 | func NewKeeper( 28 | cdc codec.BinaryCodec, 29 | storeService store.KVStoreService, 30 | ak types.AccountKeeper, 31 | blockedAddrs map[string]bool, 32 | authority string, 33 | logger log.Logger, 34 | ) Keeper { 35 | if _, err := ak.AddressCodec().StringToBytes(authority); err != nil { 36 | panic(fmt.Errorf("invalid bank authority address: %w", err)) 37 | } 38 | 39 | return Keeper{ 40 | BaseKeeper: bankkeeper.NewBaseKeeper(cdc, storeService, ak, blockedAddrs, authority, logger), 41 | ak: ak, 42 | } 43 | } 44 | 45 | // SendCoinsFromModuleToModule transfers coins from a ModuleAccount to another. 46 | // It will panic if either module account does not exist. 47 | // SendCoinsFromModuleToModule is the only send method that does not call both BlockBeforeSend and TrackBeforeSend hook. 48 | // It only calls the TrackBeforeSend hook. 49 | func (k Keeper) SendCoinsFromModuleToModule( 50 | ctx context.Context, senderModule, recipientModule string, amt sdk.Coins, 51 | ) error { 52 | senderAddr := k.ak.GetModuleAddress(senderModule) 53 | if senderAddr == nil { 54 | panic(errorsmod.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", senderModule)) 55 | } 56 | 57 | recipientAcc := k.ak.GetModuleAccount(ctx, recipientModule) 58 | if recipientAcc == nil { 59 | panic(errorsmod.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", recipientModule)) 60 | } 61 | 62 | // return k.SendCoins(ctx, senderAddr, recipientAcc.GetAddress(), amt) 63 | return k.SendCoinsWithoutBlockHook(ctx, senderAddr, recipientAcc.GetAddress(), amt) 64 | } 65 | 66 | func (k Keeper) SendCoinsFromModuleToAccount( 67 | ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins, 68 | ) error { 69 | senderAddr := k.ak.GetModuleAddress(senderModule) 70 | if senderAddr == nil { 71 | panic(errorsmod.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", senderModule)) 72 | } 73 | 74 | if k.BlockedAddr(recipientAddr) { 75 | return errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "%s is not allowed to receive funds", recipientAddr) 76 | } 77 | 78 | return k.SendCoins(ctx, senderAddr, recipientAddr, amt) 79 | } 80 | 81 | // SendCoinsFromAccountToModule transfers coins from an AccAddress to a ModuleAccount. 82 | // It will panic if the module account does not exist. 83 | func (k Keeper) SendCoinsFromAccountToModule( 84 | ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins, 85 | ) error { 86 | recipientAcc := k.ak.GetModuleAccount(ctx, recipientModule) 87 | if recipientAcc == nil { 88 | panic(errorsmod.Wrapf(sdkerrors.ErrUnknownAddress, "module account %s does not exist", recipientModule)) 89 | } 90 | 91 | return k.SendCoins(ctx, senderAddr, recipientAcc.GetAddress(), amt) 92 | } 93 | 94 | type BankHooks interface { 95 | TrackBeforeSend(ctx context.Context, from, to sdk.AccAddress, amount sdk.Coins) // Must be before any send is executed 96 | BlockBeforeSend(ctx context.Context, from, to sdk.AccAddress, amount sdk.Coins) error // Must be before any send is executed 97 | } 98 | 99 | // TrackBeforeSend executes the TrackBeforeSend hook if registered. 100 | func (k Keeper) TrackBeforeSend(ctx context.Context, from, to sdk.AccAddress, amount sdk.Coins) { 101 | if k.hooks != nil { 102 | k.hooks.TrackBeforeSend(ctx, from, to, amount) 103 | } 104 | } 105 | 106 | // BlockBeforeSend executes the BlockBeforeSend hook if registered. 107 | func (k Keeper) BlockBeforeSend(ctx context.Context, from, to sdk.AccAddress, amount sdk.Coins) error { 108 | if k.hooks != nil { 109 | return k.hooks.BlockBeforeSend(ctx, from, to, amount) 110 | } 111 | return nil 112 | } 113 | 114 | func (k *Keeper) SetHooks(bh BankHooks) *Keeper { 115 | if k.hooks != nil { 116 | panic("cannot set bank hooks twice") 117 | } 118 | k.hooks = bh 119 | return k 120 | } 121 | 122 | // SendCoinsWithoutBlockHook calls sendCoins without calling the `BlockBeforeSend` hook. 123 | func (k Keeper) SendCoinsWithoutBlockHook(ctx context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error { 124 | // call the TrackBeforeSend hooks 125 | k.TrackBeforeSend(ctx, fromAddr, toAddr, amt) 126 | return k.BaseSendKeeper.SendCoins(ctx, fromAddr, toAddr, amt) 127 | } 128 | 129 | func (k Keeper) SendCoins(ctx context.Context, fromAddr, toAddr sdk.AccAddress, amt sdk.Coins) error { 130 | // BlockBeforeSend hook should always be called before the TrackBeforeSend hook. 131 | err := k.BlockBeforeSend(ctx, fromAddr, toAddr, amt) 132 | if err != nil { 133 | return err 134 | } 135 | // call the TrackBeforeSend hooks 136 | k.TrackBeforeSend(ctx, fromAddr, toAddr, amt) 137 | return k.BaseSendKeeper.SendCoins(ctx, fromAddr, toAddr, amt) 138 | } 139 | -------------------------------------------------------------------------------- /x/bank/module.go: -------------------------------------------------------------------------------- 1 | package bank 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | "github.com/cosmos/cosmos-sdk/types/module" 6 | bank "github.com/cosmos/cosmos-sdk/x/bank" 7 | "github.com/cosmos/cosmos-sdk/x/bank/exported" 8 | types "github.com/cosmos/cosmos-sdk/x/bank/types" 9 | 10 | "github.com/initia-labs/miniwasm/x/bank/keeper" 11 | ) 12 | 13 | const ConsensusVersion = 1 14 | 15 | // AppModule implements an application module for the bank module. 16 | type AppModule struct { 17 | Keeper *keeper.Keeper 18 | bank.AppModule 19 | } 20 | 21 | // NewAppModule creates a new AppModule object 22 | func NewAppModule(cdc codec.Codec, keeper *keeper.Keeper, accountKeeper types.AccountKeeper, ss exported.Subspace) AppModule { 23 | return AppModule{ 24 | Keeper: keeper, 25 | AppModule: bank.NewAppModule(cdc, keeper.BaseKeeper, accountKeeper, ss), 26 | } 27 | } 28 | 29 | func (am AppModule) RegisterServices(cfg module.Configurator) { 30 | types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.Keeper)) 31 | types.RegisterQueryServer(cfg.QueryServer(), am.Keeper.BaseKeeper) 32 | } 33 | 34 | // ConsensusVersion implements ConsensusVersion. 35 | func (AppModule) ConsensusVersion() uint64 { return ConsensusVersion } 36 | -------------------------------------------------------------------------------- /x/tokenfactory/autocli.go: -------------------------------------------------------------------------------- 1 | package tokenfactory 2 | 3 | import ( 4 | autocliv1 "cosmossdk.io/api/cosmos/autocli/v1" 5 | tokenfactoryv1 "github.com/initia-labs/miniwasm/api/miniwasm/tokenfactory/v1" 6 | ) 7 | 8 | // AutoCLIOptions implements the autocli.HasAutoCLIConfig interface. 9 | func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { 10 | return &autocliv1.ModuleOptions{ 11 | Query: &autocliv1.ServiceCommandDescriptor{ 12 | Service: tokenfactoryv1.Query_ServiceDesc.ServiceName, 13 | RpcCommandOptions: []*autocliv1.RpcCommandOptions{ 14 | { 15 | RpcMethod: "DenomAuthorityMetadata", 16 | Use: "denom-authority-metadata [denom]", 17 | Short: "Get the authority metadata for a specific denom", 18 | PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 19 | {ProtoField: "denom"}, 20 | }, 21 | }, 22 | { 23 | RpcMethod: "DenomsFromCreator", 24 | Use: "denoms-from-creator [creator]", 25 | Short: "Returns a list of all tokens created by a specific creator address", 26 | PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 27 | {ProtoField: "creator"}, 28 | }, 29 | }, 30 | { 31 | RpcMethod: "BeforeSendHookAddress", 32 | Use: "before_send_hook [denom]", 33 | Short: "Get the BeforeSend hook for a specific denom", 34 | PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 35 | {ProtoField: "denom"}, 36 | }, 37 | }, 38 | { 39 | RpcMethod: "Params", 40 | Use: "params", 41 | Short: "Returns the tokenfactory module's parameters", 42 | }, 43 | }, 44 | EnhanceCustomCommand: true, // We still have manual commands in gov that we want to keep 45 | }, 46 | // Tx: &autocliv1.ServiceCommandDescriptor{ 47 | // Service: tokenfactoryv1.Msg_ServiceDesc.ServiceName, 48 | // RpcCommandOptions: []*autocliv1.RpcCommandOptions{ 49 | // { 50 | // RpcMethod: "CreateDenom", 51 | // Use: "create-denom [sender] [sub-denom]", 52 | // Short: "create a new denom from an account.", 53 | // PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 54 | // {ProtoField: "sender"}, 55 | // {ProtoField: "subdenom"}, 56 | // }, 57 | // }, 58 | // { 59 | // RpcMethod: "Mint", 60 | // Use: "mint [sender] [amount] [mint-to-address]", 61 | // Short: "Mint a denom to an address. Must have admin authority to do so.", 62 | // PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 63 | // {ProtoField: "sender"}, 64 | // {ProtoField: "amount"}, 65 | // {ProtoField: "mintToAddress"}, 66 | // }, 67 | // }, 68 | // { 69 | // RpcMethod: "Burn", 70 | // Use: "burn [sender] [amount] [burn-from-address]", 71 | // Short: "Burn tokens from an address. Must have admin authority to do so.", 72 | // PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 73 | // {ProtoField: "sender"}, 74 | // {ProtoField: "amount"}, 75 | // {ProtoField: "burnFromAddress"}, 76 | // }, 77 | // }, 78 | // { 79 | // RpcMethod: "ChangeAdmin", 80 | // Use: "change-admin [sender] [denom] [new-admin]", 81 | // Short: "Changes the admin address for a factory-created denom. Must have admin authority to do so.", 82 | // PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 83 | // {ProtoField: "sender"}, 84 | // {ProtoField: "denom"}, 85 | // {ProtoField: "new_admin"}, 86 | // }, 87 | // }, 88 | // { 89 | // RpcMethod: "SetDenomMetadata", 90 | // Use: "set-denom-metadata [sender] [denom] [cosmwasm-address]", 91 | // Short: "Set a denom metadata", 92 | // PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 93 | // {ProtoField: "sender"}, 94 | // {ProtoField: "denom"}, 95 | // {ProtoField: "cosmwasm_address"}, 96 | // }, 97 | // }, 98 | // { 99 | // RpcMethod: "SetBeforeSendHook", 100 | // Use: "set-beforesend-hook [denom] [cosmwasm-address]", 101 | // Short: "Set a cosmwasm contract to be the beforesend hook for a factory-created denom. Must have admin authority to do so.", 102 | // PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 103 | // {ProtoField: "denom"}, 104 | // {ProtoField: "cosmwasm-address"}, 105 | // }, 106 | // }, 107 | // { 108 | // RpcMethod: "ForceTransfer", 109 | // Use: "force-transfer [sender] [amount] [transfer-from-address] [transfer-to-address]", 110 | // Short: "Transfer a factory-crated denom", 111 | // PositionalArgs: []*autocliv1.PositionalArgDescriptor{ 112 | // {ProtoField: "sender"}, 113 | // {ProtoField: "amount"}, 114 | // {ProtoField: "transferFromAddress"}, 115 | // {ProtoField: "transferToAddress"}, 116 | // }, 117 | // }, 118 | // }, 119 | // }, 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /x/tokenfactory/images/Burn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/x/tokenfactory/images/Burn.png -------------------------------------------------------------------------------- /x/tokenfactory/images/ChangeAdmin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/x/tokenfactory/images/ChangeAdmin.png -------------------------------------------------------------------------------- /x/tokenfactory/images/CreateDenom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/x/tokenfactory/images/CreateDenom.png -------------------------------------------------------------------------------- /x/tokenfactory/images/Mint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/x/tokenfactory/images/Mint.png -------------------------------------------------------------------------------- /x/tokenfactory/images/SetDenomMetadata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/x/tokenfactory/images/SetDenomMetadata.png -------------------------------------------------------------------------------- /x/tokenfactory/keeper/admins.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/initia-labs/miniwasm/x/tokenfactory/types" 7 | ) 8 | 9 | // GetAuthorityMetadata returns the authority metadata for a specific denom 10 | func (k Keeper) GetAuthorityMetadata(ctx context.Context, denom string) (types.DenomAuthorityMetadata, error) { 11 | return k.DenomAuthority.Get(ctx, denom) 12 | } 13 | 14 | // setAuthorityMetadata stores authority metadata for a specific denom 15 | func (k Keeper) setAuthorityMetadata(ctx context.Context, denom string, metadata types.DenomAuthorityMetadata) error { 16 | err := metadata.Validate(k.ac) 17 | if err != nil { 18 | return err 19 | } 20 | 21 | return k.DenomAuthority.Set(ctx, denom, metadata) 22 | } 23 | 24 | func (k Keeper) setAdmin(ctx context.Context, denom string, admin string) error { 25 | metadata, err := k.GetAuthorityMetadata(ctx, denom) 26 | if err != nil { 27 | return err 28 | } 29 | 30 | metadata.Admin = admin 31 | 32 | return k.setAuthorityMetadata(ctx, denom, metadata) 33 | } 34 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/bankactions.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | 9 | "github.com/initia-labs/miniwasm/x/tokenfactory/types" 10 | ) 11 | 12 | func (k Keeper) mintTo(ctx context.Context, amount sdk.Coin, mintTo string) error { 13 | // verify that denom is an x/tokenfactory denom 14 | _, _, err := types.DeconstructDenom(k.ac, amount.Denom) 15 | if err != nil { 16 | return err 17 | } 18 | 19 | err = k.bankKeeper.MintCoins(ctx, types.ModuleName, sdk.NewCoins(amount)) 20 | if err != nil { 21 | return err 22 | } 23 | 24 | addr, err := k.ac.StringToBytes(mintTo) 25 | if err != nil { 26 | return err 27 | } 28 | 29 | if k.bankKeeper.BlockedAddr(addr) { 30 | return fmt.Errorf("failed to mint to blocked address: %s", addr) 31 | } 32 | 33 | return k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, 34 | addr, 35 | sdk.NewCoins(amount)) 36 | } 37 | 38 | func (k Keeper) burnFrom(ctx context.Context, amount sdk.Coin, burnFrom string) error { 39 | // verify that denom is an x/tokenfactory denom 40 | _, _, err := types.DeconstructDenom(k.ac, amount.Denom) 41 | if err != nil { 42 | return err 43 | } 44 | 45 | addr, err := k.ac.StringToBytes(burnFrom) 46 | if err != nil { 47 | return err 48 | } 49 | 50 | if k.bankKeeper.BlockedAddr(addr) { 51 | return fmt.Errorf("failed to burn from blocked address: %s", addr) 52 | } 53 | 54 | err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, 55 | addr, 56 | types.ModuleName, 57 | sdk.NewCoins(amount)) 58 | if err != nil { 59 | return err 60 | } 61 | 62 | return k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(amount)) 63 | } 64 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/createdenom.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 9 | 10 | "github.com/initia-labs/miniwasm/x/tokenfactory/types" 11 | ) 12 | 13 | // ConvertToBaseToken converts a fee amount in a whitelisted fee token to the base fee token amount 14 | func (k Keeper) CreateDenom(ctx context.Context, creatorAddr string, subdenom string) (newTokenDenom string, err error) { 15 | denom, err := k.validateCreateDenom(ctx, creatorAddr, subdenom) 16 | if err != nil { 17 | return "", err 18 | } 19 | 20 | err = k.chargeForCreateDenom(ctx, creatorAddr) 21 | if err != nil { 22 | return "", err 23 | } 24 | 25 | err = k.createDenomAfterValidation(ctx, creatorAddr, denom) 26 | return denom, err 27 | } 28 | 29 | // Runs CreateDenom logic after the charge and all denom validation has been handled. 30 | // Made into a second function for genesis initialization. 31 | func (k Keeper) createDenomAfterValidation(ctx context.Context, creatorAddr string, denom string) (err error) { 32 | _, exists := k.bankKeeper.GetDenomMetaData(ctx, denom) 33 | if !exists { 34 | denomMetaData := banktypes.Metadata{ 35 | DenomUnits: []*banktypes.DenomUnit{{ 36 | Denom: denom, 37 | Exponent: 0, 38 | }}, 39 | Base: denom, 40 | Name: denom, 41 | Symbol: denom, 42 | Display: denom, 43 | } 44 | 45 | k.bankKeeper.SetDenomMetaData(ctx, denomMetaData) 46 | } 47 | 48 | authorityMetadata := types.DenomAuthorityMetadata{ 49 | Admin: creatorAddr, 50 | } 51 | err = k.setAuthorityMetadata(ctx, denom, authorityMetadata) 52 | if err != nil { 53 | return err 54 | } 55 | 56 | return k.addDenomFromCreator(ctx, creatorAddr, denom) 57 | } 58 | 59 | func (k Keeper) validateCreateDenom(ctx context.Context, creatorAddr string, subdenom string) (newTokenDenom string, err error) { 60 | // Temporary check until IBC bug is sorted out 61 | if k.bankKeeper.HasSupply(ctx, subdenom) { 62 | return "", fmt.Errorf("temporary error until IBC bug is sorted out, " + 63 | "can't create subdenoms that are the same as a native denom") 64 | } 65 | 66 | denom, err := types.GetTokenDenom(creatorAddr, subdenom) 67 | if err != nil { 68 | return "", err 69 | } 70 | 71 | _, found := k.bankKeeper.GetDenomMetaData(ctx, denom) 72 | if found { 73 | return "", types.ErrDenomExists 74 | } 75 | 76 | return denom, nil 77 | } 78 | 79 | func (k Keeper) chargeForCreateDenom(ctx context.Context, creatorAddr string) (err error) { 80 | params := k.GetParams(ctx) 81 | 82 | // if DenomCreationFee is non-zero, transfer the tokens from the creator 83 | // account to community pool 84 | if params.DenomCreationFee != nil { 85 | accAddr, err := sdk.AccAddressFromBech32(creatorAddr) 86 | if err != nil { 87 | return err 88 | } 89 | 90 | if err := k.communityPoolKeeper.FundCommunityPool(ctx, params.DenomCreationFee, accAddr); err != nil { 91 | return err 92 | } 93 | } 94 | 95 | // if DenomCreationGasConsume is non-zero, consume the gas 96 | if params.DenomCreationGasConsume != 0 { 97 | sdkCtx := sdk.UnwrapSDKContext(ctx) 98 | sdkCtx.GasMeter().ConsumeGas(params.DenomCreationGasConsume, "consume denom creation gas") 99 | } 100 | 101 | return nil 102 | } 103 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/creators.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "cosmossdk.io/collections" 7 | ) 8 | 9 | func (k Keeper) addDenomFromCreator(ctx context.Context, creator, denom string) error { 10 | return k.CreatorDenoms.Set(ctx, collections.Join(creator, denom)) 11 | } 12 | 13 | func (k Keeper) getDenomsFromCreator(ctx context.Context, creator string) []string { 14 | denoms := []string{} 15 | err := k.CreatorDenoms.Walk(ctx, collections.NewPrefixedPairRange[string, string](creator), func(key collections.Pair[string, string]) (stop bool, err error) { 16 | denoms = append(denoms, key.K2()) 17 | return false, nil 18 | }) 19 | if err != nil { 20 | panic(err) 21 | } 22 | return denoms 23 | } 24 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/genesis.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "cosmossdk.io/collections" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | 8 | "github.com/initia-labs/miniwasm/x/tokenfactory/types" 9 | ) 10 | 11 | // InitGenesis initializes the tokenfactory module's state from a provided genesis 12 | // state. 13 | func (k Keeper) InitGenesis(ctx sdk.Context, genState types.GenesisState) { 14 | k.CreateModuleAccount(ctx) 15 | 16 | if genState.Params.DenomCreationFee == nil { 17 | genState.Params.DenomCreationFee = sdk.NewCoins() 18 | } 19 | 20 | if err := k.SetParams(ctx, genState.Params); err != nil { 21 | panic(err) 22 | } 23 | 24 | for _, genDenom := range genState.GetFactoryDenoms() { 25 | creator, _, err := types.DeconstructDenom(k.ac, genDenom.GetDenom()) 26 | if err != nil { 27 | panic(err) 28 | } 29 | err = k.createDenomAfterValidation(ctx, creator, genDenom.GetDenom()) 30 | if err != nil { 31 | panic(err) 32 | } 33 | err = k.setAuthorityMetadata(ctx, genDenom.GetDenom(), genDenom.GetAuthorityMetadata()) 34 | if err != nil { 35 | panic(err) 36 | } 37 | } 38 | } 39 | 40 | // ExportGenesis returns the tokenfactory module's exported genesis. 41 | func (k Keeper) ExportGenesis(ctx sdk.Context) *types.GenesisState { 42 | genDenoms := []types.GenesisDenom{} 43 | err := k.CreatorDenoms.Walk(ctx, nil, func(key collections.Pair[string, string]) (stop bool, err error) { 44 | denom := key.K2() 45 | 46 | authorityMetadata, err := k.GetAuthorityMetadata(ctx, denom) 47 | if err != nil { 48 | panic(err) 49 | } 50 | 51 | genDenoms = append(genDenoms, types.GenesisDenom{ 52 | Denom: denom, 53 | AuthorityMetadata: authorityMetadata, 54 | }) 55 | return false, nil 56 | }) 57 | if err != nil { 58 | panic(err) 59 | } 60 | 61 | return &types.GenesisState{ 62 | FactoryDenoms: genDenoms, 63 | Params: k.GetParams(ctx), 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/genesis_test.go: -------------------------------------------------------------------------------- 1 | package keeper_test 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/require" 8 | 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 11 | 12 | "github.com/initia-labs/miniwasm/x/tokenfactory/types" 13 | ) 14 | 15 | func TestGenesis(t *testing.T) { 16 | ctx, input := createDefaultTestInput(t) 17 | 18 | tokenFactoryKeeper := input.TokenFactoryKeeper 19 | bankKeeper := input.BankKeeper 20 | accountKeeper := input.AccountKeeper 21 | 22 | creator, err := input.AddressCodec.BytesToString(addrs[0]) 23 | require.NoError(t, err, "address encoding") 24 | 25 | another, err := input.AddressCodec.BytesToString(addrs[1]) 26 | require.NoError(t, err, "address encoding") 27 | 28 | genesisState := types.GenesisState{ 29 | FactoryDenoms: []types.GenesisDenom{ 30 | { 31 | Denom: fmt.Sprintf("factory/%s/bitcoin", creator), 32 | AuthorityMetadata: types.DenomAuthorityMetadata{ 33 | Admin: creator, 34 | }, 35 | }, 36 | { 37 | Denom: fmt.Sprintf("factory/%s/diff-admin", creator), 38 | AuthorityMetadata: types.DenomAuthorityMetadata{ 39 | Admin: another, 40 | }, 41 | }, 42 | { 43 | Denom: fmt.Sprintf("factory/%s/litecoin", creator), 44 | AuthorityMetadata: types.DenomAuthorityMetadata{ 45 | Admin: creator, 46 | }, 47 | }, 48 | }, 49 | } 50 | 51 | // Test both with bank denom metadata set, and not set. 52 | for i, denom := range genesisState.FactoryDenoms { 53 | // hacky, sets bank metadata to exist if i != 0, to cover both cases. 54 | if i != 0 { 55 | bankKeeper.SetDenomMetaData(ctx, banktypes.Metadata{ 56 | DenomUnits: []*banktypes.DenomUnit{{ 57 | Denom: denom.GetDenom(), 58 | Exponent: 0, 59 | }}, 60 | Base: denom.GetDenom(), 61 | Display: denom.GetDenom(), 62 | Name: denom.GetDenom(), 63 | Symbol: denom.GetDenom(), 64 | }) 65 | } 66 | } 67 | 68 | // check before initGenesis that the module account is nil 69 | tokenfactoryModuleAccount := accountKeeper.GetAccount(ctx, accountKeeper.GetModuleAddress(types.ModuleName)) 70 | require.Nil(t, tokenfactoryModuleAccount) 71 | 72 | tokenFactoryKeeper.SetParams(ctx, types.Params{DenomCreationFee: sdk.Coins{sdk.NewInt64Coin("uinit", 100)}}) //nolint:errcheck 73 | tokenFactoryKeeper.InitGenesis(ctx, genesisState) 74 | 75 | // check that the module account is now initialized 76 | tokenfactoryModuleAccount = accountKeeper.GetAccount(ctx, accountKeeper.GetModuleAddress(types.ModuleName)) 77 | require.NotNil(t, tokenfactoryModuleAccount) 78 | 79 | exportedGenesis := tokenFactoryKeeper.ExportGenesis(ctx) 80 | require.NotNil(t, exportedGenesis) 81 | require.Equal(t, genesisState, *exportedGenesis) 82 | 83 | // verify that the exported bank genesis is valid 84 | bankKeeper.SetParams(ctx, banktypes.DefaultParams()) //nolint:errcheck 85 | exportedBankGenesis := bankKeeper.ExportGenesis(ctx) 86 | require.NoError(t, exportedBankGenesis.Validate()) 87 | 88 | bankKeeper.InitGenesis(ctx, exportedBankGenesis) 89 | for i, denom := range genesisState.FactoryDenoms { 90 | // hacky, check whether bank metadata is not replaced if i != 0, to cover both cases. 91 | if i != 0 { 92 | metadata, found := bankKeeper.GetDenomMetaData(ctx, denom.GetDenom()) 93 | require.True(t, found) 94 | require.EqualValues(t, metadata, banktypes.Metadata{ 95 | DenomUnits: []*banktypes.DenomUnit{{ 96 | Denom: denom.GetDenom(), 97 | Exponent: 0, 98 | }}, 99 | Base: denom.GetDenom(), 100 | Display: denom.GetDenom(), 101 | Name: denom.GetDenom(), 102 | Symbol: denom.GetDenom(), 103 | }) 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/grpc_query.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "net/url" 7 | 8 | "github.com/initia-labs/miniwasm/x/tokenfactory/types" 9 | ) 10 | 11 | type Querier struct { 12 | *Keeper 13 | } 14 | 15 | var _ types.QueryServer = Querier{} 16 | 17 | func (q Querier) Params(ctx context.Context, req *types.QueryParamsRequest) (*types.QueryParamsResponse, error) { 18 | params := q.GetParams(ctx) 19 | 20 | return &types.QueryParamsResponse{Params: params}, nil 21 | } 22 | 23 | func (q Querier) DenomAuthorityMetadata(ctx context.Context, req *types.QueryDenomAuthorityMetadataRequest) (*types.QueryDenomAuthorityMetadataResponse, error) { 24 | decodedDenom, err := url.QueryUnescape(req.Denom) 25 | if err == nil { 26 | req.Denom = decodedDenom 27 | } 28 | authorityMetadata, err := q.GetAuthorityMetadata(ctx, req.GetDenom()) 29 | if err != nil { 30 | return nil, err 31 | } 32 | 33 | return &types.QueryDenomAuthorityMetadataResponse{AuthorityMetadata: authorityMetadata}, nil 34 | } 35 | 36 | func (q Querier) DenomsFromCreator(ctx context.Context, req *types.QueryDenomsFromCreatorRequest) (*types.QueryDenomsFromCreatorResponse, error) { 37 | denoms := q.getDenomsFromCreator(ctx, req.GetCreator()) 38 | return &types.QueryDenomsFromCreatorResponse{Denoms: denoms}, nil 39 | } 40 | 41 | func (q Querier) BeforeSendHookAddress(ctx context.Context, req *types.QueryBeforeSendHookAddressRequest) (*types.QueryBeforeSendHookAddressResponse, error) { 42 | decodedDenom, err := url.QueryUnescape(req.Denom) 43 | if err == nil { 44 | req.Denom = decodedDenom 45 | } 46 | 47 | cosmwasmAddress := q.GetBeforeSendHook(ctx, req.GetDenom()) 48 | 49 | return &types.QueryBeforeSendHookAddressResponse{CosmwasmAddress: cosmwasmAddress}, nil 50 | } 51 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/keeper.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "cosmossdk.io/collections" 7 | "cosmossdk.io/core/address" 8 | 9 | corestoretypes "cosmossdk.io/core/store" 10 | 11 | "cosmossdk.io/log" 12 | 13 | "github.com/cosmos/cosmos-sdk/codec" 14 | 15 | sdk "github.com/cosmos/cosmos-sdk/types" 16 | 17 | "github.com/initia-labs/miniwasm/x/tokenfactory/types" 18 | ) 19 | 20 | type Keeper struct { 21 | ac address.Codec 22 | cdc codec.Codec 23 | storeService corestoretypes.KVStoreService 24 | 25 | accountKeeper types.AccountKeeper 26 | bankKeeper types.BankKeeper 27 | contractKeeper types.ContractKeeper 28 | 29 | communityPoolKeeper types.CommunityPoolKeeper 30 | 31 | Schema collections.Schema 32 | // key = [creator,denom], value = metadata 33 | CreatorDenoms collections.KeySet[collections.Pair[string, string]] 34 | DenomAuthority collections.Map[string, types.DenomAuthorityMetadata] 35 | DenomHookAddr collections.Map[string, string] 36 | Params collections.Item[types.Params] 37 | 38 | authority string 39 | } 40 | 41 | // NewKeeper returns a new instance of the x/tokenfactory keeper 42 | func NewKeeper( 43 | ac address.Codec, 44 | cdc codec.Codec, 45 | storeService corestoretypes.KVStoreService, 46 | accountKeeper types.AccountKeeper, 47 | bankKeeper types.BankKeeper, 48 | communityPoolKeeper types.CommunityPoolKeeper, 49 | authority string, 50 | ) Keeper { 51 | sb := collections.NewSchemaBuilder(storeService) 52 | k := Keeper{ 53 | ac: ac, 54 | cdc: cdc, 55 | storeService: storeService, 56 | 57 | accountKeeper: accountKeeper, 58 | bankKeeper: bankKeeper, 59 | communityPoolKeeper: communityPoolKeeper, 60 | 61 | CreatorDenoms: collections.NewKeySet(sb, types.CreatorDenomsPrefix, "creatordenom", collections.PairKeyCodec(collections.StringKey, collections.StringKey)), 62 | DenomAuthority: collections.NewMap(sb, types.DenomAuthorityPrefix, "denomauthority", collections.StringKey, codec.CollValue[types.DenomAuthorityMetadata](cdc)), 63 | DenomHookAddr: collections.NewMap(sb, types.DenomHookAddrPrefix, "denomhookaddr", collections.StringKey, collections.StringValue), 64 | 65 | Params: collections.NewItem(sb, types.ParamsKeyPrefix, "params", codec.CollValue[types.Params](cdc)), 66 | 67 | authority: authority, 68 | } 69 | schema, err := sb.Build() 70 | if err != nil { 71 | panic(err) 72 | } 73 | k.Schema = schema 74 | 75 | return k 76 | } 77 | 78 | // GetAuthority returns the x/tokenfactory module's authority. 79 | func (k Keeper) GetAuthority() string { 80 | return k.authority 81 | } 82 | 83 | // Logger returns a logger for the x/tokenfactory module 84 | func (k Keeper) Logger(ctx context.Context) log.Logger { 85 | sdkCtx := sdk.UnwrapSDKContext(ctx) 86 | return sdkCtx.Logger().With("module", "x/"+types.ModuleName) 87 | } 88 | 89 | // Set the wasm keeper. 90 | func (k *Keeper) SetContractKeeper(contractKeeper types.ContractKeeper) { 91 | k.contractKeeper = contractKeeper 92 | } 93 | 94 | // CreateModuleAccount creates a module account with minting and burning capabilities 95 | // This account isn't intended to store any coins, 96 | // it purely mints and burns them on behalf of the admin of respective denoms, 97 | // and sends to the relevant address. 98 | func (k Keeper) CreateModuleAccount(ctx sdk.Context) { 99 | k.accountKeeper.GetModuleAccount(ctx, types.ModuleName) 100 | } 101 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/params.go: -------------------------------------------------------------------------------- 1 | package keeper 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/initia-labs/miniwasm/x/tokenfactory/types" 7 | ) 8 | 9 | // GetParams returns the total set params. 10 | func (k Keeper) GetParams(ctx context.Context) (params types.Params) { 11 | params, err := k.Params.Get(ctx) 12 | if err != nil { 13 | return types.Params{} 14 | } 15 | 16 | return params 17 | } 18 | 19 | // SetParams sets the total set of params. 20 | func (k Keeper) SetParams(ctx context.Context, params types.Params) error { 21 | return k.Params.Set(ctx, params) 22 | } 23 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --release --lib --target wasm32-unknown-unknown" 3 | unit-test = "test --lib" 4 | all-test = "test --workspace" 5 | schema = "run --bin schema" 6 | 7 | [env] 8 | RUSTFLAGS = "-C link-arg=-s" 9 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Generated by rust-optimizer 7 | artifacts/ 8 | 9 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 10 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 11 | Cargo.lock 12 | 13 | # These are backup files generated by rustfmt 14 | **/*.rs.bk 15 | 16 | # MSVC Windows builds of rustc generate these, which store debugging information 17 | *.pdb 18 | 19 | 20 | # Ignores local beaker state 21 | **/state.local.json 22 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/Beaker.toml: -------------------------------------------------------------------------------- 1 | name = "tokenfactory-test-contract" 2 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | 'contracts/*', 5 | ] 6 | 7 | [profile.release] 8 | codegen-units = 1 9 | debug = false 10 | debug-assertions = false 11 | incremental = false 12 | lto = true 13 | opt-level = 3 14 | overflow-checks = true 15 | panic = 'abort' 16 | rpath = false 17 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/x/tokenfactory/keeper/testcontracts/contracts/.gitkeep -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/infinite-track-beforesend/.cargo/config: -------------------------------------------------------------------------------- 1 | [alias] 2 | wasm = "build --release --lib --target wasm32-unknown-unknown" 3 | unit-test = "test --lib" 4 | schema = "run --bin schema" 5 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/infinite-track-beforesend/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | 10 | [*.rs] 11 | indent_size = 4 12 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/infinite-track-beforesend/.github/workflows/Basic.yml: -------------------------------------------------------------------------------- 1 | # Based on https://github.com/actions-rs/example/blob/master/.github/workflows/quickstart.yml 2 | 3 | on: [push, pull_request] 4 | 5 | name: Basic 6 | 7 | jobs: 8 | 9 | test: 10 | name: Test Suite 11 | runs-on: ubuntu-22.04 12 | steps: 13 | - name: Checkout sources 14 | uses: actions/checkout@v2 15 | 16 | - name: Install stable toolchain 17 | uses: actions-rs/toolchain@v1 18 | with: 19 | profile: minimal 20 | toolchain: 1.58.1 21 | target: wasm32-unknown-unknown 22 | override: true 23 | 24 | - name: Run unit tests 25 | uses: actions-rs/cargo@v1 26 | with: 27 | command: unit-test 28 | args: --locked 29 | env: 30 | RUST_BACKTRACE: 1 31 | 32 | - name: Compile WASM contract 33 | uses: actions-rs/cargo@v1 34 | with: 35 | command: wasm 36 | args: --locked 37 | env: 38 | RUSTFLAGS: "-C link-arg=-s" 39 | 40 | lints: 41 | name: Lints 42 | runs-on: ubuntu-22.04 43 | steps: 44 | - name: Checkout sources 45 | uses: actions/checkout@v2 46 | 47 | - name: Install stable toolchain 48 | uses: actions-rs/toolchain@v1 49 | with: 50 | profile: minimal 51 | toolchain: 1.58.1 52 | override: true 53 | components: rustfmt, clippy 54 | 55 | - name: Run cargo fmt 56 | uses: actions-rs/cargo@v1 57 | with: 58 | command: fmt 59 | args: --all -- --check 60 | 61 | - name: Run cargo clippy 62 | uses: actions-rs/cargo@v1 63 | with: 64 | command: clippy 65 | args: -- -D warnings 66 | 67 | - name: Generate Schema 68 | uses: actions-rs/cargo@v1 69 | with: 70 | command: schema 71 | args: --locked 72 | 73 | - name: Schema Changes 74 | # fails if any changes not committed 75 | run: git diff --exit-code schema 76 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/infinite-track-beforesend/.github/workflows/Release.yml: -------------------------------------------------------------------------------- 1 | name: release wasm 2 | 3 | on: 4 | release: 5 | types: [created] 6 | 7 | jobs: 8 | release: 9 | runs-on: ubuntu-22.04 10 | steps: 11 | - name: Checkout sources 12 | uses: actions/checkout@v2 13 | - name: Install cargo-run-script 14 | uses: actions-rs/cargo@v1 15 | with: 16 | command: install 17 | args: cargo-run-script 18 | - name: Run cargo optimize 19 | uses: actions-rs/cargo@v1 20 | with: 21 | command: run-script 22 | args: optimize 23 | - name: Get release ID 24 | id: get_release 25 | uses: bruceadams/get-release@v1.2.3 26 | env: 27 | GITHUB_TOKEN: ${{ github.token }} 28 | - name: Upload optimized wasm 29 | uses: svenstaro/upload-release-action@v2 30 | with: 31 | repo_token: ${{ secrets.GITHUB_TOKEN }} 32 | file: ./artifacts/*.wasm 33 | tag: ${{ github.ref }} 34 | overwrite: true 35 | file_glob: true 36 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/infinite-track-beforesend/.gitignore: -------------------------------------------------------------------------------- 1 | # Build results 2 | /target 3 | /schema 4 | 5 | # Cargo+Git helper file (https://github.com/rust-lang/cargo/blob/0.44.1/src/cargo/sources/git/utils.rs#L320-L327) 6 | .cargo-ok 7 | 8 | # Text file backups 9 | **/*.rs.bk 10 | 11 | # macOS 12 | .DS_Store 13 | 14 | # IDEs 15 | *.iml 16 | .idea 17 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/infinite-track-beforesend/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "infinite-track-beforesend" 3 | version = "0.1.0" 4 | authors = ["Supanat Potiwarakorn "] 5 | edition = "2021" 6 | 7 | exclude = [ 8 | # Those files are rust-optimizer artifacts. You might want to commit them for convenience but they should not be part of the source code publication. 9 | "contract.wasm", 10 | "hash.txt", 11 | ] 12 | 13 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 14 | 15 | [lib] 16 | crate-type = ["cdylib", "rlib"] 17 | 18 | [profile.release] 19 | opt-level = 3 20 | debug = false 21 | rpath = false 22 | lto = true 23 | debug-assertions = false 24 | codegen-units = 1 25 | panic = 'abort' 26 | incremental = false 27 | overflow-checks = true 28 | 29 | [features] 30 | # use library feature to disable all instantiate/execute/query exports 31 | library = [] 32 | 33 | [package.metadata.scripts] 34 | optimize = """docker run --rm -v "$(pwd)":/code \ 35 | --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ 36 | --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ 37 | cosmwasm/rust-optimizer:0.12.6 38 | """ 39 | 40 | [dependencies] 41 | cosmwasm-schema = "2.0.4" 42 | cosmwasm-std = "2.0.4" 43 | cosmwasm-storage = "1.1.2" 44 | cw-storage-plus = "2.0.0" 45 | cw2 = "2.0.0" 46 | schemars = "0.8.8" 47 | serde = { version = "1.0.137", default-features = false, features = ["derive"] } 48 | thiserror = { version = "1.0.31" } 49 | 50 | [dev-dependencies] 51 | cw-multi-test = "2.1.0" 52 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/infinite-track-beforesend/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2023 Supanat Potiwarakorn 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/infinite-track-beforesend/README.md: -------------------------------------------------------------------------------- 1 | # infinite-track-beforesend 2 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/infinite-track-beforesend/src/contract.rs: -------------------------------------------------------------------------------- 1 | #[cfg(not(feature = "library"))] 2 | use cosmwasm_std::entry_point; 3 | use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult}; 4 | use cw2::set_contract_version; 5 | 6 | use crate::error::ContractError; 7 | use crate::msg::{InstantiateMsg, SudoMsg}; 8 | 9 | // version info for migration info 10 | const CONTRACT_NAME: &str = "crates.io:infinite-track-beforesend"; 11 | const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION"); 12 | 13 | /// Handling contract instantiation 14 | #[cfg_attr(not(feature = "library"), entry_point)] 15 | pub fn instantiate( 16 | deps: DepsMut, 17 | _env: Env, 18 | info: MessageInfo, 19 | _msg: InstantiateMsg, 20 | ) -> Result { 21 | set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?; 22 | 23 | // With `Response` type, it is possible to dispatch message to invoke external logic. 24 | // See: https://github.com/CosmWasm/cosmwasm/blob/main/SEMANTICS.md#dispatching-messages 25 | Ok(Response::new() 26 | .add_attribute("method", "instantiate") 27 | .add_attribute("owner", info.sender)) 28 | } 29 | 30 | #[cfg_attr(not(feature = "library"), entry_point)] 31 | pub fn execute( 32 | _deps: DepsMut, 33 | _env: Env, 34 | _info: MessageInfo, 35 | _msg: (), 36 | ) -> Result { 37 | Ok(Response::default()) 38 | } 39 | 40 | #[cfg_attr(not(feature = "library"), entry_point)] 41 | pub fn query(_deps: Deps, _env: Env, _msg: ()) -> StdResult { 42 | Ok(Binary::default()) 43 | } 44 | 45 | #[cfg_attr(not(feature = "library"), entry_point)] 46 | pub fn sudo(_deps: DepsMut, _env: Env, msg: SudoMsg) -> Result { 47 | match msg { 48 | SudoMsg::TrackBeforeSend { .. } => 49 | { 50 | #[allow(clippy::empty_loop)] 51 | loop {} 52 | } 53 | SudoMsg::BlockBeforeSend { .. } => 54 | { 55 | #[allow(clippy::empty_loop)] 56 | loop {} 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/infinite-track-beforesend/src/error.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_std::StdError; 2 | use thiserror::Error; 3 | 4 | #[derive(Error, Debug)] 5 | pub enum ContractError { 6 | #[error("{0}")] 7 | Std(#[from] StdError), 8 | 9 | #[error("Unauthorized")] 10 | Unauthorized {}, 11 | 12 | #[error("Custom Error val: {val:?}")] 13 | CustomError { val: String }, 14 | // Add any other custom errors you like here. 15 | // Look at https://docs.rs/thiserror/1.0.21/thiserror/ for details. 16 | } 17 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/infinite-track-beforesend/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod contract; 2 | mod error; 3 | pub mod msg; 4 | 5 | pub use crate::error::ContractError; 6 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testcontracts/contracts/infinite-track-beforesend/src/msg.rs: -------------------------------------------------------------------------------- 1 | use cosmwasm_schema::cw_serde; 2 | use cosmwasm_std::Coin; 3 | 4 | #[cw_serde] 5 | pub struct InstantiateMsg {} 6 | 7 | #[cw_serde] 8 | pub enum SudoMsg { 9 | TrackBeforeSend { 10 | from: String, 11 | to: String, 12 | amount: Coin, 13 | }, 14 | BlockBeforeSend { 15 | from: String, 16 | to: String, 17 | amount: Coin, 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testdata/infinite_track_beforesend.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/x/tokenfactory/keeper/testdata/infinite_track_beforesend.wasm -------------------------------------------------------------------------------- /x/tokenfactory/keeper/testdata/no100.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/initia-labs/miniwasm/5d618897f607d47c65d1a7a8f5d02fdb5c6a401e/x/tokenfactory/keeper/testdata/no100.wasm -------------------------------------------------------------------------------- /x/tokenfactory/types/authority_metadata.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "cosmossdk.io/core/address" 5 | ) 6 | 7 | func (metadata DenomAuthorityMetadata) Validate(ac address.Codec) error { 8 | if metadata.Admin != "" { 9 | _, err := ac.StringToBytes(metadata.Admin) 10 | if err != nil { 11 | return err 12 | } 13 | } 14 | return nil 15 | } 16 | -------------------------------------------------------------------------------- /x/tokenfactory/types/before_send.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | wasmvmtypes "github.com/CosmWasm/wasmvm/v2/types" 5 | ) 6 | 7 | type BlockBeforeSendSudoMsg struct { 8 | BlockBeforeSend BlockBeforeSendMsg `json:"block_before_send,omitempty"` 9 | } 10 | 11 | type TrackBeforeSendSudoMsg struct { 12 | TrackBeforeSend TrackBeforeSendMsg `json:"track_before_send"` 13 | } 14 | 15 | type TrackBeforeSendMsg struct { 16 | From string `json:"from"` 17 | To string `json:"to"` 18 | Amount wasmvmtypes.Coin `json:"amount"` 19 | } 20 | 21 | type BlockBeforeSendMsg struct { 22 | From string `json:"from"` 23 | To string `json:"to"` 24 | Amount wasmvmtypes.Coin `json:"amount"` 25 | } 26 | -------------------------------------------------------------------------------- /x/tokenfactory/types/codec.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "github.com/cosmos/cosmos-sdk/codec" 5 | "github.com/cosmos/cosmos-sdk/codec/legacy" 6 | cdctypes "github.com/cosmos/cosmos-sdk/codec/types" 7 | sdk "github.com/cosmos/cosmos-sdk/types" 8 | 9 | // this line is used by starport scaffolding # 1 10 | "github.com/cosmos/cosmos-sdk/types/msgservice" 11 | ) 12 | 13 | func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) { 14 | legacy.RegisterAminoMsg(cdc, &MsgCreateDenom{}, "tokenfactory/MsgCreateDenom") 15 | legacy.RegisterAminoMsg(cdc, &MsgMint{}, "tokenfactory/MsgMint") 16 | legacy.RegisterAminoMsg(cdc, &MsgBurn{}, "tokenfactory/MsgBurn") 17 | legacy.RegisterAminoMsg(cdc, &MsgChangeAdmin{}, "tokenfactory/MsgChangeAdmin") 18 | legacy.RegisterAminoMsg(cdc, &MsgSetDenomMetadata{}, "tokenfactory/MsgSetDenomMetadata") 19 | legacy.RegisterAminoMsg(cdc, &MsgSetBeforeSendHook{}, "tokenfactory/MsgSetBeforeSendHook") 20 | legacy.RegisterAminoMsg(cdc, &MsgUpdateParams{}, "tokenfactory/MsgUpdateParams") 21 | } 22 | 23 | func RegisterInterfaces(registry cdctypes.InterfaceRegistry) { 24 | registry.RegisterImplementations( 25 | (*sdk.Msg)(nil), 26 | &MsgCreateDenom{}, 27 | &MsgMint{}, 28 | &MsgBurn{}, 29 | &MsgChangeAdmin{}, 30 | &MsgSetDenomMetadata{}, 31 | &MsgSetBeforeSendHook{}, 32 | &MsgUpdateParams{}, 33 | ) 34 | msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc) 35 | } 36 | -------------------------------------------------------------------------------- /x/tokenfactory/types/constants.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | var ( 4 | BeforeSendHookGasLimit = uint64(500_000) 5 | ) 6 | -------------------------------------------------------------------------------- /x/tokenfactory/types/denoms.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "strings" 5 | 6 | "cosmossdk.io/core/address" 7 | errorsmod "cosmossdk.io/errors" 8 | 9 | sdk "github.com/cosmos/cosmos-sdk/types" 10 | ) 11 | 12 | const ( 13 | ModuleDenomPrefix = "factory" 14 | // See the TokenFactory readme for a derivation of these. 15 | // TL;DR, MaxSubdenomLength + MaxHrpLength = 60 comes from SDK max denom length = 128 16 | // and the structure of tokenfactory denoms. 17 | MaxSubdenomLength = 44 18 | MaxHrpLength = 16 19 | // MaxCreatorLength = 59 + MaxHrpLength 20 | MaxCreatorLength = 59 + MaxHrpLength 21 | ) 22 | 23 | // GetTokenDenom constructs a denom string for tokens created by tokenfactory 24 | // based on an input creator address and a subdenom 25 | // The denom constructed is factory/{creator}/{subdenom} 26 | func GetTokenDenom(creator, subdenom string) (string, error) { 27 | if len(subdenom) > MaxSubdenomLength { 28 | return "", ErrSubdenomTooLong 29 | } 30 | if len(creator) > MaxCreatorLength { 31 | return "", ErrCreatorTooLong 32 | } 33 | if strings.Contains(creator, "/") { 34 | return "", ErrInvalidCreator 35 | } 36 | denom := strings.Join([]string{ModuleDenomPrefix, creator, subdenom}, "/") 37 | return denom, sdk.ValidateDenom(denom) 38 | } 39 | 40 | // DeconstructDenom takes a token denom string and verifies that it is a valid 41 | // denom of the tokenfactory module, and is of the form `factory/{creator}/{subdenom}` 42 | // If valid, it returns the creator address and subdenom 43 | func DeconstructDenom(ac address.Codec, denom string) (creator string, subdenom string, err error) { 44 | err = sdk.ValidateDenom(denom) 45 | if err != nil { 46 | return "", "", err 47 | } 48 | 49 | strParts := strings.Split(denom, "/") 50 | if len(strParts) < 3 { 51 | return "", "", errorsmod.Wrapf(ErrInvalidDenom, "not enough parts of denom %s", denom) 52 | } 53 | 54 | if strParts[0] != ModuleDenomPrefix { 55 | return "", "", errorsmod.Wrapf(ErrInvalidDenom, "denom prefix is incorrect. Is: %s. Should be: %s", strParts[0], ModuleDenomPrefix) 56 | } 57 | 58 | creator = strParts[1] 59 | creatorAddr, err := ac.StringToBytes(creator) 60 | if err != nil { 61 | return "", "", errorsmod.Wrapf(ErrInvalidDenom, "Invalid creator address (%s)", err) 62 | } 63 | 64 | // Handle the case where a denom has a slash in its subdenom. For example, 65 | // when we did the split, we'd turn factory/accaddr/atomderivative/sikka into ["factory", "accaddr", "atomderivative", "sikka"] 66 | // So we have to join [2:] with a "/" as the delimiter to get back the correct subdenom which should be "atomderivative/sikka" 67 | subdenom = strings.Join(strParts[2:], "/") 68 | 69 | strAddr, err := ac.BytesToString(creatorAddr) 70 | if err != nil { 71 | return "", "", errorsmod.Wrapf(ErrInvalidDenom, "Invalid creator address (%s)", err) 72 | } 73 | 74 | return strAddr, subdenom, nil 75 | } 76 | -------------------------------------------------------------------------------- /x/tokenfactory/types/denoms_test.go: -------------------------------------------------------------------------------- 1 | package types_test 2 | 3 | import ( 4 | fmt "fmt" 5 | "testing" 6 | 7 | "github.com/cometbft/cometbft/crypto/ed25519" 8 | "github.com/stretchr/testify/require" 9 | 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" 12 | 13 | "github.com/initia-labs/miniwasm/x/tokenfactory/types" 14 | ) 15 | 16 | func TestDeconstructDenom(t *testing.T) { 17 | ac := authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()) 18 | bytesAddr := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) 19 | creator, err := ac.BytesToString(bytesAddr) 20 | require.NoError(t, err, "address bytes encoding error") 21 | 22 | for _, tc := range []struct { 23 | desc string 24 | denom string 25 | expectedSubdenom string 26 | err error 27 | }{ 28 | { 29 | desc: "empty is invalid", 30 | denom: "", 31 | err: types.ErrInvalidDenom, 32 | }, 33 | { 34 | desc: "normal", 35 | denom: fmt.Sprintf("factory/%s/bitcoin", creator), 36 | expectedSubdenom: "bitcoin", 37 | }, 38 | { 39 | desc: "multiple slashes in subdenom", 40 | denom: fmt.Sprintf("factory/%s/bitcoin/1", creator), 41 | expectedSubdenom: "bitcoin/1", 42 | }, 43 | { 44 | desc: "no subdenom", 45 | denom: fmt.Sprintf("factory/%s/", creator), 46 | expectedSubdenom: "", 47 | }, 48 | { 49 | desc: "incorrect prefix", 50 | denom: fmt.Sprintf("ibc/%s/bitcoin", creator), 51 | err: types.ErrInvalidDenom, 52 | }, 53 | { 54 | desc: "subdenom of only slashes", 55 | denom: fmt.Sprintf("factory/%s/////", creator), 56 | expectedSubdenom: "////", 57 | }, 58 | { 59 | desc: "too long name", 60 | denom: fmt.Sprintf("factory/%s/adsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsf", creator), 61 | err: types.ErrInvalidDenom, 62 | }, 63 | } { 64 | t.Run(tc.desc, func(t *testing.T) { 65 | expectedCreator := creator 66 | creator, subdenom, err := types.DeconstructDenom(ac, tc.denom) 67 | if tc.err != nil { 68 | require.ErrorContains(t, err, tc.err.Error()) 69 | } else { 70 | require.NoError(t, err) 71 | require.Equal(t, expectedCreator, creator) 72 | require.Equal(t, tc.expectedSubdenom, subdenom) 73 | } 74 | }) 75 | } 76 | } 77 | 78 | func TestGetTokenDenom(t *testing.T) { 79 | ac := authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()) 80 | bytesAddr := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) 81 | creator, err := ac.BytesToString(bytesAddr) 82 | require.NoError(t, err, "address bytes encoding error") 83 | 84 | for _, tc := range []struct { 85 | desc string 86 | creator string 87 | subdenom string 88 | valid bool 89 | }{ 90 | { 91 | desc: "normal", 92 | creator: creator, 93 | subdenom: "bitcoin", 94 | valid: true, 95 | }, 96 | { 97 | desc: "multiple slashes in subdenom", 98 | creator: creator, 99 | subdenom: "bitcoin/1", 100 | valid: true, 101 | }, 102 | { 103 | desc: "no subdenom", 104 | creator: creator, 105 | subdenom: "", 106 | valid: true, 107 | }, 108 | { 109 | desc: "subdenom of only slashes", 110 | creator: creator, 111 | subdenom: "/////", 112 | valid: true, 113 | }, 114 | { 115 | desc: "too long name", 116 | creator: creator, 117 | subdenom: "adsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsfadsf", 118 | valid: false, 119 | }, 120 | { 121 | desc: "subdenom is exactly max length", 122 | creator: creator, 123 | subdenom: "bitcoinfsadfsdfeadfsafwefsefsefsdfsdafasefsf", 124 | valid: true, 125 | }, 126 | { 127 | desc: "creator is exactly max length", 128 | creator: "init1t7egva48prqmzl59x5ngv4zx0dtrwewc9m7z44jhgjhgkhjklhkjhkjhgjhgjgjghelugt", 129 | subdenom: "bitcoin", 130 | valid: true, 131 | }, 132 | } { 133 | t.Run(tc.desc, func(t *testing.T) { 134 | _, err := types.GetTokenDenom(tc.creator, tc.subdenom) 135 | if tc.valid { 136 | require.NoError(t, err) 137 | } else { 138 | require.Error(t, err) 139 | } 140 | }) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /x/tokenfactory/types/errors.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // DONTCOVER 4 | 5 | import ( 6 | fmt "fmt" 7 | 8 | errorsmod "cosmossdk.io/errors" 9 | ) 10 | 11 | // x/tokenfactory module sentinel errors 12 | var ( 13 | ErrEmptySender = errorsmod.Register(ModuleName, 2, "empty sender") 14 | ErrEmptyMintToAddress = errorsmod.Register(ModuleName, 3, "empty mint-to-address") 15 | ErrEmptyTransferFromAddress = errorsmod.Register(ModuleName, 4, "empty transfer-from-address") 16 | ErrEmptyTransferToAddress = errorsmod.Register(ModuleName, 5, "empty transfer-to-address") 17 | ErrEmptyNewAdmin = errorsmod.Register(ModuleName, 6, "empty new-admin") 18 | ErrDenomExists = errorsmod.Register(ModuleName, 7, "attempting to create a denom that already exists (has bank metadata)") 19 | ErrUnauthorized = errorsmod.Register(ModuleName, 8, "unauthorized account") 20 | ErrInvalidDenom = errorsmod.Register(ModuleName, 9, "invalid denom") 21 | ErrInvalidCreator = errorsmod.Register(ModuleName, 10, "invalid creator") 22 | ErrInvalidAuthorityMetadata = errorsmod.Register(ModuleName, 11, "invalid authority metadata") 23 | ErrInvalidGenesis = errorsmod.Register(ModuleName, 12, "invalid genesis") 24 | ErrSubdenomTooLong = errorsmod.Register(ModuleName, 13, fmt.Sprintf("subdenom too long, max length is %d bytes", MaxSubdenomLength)) 25 | ErrCreatorTooLong = errorsmod.Register(ModuleName, 14, fmt.Sprintf("creator too long, max length is %d bytes", MaxCreatorLength)) 26 | ErrDenomDoesNotExist = errorsmod.Register(ModuleName, 15, "denom does not exist") 27 | ErrBurnFromModuleAccount = errorsmod.Register(ModuleName, 16, "burning from Module Account is not allowed") 28 | ErrBeforeSendHookOutOfGas = errorsmod.Register(ModuleName, 17, "gas meter hit maximum limit") 29 | ) 30 | -------------------------------------------------------------------------------- /x/tokenfactory/types/events.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | // event types 4 | const ( 5 | AttributeAmount = "amount" 6 | AttributeCreator = "creator" 7 | AttributeSubdenom = "subdenom" 8 | AttributeNewTokenDenom = "new_token_denom" 9 | AttributeMintToAddress = "mint_to_address" 10 | AttributeBurnFromAddress = "burn_from_address" 11 | AttributeTransferFromAddress = "transfer_from_address" 12 | AttributeTransferToAddress = "transfer_to_address" 13 | AttributeDenom = "denom" 14 | AttributeNewAdmin = "new_admin" 15 | AttributeDenomMetadata = "denom_metadata" 16 | AttributeBeforeSendHookAddress = "before_send_hook_address" 17 | ) 18 | -------------------------------------------------------------------------------- /x/tokenfactory/types/expected_keepers.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "context" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 8 | ) 9 | 10 | type BankKeeper interface { 11 | // Methods imported from bank should be defined here 12 | GetDenomMetaData(ctx context.Context, denom string) (banktypes.Metadata, bool) 13 | SetDenomMetaData(ctx context.Context, denomMetaData banktypes.Metadata) 14 | 15 | HasSupply(ctx context.Context, denom string) bool 16 | 17 | SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error 18 | SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error 19 | MintCoins(ctx context.Context, moduleName string, amt sdk.Coins) error 20 | BurnCoins(ctx context.Context, moduleName string, amt sdk.Coins) error 21 | 22 | SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error 23 | HasBalance(ctx context.Context, addr sdk.AccAddress, amt sdk.Coin) bool 24 | 25 | BlockedAddr(addr sdk.AccAddress) bool 26 | } 27 | 28 | type AccountKeeper interface { 29 | GetAccount(context.Context, sdk.AccAddress) sdk.AccountI 30 | GetModuleAccount(ctx context.Context, moduleName string) sdk.ModuleAccountI 31 | } 32 | 33 | // BankHooks event hooks 34 | type BankHooks interface { 35 | TrackBeforeSend(ctx context.Context, from, to sdk.AccAddress, amount sdk.Coins) // Must be before any send is executed 36 | BlockBeforeSend(ctx context.Context, from, to sdk.AccAddress, amount sdk.Coins) error // Must be before any send is executed 37 | } 38 | 39 | // CommunityPoolKeeper defines the contract needed to be fulfilled for community pool interactions. 40 | type CommunityPoolKeeper interface { 41 | FundCommunityPool(ctx context.Context, amount sdk.Coins, sender sdk.AccAddress) error 42 | } 43 | 44 | type ContractKeeper interface { 45 | Sudo(ctx sdk.Context, contractAddress sdk.AccAddress, msg []byte) ([]byte, error) 46 | } 47 | -------------------------------------------------------------------------------- /x/tokenfactory/types/genesis.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "cosmossdk.io/core/address" 5 | errorsmod "cosmossdk.io/errors" 6 | ) 7 | 8 | // this line is used by starport scaffolding # genesis/types/import 9 | 10 | // DefaultIndex is the default capability global index 11 | const DefaultIndex uint64 = 1 12 | 13 | // DefaultGenesis returns the default Capability genesis state 14 | func DefaultGenesis() *GenesisState { 15 | return &GenesisState{ 16 | Params: DefaultParams(), 17 | FactoryDenoms: []GenesisDenom{}, 18 | } 19 | } 20 | 21 | // Validate performs basic genesis state validation returning an error upon any 22 | // failure. 23 | func (gs GenesisState) Validate(ac address.Codec) error { 24 | err := gs.Params.Validate() 25 | if err != nil { 26 | return err 27 | } 28 | 29 | seenDenoms := map[string]bool{} 30 | 31 | for _, denom := range gs.GetFactoryDenoms() { 32 | if seenDenoms[denom.GetDenom()] { 33 | return errorsmod.Wrapf(ErrInvalidGenesis, "duplicate denom: %s", denom.GetDenom()) 34 | } 35 | seenDenoms[denom.GetDenom()] = true 36 | 37 | _, _, err := DeconstructDenom(ac, denom.GetDenom()) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | if denom.AuthorityMetadata.Admin != "" { 43 | _, err = ac.StringToBytes(denom.AuthorityMetadata.Admin) 44 | if err != nil { 45 | return errorsmod.Wrapf(ErrInvalidAuthorityMetadata, "Invalid admin address (%s)", err) 46 | } 47 | } 48 | } 49 | 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /x/tokenfactory/types/genesis_test.go: -------------------------------------------------------------------------------- 1 | package types_test 2 | 3 | import ( 4 | fmt "fmt" 5 | "testing" 6 | 7 | "github.com/cometbft/cometbft/crypto/ed25519" 8 | "github.com/stretchr/testify/require" 9 | 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | authcodec "github.com/cosmos/cosmos-sdk/x/auth/codec" 12 | 13 | "github.com/initia-labs/miniwasm/x/tokenfactory/types" 14 | ) 15 | 16 | func TestGenesisState_Validate(t *testing.T) { 17 | ac := authcodec.NewBech32Codec(sdk.GetConfig().GetBech32AccountAddrPrefix()) 18 | bytesAddr := sdk.AccAddress(ed25519.GenPrivKey().PubKey().Address()) 19 | creator, err := ac.BytesToString(bytesAddr) 20 | require.NoError(t, err, "address bytes encoding error") 21 | 22 | bitcoin := fmt.Sprintf("factory/%s/bitcoin", creator) 23 | litecoin := fmt.Sprintf("factory/%s/litecoin", creator) 24 | 25 | for _, tc := range []struct { 26 | desc string 27 | genState *types.GenesisState 28 | valid bool 29 | }{ 30 | { 31 | desc: "default is valid", 32 | genState: types.DefaultGenesis(), 33 | valid: true, 34 | }, 35 | { 36 | desc: "valid genesis state", 37 | genState: &types.GenesisState{ 38 | FactoryDenoms: []types.GenesisDenom{ 39 | { 40 | Denom: bitcoin, 41 | AuthorityMetadata: types.DenomAuthorityMetadata{ 42 | Admin: creator, 43 | }, 44 | }, 45 | }, 46 | }, 47 | valid: true, 48 | }, 49 | { 50 | desc: "different admin from creator", 51 | genState: &types.GenesisState{ 52 | FactoryDenoms: []types.GenesisDenom{ 53 | { 54 | Denom: bitcoin, 55 | AuthorityMetadata: types.DenomAuthorityMetadata{ 56 | Admin: creator, 57 | }, 58 | }, 59 | }, 60 | }, 61 | valid: true, 62 | }, 63 | { 64 | desc: "empty admin", 65 | genState: &types.GenesisState{ 66 | FactoryDenoms: []types.GenesisDenom{ 67 | { 68 | Denom: bitcoin, 69 | AuthorityMetadata: types.DenomAuthorityMetadata{ 70 | Admin: "", 71 | }, 72 | }, 73 | }, 74 | }, 75 | valid: true, 76 | }, 77 | { 78 | desc: "no admin", 79 | genState: &types.GenesisState{ 80 | FactoryDenoms: []types.GenesisDenom{ 81 | { 82 | Denom: bitcoin, 83 | }, 84 | }, 85 | }, 86 | valid: true, 87 | }, 88 | { 89 | desc: "invalid admin", 90 | genState: &types.GenesisState{ 91 | FactoryDenoms: []types.GenesisDenom{ 92 | { 93 | Denom: bitcoin, 94 | AuthorityMetadata: types.DenomAuthorityMetadata{ 95 | Admin: "moose", 96 | }, 97 | }, 98 | }, 99 | }, 100 | valid: false, 101 | }, 102 | { 103 | desc: "multiple denoms", 104 | genState: &types.GenesisState{ 105 | FactoryDenoms: []types.GenesisDenom{ 106 | { 107 | Denom: bitcoin, 108 | AuthorityMetadata: types.DenomAuthorityMetadata{ 109 | Admin: "", 110 | }, 111 | }, 112 | { 113 | Denom: litecoin, 114 | AuthorityMetadata: types.DenomAuthorityMetadata{ 115 | Admin: "", 116 | }, 117 | }, 118 | }, 119 | }, 120 | valid: true, 121 | }, 122 | { 123 | desc: "duplicate denoms", 124 | genState: &types.GenesisState{ 125 | FactoryDenoms: []types.GenesisDenom{ 126 | { 127 | Denom: bitcoin, 128 | AuthorityMetadata: types.DenomAuthorityMetadata{ 129 | Admin: "", 130 | }, 131 | }, 132 | { 133 | Denom: bitcoin, 134 | AuthorityMetadata: types.DenomAuthorityMetadata{ 135 | Admin: "", 136 | }, 137 | }, 138 | }, 139 | }, 140 | valid: false, 141 | }, 142 | } { 143 | t.Run(tc.desc, func(t *testing.T) { 144 | err := tc.genState.Validate(ac) 145 | if tc.valid { 146 | require.NoError(t, err) 147 | } else { 148 | require.Error(t, err) 149 | } 150 | }) 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /x/tokenfactory/types/keys.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | const ( 4 | // ModuleName defines the module name 5 | ModuleName = "tokenfactory" 6 | 7 | // StoreKey defines the primary module store key 8 | StoreKey = ModuleName 9 | 10 | // RouterKey is the message route for slashing 11 | RouterKey = ModuleName 12 | 13 | // QuerierRoute defines the module's query routing key 14 | QuerierRoute = ModuleName 15 | 16 | // MemStoreKey defines the in-memory store key 17 | MemStoreKey = "mem_tokenfactory" 18 | ) 19 | 20 | var ( 21 | CreatorDenomsPrefix = []byte{0x11} 22 | DenomAuthorityPrefix = []byte{0x12} 23 | DenomHookAddrPrefix = []byte{0x13} 24 | ParamsKeyPrefix = []byte{0x14} 25 | ) 26 | -------------------------------------------------------------------------------- /x/tokenfactory/types/msgs.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "cosmossdk.io/core/address" 5 | 6 | sdkmath "cosmossdk.io/math" 7 | 8 | errorsmod "cosmossdk.io/errors" 9 | 10 | sdk "github.com/cosmos/cosmos-sdk/types" 11 | sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" 12 | banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" 13 | ) 14 | 15 | // constants 16 | const ( 17 | TypeMsgCreateDenom = "create_denom" 18 | TypeMsgMint = "tf_mint" 19 | TypeMsgBurn = "tf_burn" 20 | TypeMsgForceTransfer = "force_transfer" 21 | TypeMsgChangeAdmin = "change_admin" 22 | TypeMsgSetDenomMetadata = "set_denom_metadata" 23 | TypeMsgSetBeforeSendHook = "set_before_send_hook" 24 | ) 25 | 26 | var ( 27 | _ sdk.Msg = &MsgCreateDenom{} 28 | _ sdk.Msg = &MsgMint{} 29 | _ sdk.Msg = &MsgBurn{} 30 | _ sdk.Msg = &MsgChangeAdmin{} 31 | _ sdk.Msg = &MsgSetDenomMetadata{} 32 | _ sdk.Msg = &MsgSetBeforeSendHook{} 33 | ) 34 | 35 | // NewMsgCreateDenom creates a msg to create a new denom 36 | func NewMsgCreateDenom(sender, subdenom string) *MsgCreateDenom { 37 | return &MsgCreateDenom{ 38 | Sender: sender, 39 | Subdenom: subdenom, 40 | } 41 | } 42 | 43 | func (m MsgCreateDenom) Validate(accAddrCodec address.Codec) error { 44 | if addr, err := accAddrCodec.StringToBytes(m.Sender); err != nil { 45 | return err 46 | } else if len(addr) == 0 { 47 | return ErrEmptySender 48 | } 49 | 50 | if _, err := GetTokenDenom(m.Sender, m.Subdenom); err != nil { 51 | return ErrInvalidDenom 52 | } 53 | 54 | return nil 55 | } 56 | 57 | // NewMsgMint creates a message to mint tokens 58 | func NewMsgMint(sender string, amount sdk.Coin) *MsgMint { 59 | return &MsgMint{ 60 | Sender: sender, 61 | Amount: amount, 62 | } 63 | } 64 | 65 | func NewMsgMintTo(sender string, amount sdk.Coin, mintToAddress string) *MsgMint { 66 | return &MsgMint{ 67 | Sender: sender, 68 | Amount: amount, 69 | MintToAddress: mintToAddress, 70 | } 71 | } 72 | 73 | func (m MsgMint) Validate(accAddrCodec address.Codec) error { 74 | if addr, err := accAddrCodec.StringToBytes(m.Sender); err != nil { 75 | return err 76 | } else if len(addr) == 0 { 77 | return ErrEmptySender 78 | } 79 | 80 | if !m.Amount.IsValid() || m.Amount.Amount.Equal(sdkmath.ZeroInt()) { 81 | return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, m.Amount.String()) 82 | } 83 | 84 | return nil 85 | } 86 | 87 | // NewMsgBurn creates a message to burn tokens 88 | func NewMsgBurn(sender string, amount sdk.Coin) *MsgBurn { 89 | return &MsgBurn{ 90 | Sender: sender, 91 | Amount: amount, 92 | } 93 | } 94 | 95 | func (m MsgBurn) Validate(accAddrCodec address.Codec) error { 96 | if addr, err := accAddrCodec.StringToBytes(m.Sender); err != nil { 97 | return err 98 | } else if len(addr) == 0 { 99 | return ErrEmptySender 100 | } 101 | 102 | if !m.Amount.IsValid() || m.Amount.Amount.Equal(sdkmath.ZeroInt()) { 103 | return errorsmod.Wrap(sdkerrors.ErrInvalidCoins, m.Amount.String()) 104 | } 105 | 106 | return nil 107 | } 108 | 109 | // NewMsgChangeAdmin creates a message to change the admin of a denom 110 | func NewMsgChangeAdmin(sender, denom, newAdmin string) *MsgChangeAdmin { 111 | return &MsgChangeAdmin{ 112 | Sender: sender, 113 | Denom: denom, 114 | NewAdmin: newAdmin, 115 | } 116 | } 117 | 118 | func (m MsgChangeAdmin) Validate(accAddrCodec address.Codec) error { 119 | if _, err := accAddrCodec.StringToBytes(m.Sender); err != nil { 120 | return err 121 | } 122 | 123 | // allow empty address 124 | if len(m.NewAdmin) > 0 { 125 | if _, err := accAddrCodec.StringToBytes(m.NewAdmin); err != nil { 126 | return err 127 | } 128 | } 129 | 130 | if _, _, err := DeconstructDenom(accAddrCodec, m.Denom); err != nil { 131 | return err 132 | } 133 | 134 | return nil 135 | } 136 | 137 | // NewMsgChangeAdmin creates a message to set metadata for a denom 138 | func NewMsgSetDenomMetadata(sender string, metadata banktypes.Metadata) *MsgSetDenomMetadata { 139 | return &MsgSetDenomMetadata{ 140 | Sender: sender, 141 | Metadata: metadata, 142 | } 143 | } 144 | 145 | func (m MsgSetDenomMetadata) Validate(accAddrCodec address.Codec) error { 146 | if addr, err := accAddrCodec.StringToBytes(m.Sender); err != nil { 147 | return err 148 | } else if len(addr) == 0 { 149 | return ErrEmptySender 150 | } 151 | 152 | if err := m.Metadata.Validate(); err != nil { 153 | return err 154 | } 155 | 156 | if _, _, err := DeconstructDenom(accAddrCodec, m.Metadata.Base); err != nil { 157 | return err 158 | } 159 | return nil 160 | } 161 | 162 | // NewMsgSetBeforeSendHook create a message to set a before-send hook for a denom 163 | func NewMsgSetBeforeSendHook(sender string, denom string, cosmwasmAddress string) *MsgSetBeforeSendHook { 164 | return &MsgSetBeforeSendHook{ 165 | Sender: sender, 166 | Denom: denom, 167 | CosmwasmAddress: cosmwasmAddress, 168 | } 169 | } 170 | 171 | func (m MsgSetBeforeSendHook) Validate(accAddrCodec address.Codec) error { 172 | if addr, err := accAddrCodec.StringToBytes(m.Sender); err != nil { 173 | return err 174 | } else if len(addr) == 0 { 175 | return ErrEmptySender 176 | } 177 | 178 | if _, _, err := DeconstructDenom(accAddrCodec, m.Denom); err != nil { 179 | return ErrInvalidDenom 180 | } 181 | return nil 182 | } 183 | -------------------------------------------------------------------------------- /x/tokenfactory/types/params.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "fmt" 5 | 6 | sdk "github.com/cosmos/cosmos-sdk/types" 7 | ) 8 | 9 | // Parameter store keys. 10 | var ( 11 | KeyDenomCreationFee = []byte("DenomCreationFee") 12 | KeyDenomCreationGasConsume = []byte("DenomCreationGasConsume") 13 | 14 | // chosen as an arbitrary large number, less than the max_gas_wanted_per_tx in config. 15 | DefaultCreationGasFee = 1_000_000 16 | ) 17 | 18 | func NewParams(denomCreationFee sdk.Coins, denomCreationGasConsume uint64) Params { 19 | return Params{ 20 | DenomCreationFee: denomCreationFee, 21 | DenomCreationGasConsume: denomCreationGasConsume, 22 | } 23 | } 24 | 25 | // default gamm module parameters. 26 | func DefaultParams() Params { 27 | return Params{ 28 | // For choice, see: https://github.com/osmosis-labs/osmosis/pull/4983 29 | DenomCreationFee: sdk.NewCoins(), // used to be 10 OSMO at launch. 30 | DenomCreationGasConsume: uint64(DefaultCreationGasFee), 31 | } 32 | } 33 | 34 | // validate params. 35 | func (p Params) Validate() error { 36 | if err := validateDenomCreationFee(p.DenomCreationFee); err != nil { 37 | return err 38 | } 39 | 40 | if err := validateDenomCreationGasConsume(p.DenomCreationGasConsume); err != nil { 41 | return err 42 | } 43 | 44 | return nil 45 | } 46 | 47 | func validateDenomCreationFee(i any) error { 48 | v, ok := i.(sdk.Coins) 49 | if !ok { 50 | return fmt.Errorf("invalid parameter type: %T", i) 51 | } 52 | 53 | if v.Validate() != nil { 54 | return fmt.Errorf("invalid denom creation fee: %+v", i) 55 | } 56 | 57 | return nil 58 | } 59 | 60 | func validateDenomCreationGasConsume(i any) error { 61 | _, ok := i.(uint64) 62 | if !ok { 63 | return fmt.Errorf("invalid parameter type: %T", i) 64 | } 65 | 66 | return nil 67 | } 68 | --------------------------------------------------------------------------------