diff --git a/gittensor/validator/issue_competitions/contract_client.py b/gittensor/validator/issue_competitions/contract_client.py index 72a755b7..d503a62a 100644 --- a/gittensor/validator/issue_competitions/contract_client.py +++ b/gittensor/validator/issue_competitions/contract_client.py @@ -551,6 +551,19 @@ def _exec_contract_raw( bt.logging.error(err) return None, err + @staticmethod + def _encode_compact_len(length: int) -> bytes: + """SCALE compact-encode a non-negative length (used as the Vec/str prefix).""" + if length < 0: + raise ValueError(f'Length must be non-negative, got {length}') + if length <= 0b0011_1111: # single-byte mode + return bytes([length << 2]) + if length <= 0b0011_1111_1111_1111: # two-byte mode + return struct.pack(' bytes: """SCALE-encode method arguments using hardcoded type definitions.""" arg_types = CONTRACT_ARG_TYPES.get(method_name, []) @@ -568,6 +581,12 @@ def _encode_args(self, method_name: str, args: dict) -> bytes: encoded += struct.pack('> 64) + elif type_def == 'str': + # SCALE encodes str as a Vec: compact length prefix + UTF-8 bytes. + if not isinstance(value, str): + raise ValueError(f'Expected str for arg {arg_name}, got {type(value)}') + utf8 = value.encode('utf-8') + encoded += self._encode_compact_len(len(utf8)) + utf8 elif type_def == 'AccountId': if isinstance(value, str): encoded += bytes.fromhex(self.subtensor.substrate.ss58_decode(value)) diff --git a/tests/validator/test_contract_client_transactions.py b/tests/validator/test_contract_client_transactions.py index 1e23da89..a260c31e 100644 --- a/tests/validator/test_contract_client_transactions.py +++ b/tests/validator/test_contract_client_transactions.py @@ -3,6 +3,7 @@ """Tests for IssueCompetitionContractClient transaction methods.""" import hashlib +import struct from types import SimpleNamespace from unittest.mock import MagicMock, patch @@ -104,6 +105,53 @@ def test_exception_returns_false(client, wallet, method, kwargs_fn, _cm, _ea, _h assert getattr(client, method)(**kwargs_fn(wallet)) is False +def test_register_issue_encodes_str_args(client): + """register_issue declares github_url/repository_full_name as `str`; encoding + must SCALE-encode them (compact length prefix + UTF-8) rather than raising. + + Regression test for the `Unsupported type: str` bug (#1375). + """ + github_url = 'https://github.com/owner/repo/issues/1' + repo = 'owner/repo' + + encoded = client._encode_args( + 'register_issue', + { + 'github_url': github_url, + 'repository_full_name': repo, + 'issue_number': 1, + 'target_bounty': 10_000_000_000, + }, + ) + + url_bytes = github_url.encode('utf-8') + repo_bytes = repo.encode('utf-8') + expected = ( + IssueCompetitionContractClient._encode_compact_len(len(url_bytes)) + + url_bytes + + IssueCompetitionContractClient._encode_compact_len(len(repo_bytes)) + + repo_bytes + + struct.pack('