Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
02a9298
Fixed protos generation (TODO IBC protos)
MissingNO57 Aug 19, 2025
e461eb0
gogo proto fix
MissingNO57 Aug 20, 2025
64939ca
Merge branch 'main' of github.com:fetchai/cosmpy into feat/cosmos_sdk…
MissingNO57 Nov 13, 2025
3b640bb
IBC protos fix
MissingNO57 Nov 13, 2025
b342d2e
IBC fixes and disabled integration tests
MissingNO57 Nov 14, 2025
1a47cca
Fixes
MissingNO57 Nov 14, 2025
273024f
Lint
MissingNO57 Nov 14, 2025
8946eea
Mypy fix
MissingNO57 Nov 14, 2025
d804abd
Fixes and rework
MissingNO57 Nov 14, 2025
1a60462
Test fix
MissingNO57 Nov 14, 2025
922b72d
Tests
MissingNO57 Nov 14, 2025
7dfe64e
Black fix
MissingNO57 Nov 14, 2025
b202a51
lint fixes
MissingNO57 Nov 14, 2025
0542107
docs
MissingNO57 Nov 14, 2025
8cebe9c
dos
MissingNO57 Nov 14, 2025
f5ef414
SPelling fix
MissingNO57 Nov 14, 2025
d09299a
Legacy support
MissingNO57 Nov 17, 2025
126c5a2
Fixes
MissingNO57 Nov 17, 2025
5f9ac06
Lint fix
MissingNO57 Nov 17, 2025
31a9583
Docs fix
MissingNO57 Nov 17, 2025
94aff5b
feat: Docker localnet for integration tests (#425)
MissingNO57 Dec 2, 2025
8ffff11
refactor: Changed fetchd docker checkout to 0.20 feature branch (#426)
MissingNO57 Dec 4, 2025
2acdd32
chore: add history entry and bump version to 0.12.0-rc0 for release (…
MissingNO57 Dec 5, 2025
321e311
Merge branch 'main' into feat/cosmos_sdk_0_53
MissingNO57 Dec 8, 2025
785a179
chore: add history entry and bump version to 0.12.0rc0 for release (#…
MissingNO57 Dec 8, 2025
c92e0cd
fix: Init files (#430)
MissingNO57 Dec 18, 2025
89539d8
chore: add history entry and bump version to 0.12.0rc1 for release (#…
MissingNO57 Dec 18, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
42 changes: 38 additions & 4 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,18 +89,52 @@ jobs:
matrix:
os:
- ubuntu-latest
- windows-latest
- macos-latest
python-version:
- 3.9
- '3.9'
timeout-minutes: 30
steps:
- uses: actions/checkout@v3

- uses: actions/setup-python@v5
with:
python-version: '${{ matrix.python-version }}'

- name: Install dependencies
run: pip install tox==3.25.1

# Checkout docker repo
- name: Checkout fetchd docker repo
uses: actions/checkout@v4
with:
repository: fetchai/fetchd
ref: feat/cosmos_sdk_update_0_20_0
path: repo-docker
token: ${{ secrets.GITHUB_TOKEN }}

- name: Build and run docker compose
working-directory: repo-docker
run: |
docker compose build
docker compose up -d

- name: Wait for server
run: |
waiting_period=3
for i in {1..30}; do
elapsed_time=$((i * waiting_period))

if curl -s http://localhost:26657/status >/dev/null; then
echo "Server is ready!"
exit 0
fi

echo "Waiting for the node to be ready (retry after ${elapsed_time}s) ..."
sleep "$waiting_period"
done

echo "Server did not become ready in time"
exit 1

- name: Integration Tests
run: |
tox -e test-integration
Expand All @@ -114,7 +148,7 @@ jobs:
- windows-latest
- macos-latest
python-version:
- 3.9
- '3.9'
timeout-minutes: 30
steps:
- uses: actions/checkout@v3
Expand Down
4 changes: 4 additions & 0 deletions .wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -334,3 +334,7 @@ iterable
denoms
impl
bcrypt
QueryParamsResponse
ConsensusRestClient
CosmosSDK
NodeInfo
8 changes: 8 additions & 0 deletions HISTORY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Release History

## 0.12.0rc1

- fix: Init files

## 0.12.0rc0

- feat: Cosmos SDK v0.53.4

## 0.11.2

- feat: Ability to use Cosmos SDK bcrypt armored key files
Expand Down
53 changes: 42 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
COSMOS_SDK_URL := https://github.com/fetchai/cosmos-sdk
COSMOS_SDK_VERSION := v0.18.0
COSMOS_SDK_VERSION := v0.20.0-rc0
COSMOS_SDK_DIR := build/cosmos-sdk-proto-schema

WASMD_URL := https://github.com/CosmWasm/wasmd
WASMD_VERSION := v0.27.0
WASMD_VERSION := v0.61.2
WASMD_DIR := build/wasm-proto-shema

IBCGO_URL := https://github.com/cosmos/ibc-go
IBCGO_VERSION := v2.2.0
IBCGO_VERSION := v10.2.0
IBCGO_DIR := build/ibcgo-proto-schema

ICS23_URL := https://github.com/cosmos/ics23
ICS23_VERSION := go/v0.11.0
ICS23_DIR := build/ics23-protoIBC

GOGOPROTO_URL := https://github.com/cosmos/gogoproto
GOGOPROTO_VERSION := v1.7.0
GOGOPROTO_DIR := build/gogo-proto

COSMOSPROTO_URL := https://github.com/cosmos/cosmos-proto
COSMOSPROTO_VERSION := v1.0.0-beta.5
COSMOSPROTO_DIR := build/cosmos-proto

PY_GOOGLEAPIS_ROOT := $(shell python -c "import importlib.util, pathlib, sys; s=importlib.util.find_spec('google.api'); print(pathlib.Path(next(iter(s.submodule_search_locations))).parents[1]) if s and s.submodule_search_locations else sys.stdout.write('')")

COSMPY_PROTOS_DIR := cosmpy/protos
COSMPY_SRC_DIR := cosmpy
COSMPY_TESTS_DIR := tests
Expand All @@ -22,7 +36,7 @@ PYTHON_CODE_DIRS := $(COSMPY_SRC_DIR) $(COSMPY_TESTS_DIR) $(COSMPY_EXAMPLES_DIR)
### Initialise dev environment
########################################

# Create a new poetry virtual environment with all the necessary dependencies installed.
# Create a new poetry virtual environment with all the necessary dependencies installed.
# Once finished, `poetry shell` to enter the virtual environment
v := $(shell pip -V | grep virtualenvs)

Expand Down Expand Up @@ -258,14 +272,18 @@ unique = $(if $1,$(firstword $1) $(call unique,$(filter-out $(firstword $1),$1))

proto: fetch_proto_schema_source generate_proto_types generate_init_py_files

generate_proto_types: $(COSMOS_SDK_DIR) $(WASMD_DIR) $(IBCGO_DIR)
generate_proto_types: $(COSMOS_SDK_DIR) $(WASMD_DIR) $(IBCGO_DIR) $(GOGOPROTO_DIR) $(COSMOSPROTO_DIR) $(PY_GOOGLEAPIS_ROOT)
rm -frv $(COSMPY_PROTOS_DIR)/*
python -m grpc_tools.protoc --proto_path=$(WASMD_DIR)/proto --proto_path=$(WASMD_DIR)/third_party/proto --python_out=$(COSMPY_PROTOS_DIR) --grpc_python_out=$(COSMPY_PROTOS_DIR) $(shell find $(WASMD_DIR) \( -path */proto/* -or -path */third_party/proto/* \) -type f -name *.proto)
python -m grpc_tools.protoc --proto_path=$(IBCGO_DIR)/proto --proto_path=$(IBCGO_DIR)/third_party/proto --python_out=$(COSMPY_PROTOS_DIR) --grpc_python_out=$(COSMPY_PROTOS_DIR) $(shell find $(IBCGO_DIR) \( -path */proto/* -or -path */third_party/proto/* \) -type f -name *.proto)
python -m grpc_tools.protoc --proto_path=$(WASMD_DIR)/proto --proto_path=$(GOGOPROTO_DIR) --proto_path=$(COSMOSPROTO_DIR)/proto --proto_path=$(PY_GOOGLEAPIS_ROOT) --proto_path=$(COSMOS_SDK_DIR)/proto --python_out=$(COSMPY_PROTOS_DIR) --grpc_python_out=$(COSMPY_PROTOS_DIR) $(shell find $(WASMD_DIR) \( -path */proto/* -or -path */third_party/proto/* \) -type f -name *.proto)
python -m grpc_tools.protoc --proto_path=$(IBCGO_DIR)/proto --proto_path=$(GOGOPROTO_DIR) --proto_path=$(COSMOSPROTO_DIR)/proto --proto_path=$(PY_GOOGLEAPIS_ROOT) --proto_path=$(COSMOS_SDK_DIR)/proto --python_out=$(COSMPY_PROTOS_DIR) --grpc_python_out=$(COSMPY_PROTOS_DIR) $(shell find $(IBCGO_DIR) \( -path */proto/* -or -path *///third_party/proto/* \) -type f -name *.proto) --proto_path=$(COSMOS_SDK_DIR)/third_party/proto --proto_path=$(ICS23_DIR)/proto
python -m grpc_tools.protoc --proto_path=$(ICS23_DIR)/proto --proto_path=$(GOGOPROTO_DIR) --proto_path=$(COSMOSPROTO_DIR)/proto --proto_path=$(PY_GOOGLEAPIS_ROOT) --proto_path=$(COSMOS_SDK_DIR)/proto --python_out=$(COSMPY_PROTOS_DIR) --grpc_python_out=$(COSMPY_PROTOS_DIR) $(shell find $(ICS23_DIR) \( -path */proto/* -or -path *///third_party/proto/* \) -type f -name *.proto)
# ensure cosmos-sdk is last as previous modules may have duplicated proto models which are now outdated
python -m grpc_tools.protoc --proto_path=$(COSMOS_SDK_DIR)/proto --proto_path=$(COSMOS_SDK_DIR)/third_party/proto --python_out=$(COSMPY_PROTOS_DIR) --grpc_python_out=$(COSMPY_PROTOS_DIR) $(shell find $(COSMOS_SDK_DIR) \( -path */proto/* -or -path */third_party/proto/* \) -type f -name *.proto)
python -m grpc_tools.protoc --proto_path=$(COSMOS_SDK_DIR)/proto --python_out=$(COSMPY_PROTOS_DIR) --proto_path=$(GOGOPROTO_DIR) --proto_path=$(COSMOSPROTO_DIR)/proto --proto_path=$(PY_GOOGLEAPIS_ROOT) --proto_path=$(COSMOS_SDK_DIR)/proto --grpc_python_out=$(COSMPY_PROTOS_DIR) $(shell find $(COSMOS_SDK_DIR) \( -path */proto/* -or -path */third_party/proto/* \) -type f -name *.proto)
python -m grpc_tools.protoc --proto_path=$(COSMOS_SDK_DIR)/proto --python_out=$(COSMPY_PROTOS_DIR) --proto_path=$(GOGOPROTO_DIR) --proto_path=$(COSMOSPROTO_DIR)/proto --proto_path=$(PY_GOOGLEAPIS_ROOT) --proto_path=$(COSMOS_SDK_DIR)/proto --grpc_python_out=$(COSMPY_PROTOS_DIR) $(shell find $(GOGOPROTO_DIR) \( -path */gogoproto/* \) -type f -name *.proto)
python -m grpc_tools.protoc --proto_path=$(COSMOS_SDK_DIR)/proto --python_out=$(COSMPY_PROTOS_DIR) --proto_path=$(GOGOPROTO_DIR) --proto_path=$(COSMOSPROTO_DIR)/proto --proto_path=$(PY_GOOGLEAPIS_ROOT) --proto_path=$(COSMOS_SDK_DIR)/proto --grpc_python_out=$(COSMPY_PROTOS_DIR) $(shell find $(COSMOSPROTO_DIR) \( -path */proto/* \) -type f -name *.proto)

fetch_proto_schema_source: $(COSMOS_SDK_DIR) $(WASMD_DIR) $(IBCGO_DIR)

fetch_proto_schema_source: $(COSMOS_SDK_DIR) $(WASMD_DIR) $(ICS23_DIR) $(IBCGO_DIR) $(GOGOPROTO_DIR) $(COSMOSPROTO_DIR)

.PHONY: generate_init_py_files
generate_init_py_files: generate_proto_types
Expand All @@ -281,7 +299,7 @@ $(GENERATED): $(SOURCE)
$(INIT_PY_FILES_TO_CREATE): $(GENERATED_DIRS)
touch $(INIT_PY_FILES_TO_CREATE)

$(GENERATED_DIRS): $(COSMOS_SDK_DIR) $(WASMD_DIR) $(IBCGO_DIR)
$(GENERATED_DIRS): $(COSMOS_SDK_DIR) $(WASMD_DIR) $(IBCGO_DIR) $(GOGOPROTO_DIR) $(COSMOSPROTO_DIR)

$(COSMOS_SDK_DIR): Makefile
rm -rfv $(COSMOS_SDK_DIR)
Expand All @@ -298,6 +316,19 @@ $(IBCGO_DIR): Makefile
git clone --branch $(IBCGO_VERSION) --depth 1 --quiet --no-checkout --filter=blob:none $(IBCGO_URL) $(IBCGO_DIR)
cd $(IBCGO_DIR) && git checkout $(IBCGO_VERSION) -- $(IBCGO_PROTO_RELATIVE_DIRS)

$(ICS23_DIR): Makefile
rm -rfv $(ICS23_DIR)
git clone --branch $(ICS23_VERSION) --depth 1 --quiet --no-checkout --filter=blob:none $(ICS23_URL) $(ICS23_DIR)
cd $(ICS23_DIR) && git checkout $(ICS23_VERSION) -- $(ICS23_PROTO_RELATIVE_DIRS)

$(GOGOPROTO_DIR): Makefile
rm -rfv $(GOGOPROTO_DIR)
git clone --branch $(GOGOPROTO_VERSION) --depth 1 --quiet $(GOGOPROTO_URL) $(GOGOPROTO_DIR)

$(COSMOSPROTO_DIR): Makefile
rm -rfv $(COSMOSPROTO_DIR)
git clone --branch $(COSMOSPROTO_VERSION) --depth 1 --quiet $(COSMOSPROTO_URL) $(COSMOSPROTO_DIR)

debug:
$(info SOURCES_REGEX_TO_EXCLUDE: $(SOURCES_REGEX_TO_EXCLUDE))
$(info )
Expand Down Expand Up @@ -341,4 +372,4 @@ check-manifest-ci:
# Check API documentation is up-to-date
.PHONY: check-api-docs-ci
check-api-docs-ci:
python scripts/generate_api_docs.py --check-clean
python scripts/generate_api_docs.py --check-clean
45 changes: 41 additions & 4 deletions cosmpy/aerial/client/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
# ------------------------------------------------------------------------------

"""Client functionality."""

import json
import math
import time
Expand All @@ -29,6 +28,7 @@
import certifi
import grpc
from dateutil.parser import isoparse
from packaging.version import Version

from cosmpy.aerial import cast_to_int
from cosmpy.aerial.client.bank import create_bank_send_msg
Expand All @@ -54,13 +54,14 @@
from cosmpy.aerial.exceptions import NotFoundError, QueryTimeoutError
from cosmpy.aerial.gas import GasStrategy, SimulationGasStrategy
from cosmpy.aerial.tx import Transaction, TxState
from cosmpy.aerial.tx_helpers import MessageLog, SubmittedTx, TxResponse
from cosmpy.aerial.types import Account, Block
from cosmpy.aerial.tx_helpers import MessageLog, SubmittedTx, TxResponse, safe_decode
from cosmpy.aerial.types import Account, Block, NodeInfo
from cosmpy.aerial.urls import Protocol, parse_url
from cosmpy.aerial.wallet import Wallet
from cosmpy.auth.rest_client import AuthRestClient
from cosmpy.bank.rest_client import BankRestClient
from cosmpy.common.rest_client import RestClient
from cosmpy.consensus.rest_client import ConsensusRestClient
from cosmpy.cosmwasm.rest_client import CosmWasmRestClient
from cosmpy.crypto.address import Address
from cosmpy.distribution.rest_client import DistributionRestClient
Expand All @@ -76,10 +77,14 @@
from cosmpy.protos.cosmos.base.tendermint.v1beta1.query_pb2 import (
GetBlockByHeightRequest,
GetLatestBlockRequest,
GetNodeInfoRequest,
)
from cosmpy.protos.cosmos.base.tendermint.v1beta1.query_pb2_grpc import (
ServiceStub as TendermintQueryGrpcClient,
)
from cosmpy.protos.cosmos.consensus.v1.query_pb2_grpc import (
QueryStub as QueryConsensusGrpcClient,
)
from cosmpy.protos.cosmos.crypto.ed25519.keys_pb2 import ( # noqa # pylint: disable=unused-import
PubKey,
)
Expand Down Expand Up @@ -164,6 +169,7 @@ def __init__(
self.staking = StakingGrpcClient(grpc_client)
self.distribution = DistributionGrpcClient(grpc_client)
self.params = QueryParamsGrpcClient(grpc_client)
self.consensus = QueryConsensusGrpcClient(grpc_client)
self.tendermint = TendermintQueryGrpcClient(grpc_client)
else:
rest_client = RestClient(parsed_url.rest_url)
Expand All @@ -175,6 +181,7 @@ def __init__(
self.staking = StakingRestClient(rest_client) # type: ignore
self.distribution = DistributionRestClient(rest_client) # type: ignore
self.params = ParamsRestClient(rest_client) # type: ignore
self.consensus = ConsensusRestClient(rest_client) # type: ignore
self.tendermint = TendermintRestClient(rest_client) # type: ignore

@property
Expand Down Expand Up @@ -236,6 +243,36 @@ def query_params(self, subspace: str, key: str) -> Any:
resp = self.params.Params(req)
return json.loads(resp.param.value)

def query_node_info(self) -> NodeInfo:
"""
Query basic Tendermint / node information (moniker, chain-id, version, etc.).

:return: NodeInfo.
"""
request = GetNodeInfoRequest()
response = self.tendermint.GetNodeInfo(request)

cosmos_sdk_version = Version(
response.application_version.cosmos_sdk_version.lstrip("v")
)
app_name = response.application_version.name
app_version = Version(response.application_version.version.lstrip("v"))

return NodeInfo(
cosmos_sdk_version=cosmos_sdk_version,
app_name=app_name,
app_version=app_version,
)

def query_consensus_params(self) -> Any:
"""Query consensus params.

:return: Query consensus params
"""
req = QueryParamsRequest()
resp = self.consensus.Params(req)
return resp

def query_bank_balance(self, address: Address, denom: Optional[str] = None) -> int:
"""Query bank balance.

Expand Down Expand Up @@ -652,7 +689,7 @@ def _parse_tx_response(tx_response: Any) -> TxResponse:
for event in tx_response.events:
event_data = events.get(event.type, {})
for attribute in event.attributes:
event_data[attribute.key.decode()] = attribute.value.decode()
event_data[safe_decode(attribute.key)] = safe_decode(attribute.value)
events[event.type] = event_data

timestamp = None
Expand Down
24 changes: 22 additions & 2 deletions cosmpy/aerial/gas.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,31 @@ def estimate_gas(self, tx: Transaction) -> int:
def block_gas_limit(self) -> int:
"""Get the block gas limit.

:raises Exception: Failed to query max_gas
:return: block gas limit
"""
if self._max_gas is None:
block_params = self._client.query_params("baseapp", "BlockParams")
self._max_gas = int(block_params["max_gas"])
try:
params = self._client.query_consensus_params()
self._max_gas = int(params.params.block.max_gas)
except Exception as e: # pylint: disable=broad-except
try:
block_params = self._client.query_params("baseapp", "BlockParams")
self._max_gas = int(block_params["max_gas"])
except Exception as f: # pylint: disable=broad-except
raise f from e
# Alternative implementation
# pylint: disable=pointless-string-statement
"""
node_info = self._client.query_node_info()
if (node_info.app_name == "fetch" and node_info.cosmos_sdk_version >= Version("0.20.0-rc0")) \
or node_info.cosmos_sdk_version >= Version("0.50.0-rc0"):
params = self._client.query_consensus()
self._max_gas = int(params.params.block.max_gas)
else:
block_params = self._client.query_params("baseapp", "BlockParams")
self._max_gas = int(block_params["max_gas"])
"""

return self._max_gas or -1

Expand Down
14 changes: 14 additions & 0 deletions cosmpy/aerial/tx_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,17 @@ def wait_to_complete(
self._response.ensure_successful()

return self


def safe_decode(v):
"""
Decode a value from bytes to UTF-8 string if necessary.

:param v: The value to decode. If ``bytes`` it will be decoded using UTF-8,
otherwise returned unchanged.

:return: The decoded string or the original value if it was not ``bytes``.
"""
if isinstance(v, bytes):
return v.decode("utf-8")
return v
10 changes: 10 additions & 0 deletions cosmpy/aerial/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,21 @@
from typing import Any, List

from google.protobuf.timestamp_pb2 import Timestamp
from packaging.version import Version

from cosmpy.crypto.address import Address
from cosmpy.crypto.hashfuncs import sha256


@dataclass
class NodeInfo:
"""NodeInfo."""

cosmos_sdk_version: Version
app_version: Version
app_name: str


@dataclass
class Account:
"""Account."""
Expand Down
20 changes: 20 additions & 0 deletions cosmpy/consensus/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
# Copyright 2018-2021 Fetch.AI Limited
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ------------------------------------------------------------------------------

"""This package contains the Consensus module."""
Loading
Loading