Skip to content

Hb dev#1

Open
qu1zzyboy wants to merge 10 commits intomainfrom
hb_dev
Open

Hb dev#1
qu1zzyboy wants to merge 10 commits intomainfrom
hb_dev

Conversation

@qu1zzyboy
Copy link

@qu1zzyboy qu1zzyboy commented Aug 21, 2025

Summary by CodeRabbit

  • New Features

    • New top-level CLI groups: hetu, dendron, weights.
    • Dendron management: register/deregister/update/query and role checks.
    • Weights operations: set/get, template, quick-score.
    • AMM: netuid-based address resolution, approvals, pool status, richer price/swap tools.
    • Staking: per-user queries, automated WHETU approval, approve command.
    • Native HETU balance lookup.
  • Refactor

    • Replaced neuron CLI with dendron and weights.
    • wallet balance now requires wallet_name and clearer output.
  • Documentation

    • README updated to wallet-name workflows; new Introduction guide.
  • Chores

    • Updated default RPC and contract addresses.
    • Pinned Python version to 3.12.4.

@coderabbitai
Copy link

coderabbitai bot commented Aug 21, 2025

Walkthrough

This change replaces the prior “Neuron” surface with a new “Dendron” and “Weights” interface, adds ABIs (DendronManager, Weights), revises GlobalStaking ABI, updates CLI commands and wrappers accordingly, adds HETU and AMM workflows, revises config defaults, introduces docs (intro), and pins Python to 3.12.4.

Changes

Cohort / File(s) Summary
Version Pinning
./.python-version
New file pinning Python to 3.12.4.
Docs
./README.md, ./introduction.md
README reoriented to wallet-name workflow and new commands; new introduction.md documenting architecture and flows.
Contract ABIs
contracts/DendronManager.abi, contracts/GlobalStaking.abi, contracts/Weights.abi, contracts/NeuronManager.abi
Added DendronManager and Weights ABIs; significant GlobalStaking ABI updates (treasury, events, types, funcs); removed NeuronManager ABI.
CLI Entrypoint
hetu_pycli/cli.py
Adds top-level hetu, dendron, weights command groups; removes neuron group wiring.
Config Defaults
hetu_pycli/config.py
Updates RPC endpoints and on-chain addresses; replaces neuron_address with dendron_address and weights_address.
TX Command Help
hetu_pycli/src/commands/tx.py
Help text clarifies RPC is optional (uses config).
Wallet Command
hetu_pycli/src/commands/wallet.py
balance now requires wallet_name; resolves via keystore; adjusted output and errors.
AMM Integration
hetu_pycli/src/hetu/amm.py
Adds netuid-based AMM resolution; expands commands; adds approve/check_approval/pool_status; recipient defaults and richer outputs.
Native HETU
hetu_pycli/src/hetu/hetu.py
New hetu_app with balance_of supporting wallet name or address.
Dendron CLI
hetu_pycli/src/hetu/dendron.py
New dendron_app: query, register (stake allocation), deregister, update service, role checks; config/RPC/wallet resolution.
Weights CLI
hetu_pycli/src/hetu/weights.py
New weights_app: set/get weights, template creation, quick_score; JSON/file parsing and tx signing.
Neuron CLI Removal
hetu_pycli/src/hetu/neuron.py
Removed neuron_app and related commands.
Staking CLI
hetu_pycli/src/hetu/staking.py
Per-user queries via wallet-name; add WHETU allowance checks/approve; new approve and debug_stake_info.
Subnet CLI
hetu_pycli/src/hetu/subnet.py
user_subnets now takes wallet name; register flow adds cost/allowance checks; new network_rate_limit command.
Wrappers
hetu_pycli/src/hetu/wrapper/dendron_mgr.py, hetu_pycli/src/hetu/wrapper/global_staking.py, hetu_pycli/src/hetu/wrapper/weights.py
Renames/rewrites NeuronMgr → DendronMgr with method renames/shims; GlobalStaking getAvailableStake signature update; new Weights wrapper.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User (CLI)
  participant W as Wallet/Keystore
  participant RPC as JSON-RPC Node
  participant GS as GlobalStaking
  participant SM as SubnetManager
  participant DM as DendronManager
  participant WT as WHETU (ERC20)

  rect rgba(230,245,255,0.5)
  note over U: Stake and Allocate
  U->>W: Load keystore (sender)
  U->>RPC: Connect (HTTP)
  U->>GS: getStakeInfo(user)
  GS-->>U: totalStaked, totalAllocated, totalCost
  U->>SM: getRegistrationCost()/params
  SM-->>U: costs/thresholds
  U->>WT: allowance(user, GS)
  alt Insufficient allowance
    U->>WT: approve(GS, amount)
    WT-->>U: receipt
  end
  U->>GS: addGlobalStake / allocateToSubnet(...)
  GS-->>U: receipt
  end

  rect rgba(235,255,235,0.5)
  note over U: Register Dendron
  U->>DM: registerNeuronWithStakeAllocation(netuid, stake, role, service)
  DM-->>U: receipt + events
  end
Loading
sequenceDiagram
  autonumber
  participant U as User (CLI)
  participant RPC as JSON-RPC Node
  participant SM as SubnetManager
  participant AMM as Subnet AMM
  participant WT as WHETU
  participant AT as ALPHA

  rect rgba(255,245,230,0.5)
  note over U: AMM address resolution
  U->>RPC: Connect
  alt netuid provided
    U->>SM: getSubnetInfo(netuid)
    SM-->>U: ... , ammPoolAddress, ...
    U->>AMM: Bind at ammPoolAddress
  else direct contract
    U->>AMM: Bind at contract
  end
  end

  rect rgba(245,235,255,0.5)
  note over U: Approvals and Swap
  U->>AMM: getTokenAddresses()
  U->>WT: allowance(user, AMM)
  U->>AT: allowance(user, AMM)
  alt Insufficient allowance
    U->>WT: approve(AMM, amount)
    U->>AT: approve(AMM, amount)
  end
  U->>AMM: swap / addLiquidity / withdraw
  AMM-->>U: receipt
  end
Loading
sequenceDiagram
  autonumber
  participant V as Validator (CLI)
  participant W as Wallet/Keystore
  participant RPC as JSON-RPC
  participant WC as Weights Contract

  rect rgba(240,255,240,0.5)
  note over V: Set Weights
  V->>W: Decrypt keystore (sender)
  V->>RPC: Connect
  V->>WC: setWeights(netuid, weights[]) (signed tx)
  WC-->>V: receipt (WeightsSet)
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

A hedgerow hums with subnet breeze,
I thump my paws on HETU trees—
Dendrons sprout where neurons slept,
Weights are set, the ledger kept.
With netuid stars to guide my hop,
I swap, I stake—then do a little bop.
(_/)< 🥕

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch hb_dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
hetu_pycli/src/commands/tx.py (2)

10-49: Prevent precision loss and invalid recipient address when sending funds

Two issues can lead to loss of funds or failed tx:

  • value is a float; converting floats to wei can introduce rounding errors.
  • to address isn’t validated or checksummed.

Apply the following diff:

@@
-from hetu_pycli.src.commands.wallet import load_keystore, get_wallet_path
+from hetu_pycli.src.commands.wallet import load_keystore, get_wallet_path
+from decimal import Decimal
@@ def send(
-    w3 = Web3(Web3.HTTPProvider(rpc_url))
+    w3 = Web3(Web3.HTTPProvider(rpc_url))
+    if not w3.is_connected():
+        print(f"[red]Failed to connect to RPC: {rpc_url}")
+        raise typer.Exit(1)
@@
-    tx = {
-        "to": to,
-        "value": w3.to_wei(value, "ether"),
+    if not Web3.is_address(to):
+        print(f"[red]Invalid recipient address: {to}")
+        raise typer.Exit(1)
+    to_addr = Web3.to_checksum_address(to)
+    try:
+        value_wei = Web3.to_wei(Decimal(str(value)), "ether")
+    except Exception as e:
+        print(f"[red]Invalid amount '{value}': {e}")
+        raise typer.Exit(1)
+
+    tx = {
+        "to": to_addr,
+        "value": int(value_wei),
         "gas": 21000,
-        "gasPrice": w3.eth.gas_price,
+        # Prefer legacy gasPrice for simple value transfers; consider EIP-1559 below.
+        "gasPrice": w3.eth.gas_price,
         "nonce": nonce,
         "chainId": w3.eth.chain_id,
     }

Optional: Support EIP-1559 fee fields when baseFeePerGas is present, falling back to gasPrice if not. I can provide a patch if desired.


51-78: Mirror the safety fixes in send-dk (RPC connectivity, address check, Decimal for amount)

The direct-key path needs the same guardrails.

@@
-from web3 import Web3
+from web3 import Web3
+from decimal import Decimal
@@ def send_by_direct_key(
-    w3 = Web3(Web3.HTTPProvider(rpc_url))
+    w3 = Web3(Web3.HTTPProvider(rpc_url))
+    if not w3.is_connected():
+        print(f"[red]Failed to connect to RPC: {rpc_url}")
+        raise typer.Exit(1)
@@
-    tx = {
-        "to": to,
-        "value": w3.to_wei(value, "ether"),
+    if not Web3.is_address(to):
+        print(f"[red]Invalid recipient address: {to}")
+        raise typer.Exit(1)
+    to_addr = Web3.to_checksum_address(to)
+    try:
+        value_wei = Web3.to_wei(Decimal(str(value)), "ether")
+    except Exception as e:
+        print(f"[red]Invalid amount '{value}': {e}")
+        raise typer.Exit(1)
+
+    tx = {
+        "to": to_addr,
+        "value": int(value_wei),
         "gas": 21000,
-        "gasPrice": w3.eth.gas_price,
+        "gasPrice": w3.eth.gas_price,
         "nonce": nonce,
         "chainId": w3.eth.chain_id,
     }

Optional: Normalize private_key to accept both with/without 0x and validate hex length. I can add that if you want.

hetu_pycli/cli.py (1)

24-56: Map the chain CLI option to the json_rpc config key

The load_config helper currently copies all CLI args straight into the config dict under their own names—but downstream code reads json_rpc, not chain. As a result, passing --chain has no effect on RPC calls. We need to translate chain into json_rpc during config loading.

Please update hetu_pycli/config.py in the load_config function:

• After line 52 (inside the for k, v in cli_args.items(): loop), handle chain explicitly:

     for k, v in cli_args.items():
-        if v is not None:
-            config[k] = v
+        if v is not None:
+            if k == "chain":
+                # Map the CLI “chain” flag into the API URL key
+                config["json_rpc"] = v
+            else:
+                config[k] = v

This ensures that:

  • config["json_rpc"] is overridden whenever --chain is passed,
  • The old chain key can be dropped or left unused.
hetu_pycli/src/hetu/subnet.py (1)

456-468: update_subnet_params: dict-to-tuple relies on insertion order; specify schema

Relying on dict.values() assumes the CLI user picked the same order as the Solidity tuple. This is brittle and will silently send wrong fields.

  • Define an explicit ordered schema (e.g., ["paramA","paramB","paramC"]) and map by keys with validation, erroring if missing.
  • Alternatively, require a JSON array with documented ordering.

I can provide a patch if you share the expected tuple field order.

hetu_pycli/src/hetu/staking.py (2)

120-166: Stake amount unit handling and symbols

  • You convert the stake amount via to_wei(amount, "ether"), implicitly assuming WHETU has 18 decimals. Use the token’s decimals instead to avoid incorrect raw values.
  • Mixed unit labels: messages say “HETU” while the balance source is WHETU.

Apply this diff (and add “from decimal import Decimal” at top-level imports):

-    decimals = whetu.decimals()
-    whetu_balance_human = whetu_balance_raw / (10 ** decimals)
-    amount_wei = staking.web3.to_wei(amount, "ether")
+    decimals = whetu.decimals()
+    whetu_balance_human = whetu_balance_raw / (10 ** decimals)
+    amount_raw = int(Decimal(str(amount)) * (10 ** decimals))
@@
-    print(f"[green]Current WHETU balance: {whetu_balance_human} HETU")
-    print(f"[green]Amount to stake: {amount} HETU")
+    print(f"[green]Current WHETU balance: {whetu_balance_human} WHETU")
+    print(f"[green]Amount to stake: {amount} WHETU")
@@
-    if whetu_balance_raw < amount_wei:
+    if whetu_balance_raw < amount_raw:
@@
-    current_allowance = whetu.allowance(from_address, contract)
+    current_allowance = whetu.allowance(from_address, contract)
@@
-    if current_allowance < amount_wei:
+    if current_allowance < amount_raw:
@@
-        approve_tx = whetu.contract.functions.approve(contract, amount_wei).build_transaction(
+        approve_tx = whetu.contract.functions.approve(contract, amount_raw).build_transaction(
@@
-    tx = staking.contract.functions.addGlobalStake(amount_wei).build_transaction(
+    tx = staking.contract.functions.addGlobalStake(amount_raw).build_transaction(
@@
-        print(f"[green]Successfully added {amount} HETU to global stake")
+        print(f"[green]Successfully added {amount} WHETU to global stake")

Additional import required near the top of the file:

from decimal import Decimal

218-264: remove_stake: amount conversion assumes 18 decimals; align with WHETU.decimals()

Same unit issue as add_stake. Also consider reusing the WHETU decimals fetched via staking.hetuToken().

Sketch of changes:

  • Load WHETU via staking.hetuToken()
  • decimals = whetu.decimals()
  • amount_raw = int(Decimal(str(amount)) * (10 ** decimals))
  • Use amount_raw in removeGlobalStake

I can draft the exact diff if desired.

hetu_pycli/src/hetu/wrapper/dendron_mgr.py (1)

1-2: Update or remove outdated comment

The comment on lines 1-2 states "Do not edit this file directly, it is generated from the ABI" but the file has been manually edited with renamed methods and added compatibility shims. Either update the comment to reflect that this is now a manually maintained wrapper with compatibility methods, or remove the misleading instruction.

-# NeuronMgr contract class generated from [ABI](contracts/NeuronManager.abi)
-# Do not edit this file directly, it is generated from the ABI.
+# DendronMgr contract wrapper class
+# This file contains both auto-generated methods from the ABI and manually added compatibility shims
🧹 Nitpick comments (30)
.python-version (1)

1-2: Align Python version documentation and metadata

All core tooling is already targeting Python 3.12.x:

  • .python-version is pinned to 3.12.4.
  • pyproject.toml specifies python = "^3.12".
  • GitHub Actions (.github/workflows/ci.yml) uses actions/setup-python@v5 with python-version: '3.12'.

Recommended next steps:

  • Add a “Requires Python 3.12+” callout in README.md (and CONTRIBUTING.md if you introduce one) so contributors and consumers know the minimum interpreter.
  • If you ship packaging metadata via setup.cfg or setup.py, consider adding a python_requires = >=3.12 line (or the equivalent Trove classifier) to keep everything in sync.
hetu_pycli/src/hetu/wrapper/weights.py (2)

4-8: Be flexible with provider input (URL string vs Provider instance).

Accepting a URL string improves ergonomics across CLI/SDK usage.

Apply:

 class Weights:
     def __init__(self, contract_address, provider, abi):
         self.contract_address = contract_address
-        self.web3 = Web3(provider)
+        # Allow either a provider instance or an RPC URL string
+        if isinstance(provider, str):
+            self.web3 = Web3(Web3.HTTPProvider(provider))
+        else:
+            self.web3 = Web3(provider)
         self.contract = self.web3.eth.contract(address=contract_address, abi=abi)

18-26: Fix docstring and normalize inputs for the view method.

Clarify return type and use checksum addresses to avoid subtle issues.

Apply:

     def weights(self, netuid, validator, dest):
         """
         Call weights(netuid, validator, dest)
         :param netuid: uint16 (solidity name: 'netuid')
         :param validator: address (solidity name: 'validator')
         :param dest: address (solidity name: 'dest')
-        :return: [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}]
+        :return: int (uint256)
         """
-        return self.contract.functions.weights(netuid, validator, dest).call() 
+        return self.contract.functions.weights(
+            int(netuid),
+            self.web3.to_checksum_address(validator),
+            self.web3.to_checksum_address(dest),
+        ).call()
hetu_pycli/config.py (1)

16-21: Refactor contract address configuration for multi-network support and overrides

Verification confirms that neuron_address is no longer referenced anywhere (search returned no hits), so it can be safely removed. However, dendron_address and weights_address are actively consumed in both the CLI commands (README.md) and in the code (hetu_pycli/src/hetu/dendron.py and hetu_pycli/src/hetu/weights.py), so their defaults should not remain hardcoded unconditionally.

• Location to update:

  • hetu_pycli/config.py (lines 16–21): currently hardcodes all addresses in a single dict.
    • Suggested approach:
  1. Split addresses into per-network config files, e.g.:
    • config.local.yml (use localhost or zero addresses)
    • config.testnet.yml
    • config.mainnet.yml
  2. Load the appropriate file based on an environment variable (e.g. HETU_NETWORK) or a CLI flag.
  3. Allow individual overrides via environment variables or hetucli c set <key> <address> (already documented in README).
  4. Remove the old neuron_address entry entirely since it is unused.

This ensures that:

  • Developers won’t accidentally point at mainnet contracts when running locally or on testnet.
  • Users can override any single address without forking the repo.
hetu_pycli/src/commands/wallet.py (1)

17-33: Gracefully handle a missing wallet directory in load_keystore.

os.listdir raises if the directory doesn’t exist. Add an early check and clearer message.

Apply:

 def load_keystore(address_or_name, wallet_path):
     # Support lookup by wallet name or address
     # First try to find by name, otherwise iterate all files to match address field
     file_path = os.path.join(wallet_path, f"{address_or_name}.json")
+    if not os.path.exists(wallet_path):
+        print(f"[red]Wallet path not found: {wallet_path}")
+        raise typer.Exit(1)
     if os.path.exists(file_path):
         with open(file_path, "r") as f:
             return json.load(f)
     # fallback: iterate all files to find by address
     for f in os.listdir(wallet_path):
introduction.md (4)

155-159: Clarify that stake thresholds are examples unless enforced on-chain

The specific minimums (500 HETU for miners, 200 HETU for validators) read as normative policy. If these are illustrative rather than hard-coded contract constants, please label them as examples and reference the relevant on-chain constants (e.g., MIN_STAKE_TO_PARTICIPATE or MIN_SUBNET_ALLOCATION) to avoid drift.

Would you like me to scan the repo for these constants in ABIs/wrappers and wire a cross-reference?

Apply this minimal copy edit if they’re examples:

- **Miners**: Require 500 HETU minimum stake allocation to ensure sufficient economic commitment
- **Validators**: Require 200 HETU minimum stake allocation, reflecting their different role and responsibility level
+ **Miners**: Require a minimum subnet allocation of 500 HETU (example; refer to on-chain policy/constants)
+ **Validators**: Require a minimum subnet allocation of 200 HETU (example; refer to on-chain policy/constants)

160-167: Tighten grammar and list lead-in for the registration requirements

The section header and lead sentence can be streamlined; minor grammar nits in bullets.

-Users register through the CLI by providing comprehensive information:
+Users register through the CLI by providing:
- **Wallet credentials and stake amount**: Ensuring sufficient economic commitment and identity verification
+ - **Wallet credentials and desired stake amount**: Ensures sufficient economic commitment and identity verification
- **Network endpoint information**: Including Xylem endpoints for communication and Cambium endpoints for monitoring
+ - **Network endpoint information**: Include Xylem endpoints (communication) and Cambium endpoints (monitoring)
- **Role selection**: Choosing between miner (computational contributor) or validator (network validator) based on capabilities
+ - **Role selection**: Choose miner (compute) or validator (network) based on capabilities
- **Subnet identification**: Specifying the target subnet using its unique netuid identifier
+ - **Subnet identification**: Specify the target subnet by its netuid
- **Technical specifications**: Providing network configuration details for seamless integration
+ - **Technical specifications**: Provide network configuration details for seamless integration

3-6: Add scope/disclaimer note to prevent policy and tokenomics drift

A short upfront disclaimer helps readers distinguish stable interfaces from change-prone parameters (fees, thresholds, addresses).

 ## Overview
 
 Hetu is a decentralized protocol designed to create a network of specialized subnets where participants can stake HETU tokens, register as nodes (miners or validators), and contribute to the network's computational capabilities. The protocol establishes an economic incentive structure that rewards participants for contributing computational resources while maintaining network security through staking mechanisms.
+
+Note:
+- Values such as stake thresholds, fees, and addresses may change via governance or contract upgrades. Treat figures herein as illustrative unless explicitly referenced to on-chain constants or config defaults.
+- This document is informational and not financial advice.

177-188: Resolve heading punctuation/spacing and ensure consistent casing

Minor formatting: remove double spaces and use consistent casing (e.g., “smart contracts” lowercased mid-sentence).

-## Technical Architecture and Smart Contract Design
-
-### Core Smart Contracts
+## Technical Architecture and smart contract design
+
+### Core smart contracts
hetu_pycli/src/hetu/wrapper/global_staking.py (1)

78-85: Signature change safe internally—no internal callers remain

  • I ran the suggested search across the repo and found no call sites passing two arguments to getAvailableStake—only the method definition itself was matched—so the signature shrink won’t break any internal code.
  • If you maintain external consumers that still invoke the old two-parameter form, consider adding an optional deprecation shim in hetu_pycli/src/hetu/wrapper/global_staking.py:
def getAvailableStake_DEPRECATED(self, user, netuid):
    # netuid ignored per new ABI; kept for backward compatibility
    return self.getAvailableStake(user)
hetu_pycli/cli.py (1)

78-84: Wiring new groups LGTM; consider a temporary alias for backward compatibility

The dendron and weights groups replace the previous neuron surface. Consider adding a hidden alias (neuron → dendron) for one release cycle to avoid breaking existing docs/scripts.

Example:

# After registering dendron_app
app.add_typer(dendron_app, name="neuron", hidden=True, no_args_is_help=True)
README.md (4)

132-136: Inconsistent wallet subcommand alias (“w import” vs “wallet import”)

Earlier sections use “hetucli wallet …” but this line uses “hetucli w import …”. Unless “w” is an intentional alias wired in the CLI, this will 404 for users.

If “w” is not an alias, replace it:

-hetucli w import <private-key> --name test0
+hetucli wallet import <private-key> --name test0

139-152: Duplicate activation instructions; streamline for clarity

These lines repeat the activation steps already shown above. Consider keeping one concise, linear flow to reduce confusion.

Apply this minimal dedupe:

-# Activate the subnet using the returned netuid
-hetucli subnet activate-subnet --netuid <returned_netuid> --sender test0
-
-# Check subnet status
-hetucli subnet subnet-info --netuid <netuid>
-
-# Activate the subnet
-hetucli subnet activate-subnet --netuid <netuid> --sender <wallet_name>
-
-# Verify activation status
-hetucli subnet subnet-info --netuid <netuid>
+# Activate the subnet and verify status
+hetucli subnet activate-subnet --netuid <returned_netuid> --sender test0
+hetucli subnet subnet-info --netuid <returned_netuid>

107-112: Emphasize security caveat for send-dk

You already note it’s not recommended. Consider explicitly warning about shell history, process list leakage, and suggesting env-vars or a keystore workflow.

Example wording update:

-# Direct private key usage (not recommended, private key will be exposed in command history)
+# Direct private key usage (strongly discouraged: exposes key in shell history/process list; prefer keystore or env-var)

196-204: Consistency: “wallet balance” line placement

In the WHETU section, “wallet balance <wallet_name>” is mixed with whetu subcommands. Consider moving it to Wallet section or preface with “to verify balance”.

hetu_pycli/src/hetu/subnet.py (4)

196-207: Avoid float rounding for token math; use Decimal for human formatting

Dividing by 10**decimals and formatting as float will lose precision for large values or 18 decimals.

Use Decimal with context:

+    from decimal import Decimal, getcontext
+    getcontext().prec = 50
-    lock_cost_human = lock_cost_raw / (10 ** decimals)
-    lock_cost_str = f"{lock_cost_human:,.{decimals}f}".rstrip('0').rstrip('.')
+    lock_cost_human = Decimal(lock_cost_raw) / (Decimal(10) ** Decimal(decimals))
+    lock_cost_str = f"{lock_cost_human:.6f}".rstrip('0').rstrip('.')  # cap to 6 dp for readability

Repeat similarly for balance formatting below.


209-215: Deposit guidance should round up to required amount and avoid float drift

Using a raw float could suggest an amount slightly below required due to rounding.

Consider:

-        print(f"[yellow]Please deposit more WHETU using: hetucli whetu deposit --sender {sender} --value {lock_cost_human}")
+        # round up to 6 dp for CLI convenience
+        need_str = f"{lock_cost_human:.6f}".rstrip('0').rstrip('.')
+        print(f"[yellow]Please deposit more WHETU using: hetucli whetu deposit --sender {sender} --value {need_str}")

224-247: Consider approving “exact needed” allowance and/or idempotent top-ups

Approving exactly lock_cost_raw is fine, but if an approval already exists slightly below required, users will need to re-approve the whole amount. Approving delta (lockCost - currentAllowance) reduces gas/UX bumps; also consider EIP-20 allowance reset pattern for non-compliant tokens.

Example:

-    if current_allowance < lock_cost_raw:
+    required_delta = lock_cost_raw - current_allowance
+    if required_delta > 0:
         print(f"[yellow]Insufficient allowance. Approving {lock_cost_str} WHETU...")
-        approve_tx = whetu.contract.functions.approve(contract, lock_cost_raw).build_transaction(
+        approve_tx = whetu.contract.functions.approve(contract, required_delta).build_transaction(
             {

272-281: Created netuid inference may be racy under concurrency

Assuming created_netuid == next_netuid_before can be wrong if concurrent registrations occur. Prefer deriving from an emitted event or by reading getNextNetuid() after receipt and subtracting one.

Safer fallback:

-        # Get the created subnet ID - it should be the current nextNetuid
-        created_netuid = next_netuid_before
+        # Best-effort derive created netuid
+        try:
+            next_after = subnet_mgr.getNextNetuid()
+            created_netuid = max(0, next_after - 1)
+        except Exception:
+            created_netuid = next_netuid_before  # fallback

If the contract emits a “SubnetCreated”/similar event, parse receipt.logs instead for an authoritative netuid.

hetu_pycli/src/hetu/staking.py (2)

41-69: total_staked: defaulting to contract owner may be surprising

If no user is supplied, you query staking.owner() and show that account’s totals. That’s an admin address, not the caller’s wallet, and may confuse users.

Consider:

  • require --user explicitly, or
  • default to a “sender” wallet name supplied via config/flag, not owner().

95-109: DRY: repeated wallet-name-to-address resolution

The same pattern appears across multiple commands. Extract a helper, e.g., resolve_user_address(ctx, user_or_name), to reduce bugs and keep messaging uniform.

I can provide a small utility function and replace call sites in a follow-up patch.

Also applies to: 325-338, 356-369, 387-400, 466-479

contracts/GlobalStaking.abi (1)

208-237: Gas/UX: consider exposing allocateToSubnetWithMinThreshold in CLI

The ABI includes allocateToSubnetWithMinThreshold. If relevant, add a CLI surface to let validators set a min threshold to reduce slippage-like effects during allocation.

Happy to sketch a new command if you want it in this PR.

Also applies to: 239-260

hetu_pycli/src/hetu/weights.py (3)

96-119: Validate sum/normalization of weights (optional, contract-dependent)

If the contract expects weights to sum to a fixed scale (e.g., 1_000_000 per validator), add a preflight sum check to catch user mistakes early.

Example:

     contract_weights = []
+    total = 0
     for weight in weights_data:
@@
-        contract_weights.append([dest, int(weight_value)])
+        w = int(weight_value)
+        contract_weights.append([dest, w])
+        total += w
+
+    if total == 0:
+        print("[red]Total weight must be > 0")
+        raise typer.Exit(1)
+# Optional strict mode:
+#    if total != 1_000_000:
+#        print(f"[red]Total weight must sum to 1_000_000, got {total}")
+#        raise typer.Exit(1)

136-141: Gas is fixed at 300k; may underflow for larger weight sets

For long weight arrays, 300k may be insufficient. Consider eth_estimateGas with a safety multiplier, and allow --gas override.

Illustrative change:

-    tx = weights_contract.contract.functions.setWeights(netuid, new_weights).build_transaction(
-        {
-            "from": from_address,
-            "nonce": nonce,
-            "gas": 300000,
-            "gasPrice": weights_contract.web3.eth.gas_price,
-        }
-    )
+    fn = weights_contract.contract.functions.setWeights(netuid, new_weights)
+    est = weights_contract.web3.eth.estimate_gas({"from": from_address, "to": weights_contract.contract.address, "data": fn._encode_transaction_data()})
+    gas_limit = int(est * 1.2)  # 20% headroom
+    tx = fn.build_transaction({
+        "from": from_address,
+        "nonce": nonce,
+        "gas": gas_limit,
+        "gasPrice": weights_contract.web3.eth.gas_price,
+    })

Also applies to: 298-305


155-190: get_weights: nice UX; consider optional wallet-name resolution for validator

Not required, but symmetry with other commands would let users pass a wallet name for validator.

If interested, reuse the resolve helper proposed in staking.py.

hetu_pycli/src/hetu/wrapper/dendron_mgr.py (1)

25-27: Inconsistent return type documentation

The return type documentation shows a complex tuple structure, but the actual return is documented in line 25 as a single tuple. Consider making the documentation clearer by showing it's a tuple type rather than a list with one element.

        :param account: address (solidity name: 'account')
-       :return: [{'components': [{'internalType': 'address', 'name': 'account', 'type': 'address'}, {'internalType': 'uint16', 'name': 'netuid', 'type': 'uint16'}, {'internalType': 'bool', 'name': 'isActive', 'type': 'bool'}, {'internalType': 'bool', 'name': 'isValidator', 'type': 'bool'}, {'internalType': 'uint256', 'name': 'stake', 'type': 'uint256'}, {'internalType': 'uint64', 'name': 'registrationBlock', 'type': 'uint64'}, {'internalType': 'uint256', 'name': 'lastUpdate', 'type': 'uint256'}, {'internalType': 'string', 'name': 'axonEndpoint', 'type': 'string'}, {'internalType': 'uint32', 'name': 'axonPort', 'type': 'uint32'}, {'internalType': 'string', 'name': 'prometheusEndpoint', 'type': 'string'}, {'internalType': 'uint32', 'name': 'prometheusPort', 'type': 'uint32'}], 'internalType': 'struct SubnetTypes.DendronInfo', 'name': '', 'type': 'tuple'}]
+       :return: SubnetTypes.DendronInfo tuple containing (account, netuid, isActive, isValidator, stake, registrationBlock, lastUpdate, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort)
hetu_pycli/src/hetu/amm.py (2)

63-63: Add comment documenting AMM pool address index

The magic number 3 for accessing the AMM pool address from subnet info should be documented for clarity.

        # Get subnet info to extract AMM pool address
        subnet_info = subnet_mgr.getSubnetInfo(netuid)
+       # AMM Pool address is at index 3 in the SubnetInfo tuple
        amm_pool_address = subnet_info[3]  # AMM Pool address is at index 3

594-595: Typo in comment: "10 HETU" should be dynamic

The comment says "10 HETU" but the actual value used for simulation is dynamic (10.0 is hardcoded). Consider making the comment match the code.

    # Simulate swap
    hetu_amount_wei = hetu_token.web3.to_wei(10.0, "ether")
    simulated_alpha = amm.simSwapHETUForAlpha(hetu_amount_wei)
    simulated_alpha_ether = hetu_token.web3.from_wei(simulated_alpha, "ether")
    
-   print(f"\n[green]Swap Simulation (10 HETU):")
+   print(f"\n[green]Swap Simulation (10.0 HETU):")
contracts/DendronManager.abi (1)

287-296: Align ABI function name with wrapper call

The Python wrapper invokes getDendronCount(netuid), but the ABI still defines this entry as getNeuronCount. To ensure the contract functions resolve correctly at runtime, update the ABI in contracts/DendronManager.abi:

--- a/contracts/DendronManager.abi
+++ b/contracts/DendronManager.abi
@@ lines 287-296
-    "name": "getNeuronCount",
+    "name": "getDendronCount",
     "outputs": [
       {
         "internalType": "uint256",
         "name": "",
         "type": "uint256"
       }
     ],

• File: contracts/DendronManager.abi (lines ~287)
• Change "getNeuronCount""getDendronCount"

This optional refactor brings the ABI in line with your DendronMgr.getDendronCount call and prevents “function not found” errors.

hetu_pycli/src/hetu/dendron.py (1)

366-367: Unused decimals variable

The decimals variable is retrieved but never used in the approval flow.

     current_allowance = whetu.allowance(from_address, staking_contract)
-    decimals = whetu.decimals()
     
     if current_allowance < required_stake_wei:

Or if you want to use it for display:

     current_allowance = whetu.allowance(from_address, staking_contract)
     decimals = whetu.decimals()
     
     if current_allowance < required_stake_wei:
         print(f"[yellow]Insufficient allowance. Approving {mgr.web3.from_wei(required_stake_wei, 'ether')} WHETU for staking...")
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d7aab4e and e56569f.

📒 Files selected for processing (21)
  • .python-version (1 hunks)
  • README.md (5 hunks)
  • contracts/DendronManager.abi (1 hunks)
  • contracts/GlobalStaking.abi (1 hunks)
  • contracts/NeuronManager.abi (0 hunks)
  • contracts/Weights.abi (1 hunks)
  • hetu_pycli/cli.py (2 hunks)
  • hetu_pycli/config.py (1 hunks)
  • hetu_pycli/src/commands/tx.py (2 hunks)
  • hetu_pycli/src/commands/wallet.py (1 hunks)
  • hetu_pycli/src/hetu/amm.py (10 hunks)
  • hetu_pycli/src/hetu/dendron.py (1 hunks)
  • hetu_pycli/src/hetu/hetu.py (1 hunks)
  • hetu_pycli/src/hetu/neuron.py (0 hunks)
  • hetu_pycli/src/hetu/staking.py (9 hunks)
  • hetu_pycli/src/hetu/subnet.py (5 hunks)
  • hetu_pycli/src/hetu/weights.py (1 hunks)
  • hetu_pycli/src/hetu/wrapper/dendron_mgr.py (5 hunks)
  • hetu_pycli/src/hetu/wrapper/global_staking.py (1 hunks)
  • hetu_pycli/src/hetu/wrapper/weights.py (1 hunks)
  • introduction.md (1 hunks)
💤 Files with no reviewable changes (2)
  • hetu_pycli/src/hetu/neuron.py
  • contracts/NeuronManager.abi
🧰 Additional context used
🧬 Code Graph Analysis (10)
hetu_pycli/src/hetu/hetu.py (1)
hetu_pycli/src/commands/wallet.py (2)
  • load_keystore (17-32)
  • get_wallet_path (12-14)
hetu_pycli/src/hetu/wrapper/weights.py (2)
hetu_pycli/src/hetu/wrapper/subnet_amm.py (1)
  • netuid (204-209)
hetu_pycli/src/commands/contract.py (1)
  • call (10-33)
hetu_pycli/src/hetu/amm.py (6)
hetu_pycli/src/hetu/wrapper/subnet_amm.py (15)
  • netuid (204-209)
  • getAlphaPrice (64-69)
  • getPoolInfo (99-104)
  • getStatistics (106-111)
  • getSwapPreview (113-120)
  • simSwapAlphaForHETU (218-224)
  • simSwapHETUForAlpha (226-232)
  • withdrawLiquidity (318-326)
  • swapAlphaForHETU (262-270)
  • swapHETUForAlpha (272-280)
  • hetuToken (151-156)
  • alphaToken (18-23)
  • getSystemInfo (122-127)
  • getCreatorInfo (71-76)
  • mechanism (183-188)
hetu_pycli/src/hetu/subnet.py (2)
  • get_contract_address (28-35)
  • load_subnet_mgr (18-26)
hetu_pycli/src/hetu/wrapper/subnet_mgr.py (2)
  • getSubnetInfo (63-69)
  • hetuToken (87-92)
hetu_pycli/src/commands/wallet.py (2)
  • get_wallet_path (12-14)
  • load_keystore (17-32)
hetu_pycli/src/hetu/erc20.py (1)
  • load_erc20 (18-26)
hetu_pycli/src/hetu/wrapper/whetu.py (3)
  • approve (27-34)
  • balanceOf (36-42)
  • allowance (18-25)
hetu_pycli/src/hetu/weights.py (3)
hetu_pycli/src/hetu/wrapper/weights.py (3)
  • weights (18-26)
  • Weights (3-26)
  • setWeights (9-16)
hetu_pycli/src/commands/wallet.py (2)
  • load_keystore (17-32)
  • get_wallet_path (12-14)
hetu_pycli/src/hetu/dendron.py (1)
  • get_contract_address (17-24)
hetu_pycli/src/commands/wallet.py (1)
hetu_pycli/src/commands/config.py (1)
  • get (15-26)
hetu_pycli/src/hetu/subnet.py (4)
hetu_pycli/src/hetu/whetu.py (3)
  • load_whetu (17-25)
  • decimals (329-340)
  • name (357-368)
hetu_pycli/src/commands/wallet.py (2)
  • get_wallet_path (12-14)
  • load_keystore (17-32)
hetu_pycli/src/hetu/wrapper/subnet_mgr.py (5)
  • getUserSubnets (79-85)
  • getNetworkLockCost (26-31)
  • hetuToken (87-92)
  • getNextNetuid (40-45)
  • networkRateLimit (122-127)
hetu_pycli/src/hetu/wrapper/whetu.py (4)
  • decimals (44-49)
  • balanceOf (36-42)
  • allowance (18-25)
  • name (65-70)
hetu_pycli/cli.py (1)
hetu_pycli/src/hetu/wrapper/weights.py (1)
  • weights (18-26)
hetu_pycli/src/hetu/staking.py (6)
hetu_pycli/src/hetu/dendron.py (1)
  • get_contract_address (17-24)
hetu_pycli/src/hetu/whetu.py (4)
  • get_contract_address (27-34)
  • decimals (329-340)
  • load_whetu (17-25)
  • approve (280-326)
hetu_pycli/src/hetu/wrapper/global_staking.py (4)
  • owner (170-175)
  • getStakeInfo (104-110)
  • hetuToken (144-149)
  • getSubnetAllocation (112-119)
hetu_pycli/src/commands/wallet.py (2)
  • get_wallet_path (12-14)
  • load_keystore (17-32)
hetu_pycli/src/hetu/erc20.py (1)
  • load_erc20 (18-26)
hetu_pycli/src/hetu/wrapper/whetu.py (4)
  • decimals (44-49)
  • balanceOf (36-42)
  • allowance (18-25)
  • approve (27-34)
hetu_pycli/src/hetu/dendron.py (7)
hetu_pycli/src/hetu/wrapper/dendron_mgr.py (10)
  • DendronMgr (6-190)
  • getDendronInfo (20-27)
  • getSubnetDendronCount (29-35)
  • getSubnetDendrons (37-43)
  • getSubnetValidatorCount (45-51)
  • getSubnetValidators (53-59)
  • isDendron (68-75)
  • isValidator (77-84)
  • dendronList (86-93)
  • dendrons (183-190)
hetu_pycli/src/commands/wallet.py (2)
  • load_keystore (17-32)
  • get_wallet_path (12-14)
hetu_pycli/src/hetu/subnet.py (2)
  • get_contract_address (28-35)
  • load_subnet_mgr (18-26)
hetu_pycli/src/hetu/staking.py (2)
  • get_contract_address (28-35)
  • load_staking (18-26)
hetu_pycli/src/hetu/wrapper/subnet_mgr.py (1)
  • getSubnetParams (71-77)
hetu_pycli/src/hetu/wrapper/global_staking.py (2)
  • getAvailableStake (78-84)
  • getStakeInfo (104-110)
hetu_pycli/src/hetu/whetu.py (1)
  • load_whetu (17-25)
hetu_pycli/src/hetu/wrapper/dendron_mgr.py (2)
hetu_pycli/src/commands/contract.py (1)
  • call (10-33)
hetu_pycli/src/hetu/dendron.py (1)
  • dendrons (206-219)
🪛 LanguageTool
introduction.md

[grammar] ~154-~154: There might be a mistake here.
Context: ... 1. Staking Requirements and Preparation - Miners: Require 500 HETU minimum stake...

(QB_NEW_EN)


[grammar] ~160-~160: There might be a mistake here.
Context: ...ation Process and Technical Requirements Users register through the CLI by provid...

(QB_NEW_EN)


[grammar] ~161-~161: There might be a mistake here.
Context: ... by providing comprehensive information: - Wallet credentials and stake amount: E...

(QB_NEW_EN)


[grammar] ~162-~162: There might be a mistake here.
Context: ...mic commitment and identity verification - Network endpoint information: Includin...

(QB_NEW_EN)


[grammar] ~163-~163: There might be a mistake here.
Context: ...ion and Cambium endpoints for monitoring - Role selection: Choosing between miner...

(QB_NEW_EN)


[grammar] ~164-~164: There might be a mistake here.
Context: ...network validator) based on capabilities - Subnet identification: Specifying the ...

(QB_NEW_EN)


[grammar] ~165-~165: There might be a mistake here.
Context: ...ubnet using its unique netuid identifier - Technical specifications: Providing ne...

(QB_NEW_EN)


[grammar] ~169-~169: There might be a mistake here.
Context: ...r role. The activation process involves: - Smart contract verification: Ensuring ...

(QB_NEW_EN)


[grammar] ~177-~177: There might be a mistake here.
Context: ...ontract Design ### Core Smart Contracts The protocol consists of several interco...

(QB_NEW_EN)


[grammar] ~188-~188: There might be a mistake here.
Context: ...idity. ### Network Interaction Patterns The protocol implements several key inte...

(QB_NEW_EN)

🔇 Additional comments (13)
.python-version (1)

1-1: Pin to Python 3.12.4 looks good.

This helps avoid “works on my machine” interpreter drift across devs and CI.

contracts/Weights.abi (2)

1-98: ABI structure is minimal and consistent with intended usage.

  • Event WeightsSet has netuid and validator indexed; weights as non-indexed tuple[] — good.
  • setWeights is nonpayable and weights getter is view — expected.

No issues from an ABI perspective.


1-98: Sanity Check Passed: The ABI contract surface matches expectations

  • setWeights is nonpayable
  • weights() is a view returning uint256
  • WeightsSet event inputs are correctly indexed (first two) with the third as a tuple[] of { dest: address, weight: uint256 }

No further changes required.

hetu_pycli/src/commands/tx.py (2)

16-16: Help text improvement LGTM

Clearer that RPC falls back to config. Consistent with existing resolution: rpc or config["json_rpc"].


57-57: Help text improvement LGTM

Same as above for send-dk; aligns with config semantics.

hetu_pycli/cli.py (1)

9-9: Adding hetu_app entrypoint LGTM

New native HETU group import is straightforward and consistent with how other Typer apps are wired.

hetu_pycli/src/hetu/subnet.py (1)

566-579: Nice addition: network_rate_limit read endpoint

This complements the other network telemetry getters. No issues.

hetu_pycli/src/hetu/amm.py (4)

36-71: LGTM! Well-structured AMM contract address resolution

The get_amm_contract_address function provides a clean implementation for resolving AMM addresses either directly or through subnet info. The mutually exclusive parameter validation and comprehensive error handling are appropriate.


72-104: Enhanced alpha price display with exchange rates

Good improvement to the alpha price display with both raw and formatted values, plus clear exchange rate calculations. The conditional formatting for 1:1 exchange is user-friendly.


281-285: Consistent recipient defaulting pattern

Good implementation of defaulting the recipient to the sender address when not specified, with clear user notification. This pattern is consistently applied across withdraw_liquidity, swap_alpha_for_hetu, and swap_hetu_for_alpha commands.

Also applies to: 340-344, 399-403


427-521: Comprehensive token approval implementation

The approve_tokens command is well-implemented with proper token address resolution, separate approval flows for HETU and ALPHA, and appropriate transaction handling.

contracts/DendronManager.abi (1)

1-711: LGTM! Well-structured DendronManager ABI

The ABI provides a comprehensive interface for dendron/neuron management with appropriate events, errors, and functions. The structure is clean and follows Ethereum ABI standards.

hetu_pycli/src/hetu/dendron.py (1)

293-296: Further Inspection of subnet_params Usage

To accurately verify the hardcoded indices, we need to see how subnet_params is obtained and where its layout is defined. I’ve requested the snippet around lines 250–350 of dendron.py to trace the call that populates this list and locate the source (ABI or wrapper) dictating its ordering. Once we have that context, we can map each index to its intended field and confirm—or correct—the positions for baseBurnCost, validatorThreshold, and dendronThreshold.

Comment on lines +388 to +428
{
"inputs": [
{
"internalType": "address",
"name": "user",
"type": "address"
}
],
"name": "getStakeInfo",
"outputs": [
{
"components": [
{
"internalType": "uint256",
"name": "totalStaked",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalAllocated",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "totalCost",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "lastUpdateBlock",
"type": "uint256"
}
],
"internalType": "struct IGlobalStaking.StakeInfo",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

ABI changed: CLI still calls removed methods (effective/locked); align commands

Per this ABI, getEffectiveStake and getLockedStake are not present; getSubnetAllocation returns (allocated, cost, lastUpdateBlock) and getStakeInfo returns (totalStaked, totalAllocated, totalCost, lastUpdateBlock). Update the CLI accordingly (see staking.py comments).

Run this script to confirm absence in the ABI and find CLI call sites:

Also applies to: 430-469


🏁 Script executed:

#!/bin/bash
# Verify ABI does not contain removed methods
jq -r '.[].name // empty' contracts/GlobalStaking.abi | grep -E 'getEffectiveStake|getLockedStake' || echo "OK: methods not present"

# Show CLI references (expect matches in staking.py only)
rg -nP -C2 '\bgetEffectiveStake\b|\bgetLockedStake\b' --type=py

Length of output: 2731


Critical: ABI methods removed – update CLI and wrapper accordingly

The getEffectiveStake and getLockedStake functions no longer exist in contracts/GlobalStaking.abi, but the Python CLI and wrapper still call them, which will break at runtime. Please update both layers to align with the new ABI methods (getSubnetAllocation and getStakeInfo).

Affected locations:

  • hetu_pycli/src/hetu/staking.py
    • Line 370: staking.getEffectiveStake(user, netuid)
    • Line 401: staking.getLockedStake(user, netuid)
    → Remove or replace these commands. Use staking.getStakeInfo(user) (and/or a new getSubnetAllocation(user) wrapper) and format the fields:
    totalStaked, totalAllocated, totalCost, lastUpdateBlock
    – or allocated, cost, lastUpdateBlock as appropriate.

  • hetu_pycli/src/hetu/wrapper/global_staking.py
    • Methods getEffectiveStake (lines 86–93) and getLockedStake (lines 95–102) still call the removed ABI functions.
    → Remove or refactor these wrappers. You can instead expose:
    getStakeInfo(self, user) which wraps the new getStakeInfo ABI call
    – (Optionally) getSubnetAllocation(self, user) for the 3-tuple ABI return

Please ensure the CLI commands reference only existing ABI functions and that the wrapper layer maps to those correctly.

🤖 Prompt for AI Agents
In contracts/GlobalStaking.abi (around lines 388–428) and the Python code,
update hetu_pycli/src/hetu/staking.py (lines ~370 and ~401) and
hetu_pycli/src/hetu/wrapper/global_staking.py (methods at lines ~86–102): the
ABI no longer has getEffectiveStake/getLockedStake so remove calls to those
functions and replace with wrappers that call the new ABI methods—implement
getStakeInfo(self, user) in the wrapper to call the new getStakeInfo ABI and
return/format totalStaked, totalAllocated, totalCost, lastUpdateBlock (and
optionally add getSubnetAllocation(self, user) if needed to wrap the other ABI),
then update the CLI commands in staking.py to use staking.getStakeInfo(user)
(and/or staking.getSubnetAllocation(user)) and map the returned tuple fields to
the expected output instead of calling the removed methods.

Comment on lines +9 to 11
"chain": "ws://127.0.0.1:9090",
"json_rpc": "http://161.97.161.133:18545",
"network": "local",
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Default endpoints suggest a remote HTTP RPC while network is set to 'local'.

This may surprise users by hitting a remote node by default. Consider:

  • Keeping localhost defaults for “local” profile and introducing a “testnet/mainnet” profile, or
  • Sourcing endpoints from HETU_NETWORK env var and switching addresses accordingly.

At minimum, document this clearly in README and CLI help.

To spot potential mismatches, run:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Current defaults:"
rg -n 'DEFAULT_CONFIG\s*=\s*{' -n -n hetu_pycli/config.py -n
rg -n '\"network\":\s*\"local\"' hetu_pycli/config.py || true
rg -n '\"json_rpc\":' hetu_pycli/config.py || true

Length of output: 235


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "🔍 Searching for DEFAULT_CONFIG block"
rg -n 'DEFAULT_CONFIG\s*=\s*\{' -C2 hetu_pycli/config.py || true

echo ""
echo "🔍 Verifying the “network” default"
rg -n '"network":\s*"local"' hetu_pycli/config.py || true

echo ""
echo "🔍 Verifying the “json_rpc” default"
rg -n '"json_rpc":' hetu_pycli/config.py || true

Length of output: 697


Align default JSON-RPC endpoint with “local” network or document the mismatch

The DEFAULT_CONFIG in hetu_pycli/config.py currently sets:

  • chain: ws://127.0.0.1:9090
  • json_rpc: http://161.97.161.133:18545
  • network: "local"

Because the JSON-RPC URL points at a remote host while the network is labeled “local,” users invoking the CLI with no overrides may unexpectedly hit a public node. To prevent confusion or inadvertent exposure, please address this in one of the following ways:

• Switch the default JSON-RPC URL to http://127.0.0.1:18545 for the local profile (i.e. keep both endpoints on localhost).
• Introduce named profiles (e.g. “local”, “testnet”, “mainnet”) each with its own endpoint set, and default to the truly local profile.
• Source endpoints from a HETU_NETWORK (or similar) environment variable and map “local”→localhost, “testnet”→public RPC, etc.

At minimum, add clear documentation:

  • In README.md, describe that the CLI’s “local” network profile expects a local node and that the JSON-RPC default is remote unless overridden.
  • In the CLI’s help text (--help), note that --json-rpc must be specified to point elsewhere.

Locations to update:

  • hetu_pycli/config.py lines 9–11 (default JSON-RPC value and/or profile logic)
  • Documentation in README.md and CLI help output
🤖 Prompt for AI Agents
In hetu_pycli/config.py around lines 9–11 the DEFAULT_CONFIG's json_rpc points
to a remote host while network is "local"; change the json_rpc default to
"http://127.0.0.1:18545" so both endpoints are localhost for the local profile,
and update README.md to state that the CLI defaults assume a local node and that
--json-rpc can be used to target a different RPC, and add a brief note to the
CLI --help output indicating the same and recommending explicit override for
non-local networks.

Comment on lines +149 to +156
# Load keystore by wallet name
try:
keystore = load_keystore(wallet_name, wallet_path)
address = keystore.get("address")
print(f"[yellow]Wallet '{wallet_name}' address: {address}")
except Exception:
print(f"[red]Wallet not found: {wallet_name}")
raise typer.Exit(1)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Harden balance: validate keystore address and handle RPC failures.

Avoid double-printing errors, ensure address exists and is valid, checksum it, and guard RPC calls.

Apply:

-    # Load keystore by wallet name
-    try:
-        keystore = load_keystore(wallet_name, wallet_path)
-        address = keystore.get("address")
-        print(f"[yellow]Wallet '{wallet_name}' address: {address}")
-    except Exception:
-        print(f"[red]Wallet not found: {wallet_name}")
-        raise typer.Exit(1)
+    # Load keystore by wallet name
+    try:
+        keystore = load_keystore(wallet_name, wallet_path)
+    except Exception as e:
+        print(f"[red]Wallet not found or invalid: {wallet_name} ({e})")
+        raise typer.Exit(1)
+    address = keystore.get("address")
+    if not address or not Web3.is_address(address):
+        print("[red]Keystore missing a valid 'address' field.")
+        raise typer.Exit(1)
+    address = Web3.to_checksum_address(address)
+    print(f"[yellow]Wallet '{wallet_name}' address: {address}")
@@
-    bal = w3.eth.get_balance(address)
+    try:
+        bal = w3.eth.get_balance(address)
+    except Exception as e:
+        print(f"[red]Failed to fetch balance from {rpc_url}: {e}")
+        raise typer.Exit(1)
     ether = w3.from_wei(bal, 'ether')
     ether_str = f"{ether:,.18f}".rstrip('0').rstrip('.')
     print(f"[cyan]Balance: {ether_str} HETU")
     print(f"[cyan]Address: {address}")

Also applies to: 158-163

🤖 Prompt for AI Agents
In hetu_pycli/src/commands/wallet.py around lines 149-156 (and similarly
158-163), the keystore load block double-prints errors and does not validate the
address or guard RPC calls; change the except to exit once without printing
twice, verify keystore contains an "address" and that it is a valid address
(convert/normalize to checksum form) before printing, and wrap any subsequent
RPC calls in try/except to handle connection or RPC failures gracefully
(log/print a clear one-time error and exit with typer.Exit(1) on RPC errors).
Ensure you import and use a reliable checksum/address validator and only print
the wallet address after validation.

Comment on lines +50 to +63
# 检查是否是钱包名称并转换为地址
address = account
config = ctx.obj
wallet_path = get_wallet_path(config)
if not (address.startswith('0x') and len(address) == 42):
try:
keystore = load_keystore(account, wallet_path)
address = keystore.get("address")
print(f"[yellow]Converted wallet name '{account}' to address: {address}")
except Exception:
print(f"[red]Wallet not found: {account}")
raise typer.Exit(1)
account = address

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Replace repeated Chinese comments with English

Multiple locations contain Chinese comments (检查是否是钱包名称并转换为地址) that should be in English for consistency with the rest of the codebase.

-    # 检查是否是钱包名称并转换为地址
+    # Check if it's a wallet name and convert to address

Apply this change to lines 50, 141, 172, and 493.

Also applies to: 141-154, 172-185, 493-506

🤖 Prompt for AI Agents
In hetu_pycli/src/hetu/dendron.py around lines 50-63, 141-154, 172-185, and
493-506 the inline comment "检查是否是钱包名称并转换为地址" appears in Chinese; replace each
occurrence with an English comment such as "Check if input is a wallet name and
convert to address" (or similar concise phrasing consistent with project style)
and keep surrounding logic unchanged so only the comment text is modified for
consistency.

Comment on lines +237 to +241
# 由于 ABI 中没有 canRegisterDendron 函数,我们显示基本信息
print(f"[yellow]Checking registration eligibility for user {user} on subnet {netuid}")
print(f"[yellow]Validator role: {is_validator_role}")
print(f"[yellow]Note: canRegisterDendron function not available in current ABI")
print(f"[yellow]Please check subnet status and user stake allocation manually")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Remove placeholder message for canRegisterDendron

The function displays a message that canRegisterDendron is not available in the ABI, but this seems to be a placeholder implementation. Consider either implementing actual eligibility checks or removing this command if it's not functional.

Either implement actual registration eligibility checks:

-    # 由于 ABI 中没有 canRegisterDendron 函数,我们显示基本信息
-    print(f"[yellow]Checking registration eligibility for user {user} on subnet {netuid}")
-    print(f"[yellow]Validator role: {is_validator_role}")
-    print(f"[yellow]Note: canRegisterDendron function not available in current ABI")
-    print(f"[yellow]Please check subnet status and user stake allocation manually")
+    # Check if user is already registered
+    is_registered = mgr.isDendron(netuid, user)
+    if is_registered:
+        print(f"[red]User {user} is already registered as a dendron in subnet {netuid}")
+        return
+    
+    # Check stake availability
+    from hetu_pycli.src.hetu.staking import load_staking
+    staking_contract = get_contract_address(ctx, "staking_address", None)
+    staking = load_staking(staking_contract, rpc)
+    available_stake = staking.getAvailableStake(user)
+    
+    print(f"[green]User {user} can register: Not currently registered")
+    print(f"[green]Available stake: {mgr.web3.from_wei(available_stake, 'ether')} HETU")
+    print(f"[green]Validator role requested: {is_validator_role}")

Or remove the command entirely if it's not needed.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# 由于 ABI 中没有 canRegisterDendron 函数,我们显示基本信息
print(f"[yellow]Checking registration eligibility for user {user} on subnet {netuid}")
print(f"[yellow]Validator role: {is_validator_role}")
print(f"[yellow]Note: canRegisterDendron function not available in current ABI")
print(f"[yellow]Please check subnet status and user stake allocation manually")
# Check if user is already registered
is_registered = mgr.isDendron(netuid, user)
if is_registered:
print(f"[red]User {user} is already registered as a dendron in subnet {netuid}")
return
# Check stake availability
from hetu_pycli.src.hetu.staking import load_staking
staking_contract = get_contract_address(ctx, "staking_address", None)
staking = load_staking(staking_contract, rpc)
available_stake = staking.getAvailableStake(user)
print(f"[green]User {user} can register: Not currently registered")
print(f"[green]Available stake: {mgr.web3.from_wei(available_stake, 'ether')} HETU")
print(f"[green]Validator role requested: {is_validator_role}")
🤖 Prompt for AI Agents
In hetu_pycli/src/hetu/dendron.py around lines 237 to 241, the command prints a
placeholder message stating canRegisterDendron is not available in the ABI;
remove this non-functional placeholder and either implement the actual
eligibility check or delete the command: if implementing, query the
contract/subnet for registration eligibility (e.g., check user's stake, subnet
status, validator role via available ABI methods or on-chain calls), compute and
print a clear eligible/not-eligible result with reasons; if deleting, remove the
command and any references to it and update CLI help/tests accordingly.

Comment on lines +110 to +121
# 检查是否是钱包名称并转换为地址
address = sender
config = ctx.obj
wallet_path = get_wallet_path(config)
if not (address.startswith('0x') and len(address) == 42):
try:
keystore = load_keystore(sender, wallet_path)
address = keystore.get("address")
print(f"[yellow]Wallet '{sender}' address: {address}")
except Exception:
print(f"[red]Wallet not found: {sender}")
raise typer.Exit(1)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Use Web3 address validation instead of prefix/length check; unify error path

The current check uses startswith('0x') and len==42, which can pass malformed addresses. Prefer Web3.is_address and avoid double-printing errors from load_keystore.

Apply this diff:

-    # 检查是否是钱包名称并转换为地址
-    address = sender
+    # Resolve wallet name to address if needed
+    address = sender
     config = ctx.obj
     wallet_path = get_wallet_path(config)
-    if not (address.startswith('0x') and len(address) == 42):
+    if not Web3.is_address(address):
         try:
             keystore = load_keystore(sender, wallet_path)
             address = keystore.get("address")
-            print(f"[yellow]Wallet '{sender}' address: {address}")
-        except Exception:
-            print(f"[red]Wallet not found: {sender}")
+            if not Web3.is_address(address):
+                print(f"[red]Resolved address from wallet '{sender}' is invalid: {address}")
+                raise typer.Exit(1)
+            print(f"[yellow]Wallet '{sender}' address: {address}")
+        except Exception:
+            # load_keystore already prints an error; exit here to avoid duplication
             raise typer.Exit(1)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# 检查是否是钱包名称并转换为地址
address = sender
config = ctx.obj
wallet_path = get_wallet_path(config)
if not (address.startswith('0x') and len(address) == 42):
try:
keystore = load_keystore(sender, wallet_path)
address = keystore.get("address")
print(f"[yellow]Wallet '{sender}' address: {address}")
except Exception:
print(f"[red]Wallet not found: {sender}")
raise typer.Exit(1)
# Resolve wallet name to address if needed
address = sender
config = ctx.obj
wallet_path = get_wallet_path(config)
if not Web3.is_address(address):
try:
keystore = load_keystore(sender, wallet_path)
address = keystore.get("address")
if not Web3.is_address(address):
print(f"[red]Resolved address from wallet '{sender}' is invalid: {address}")
raise typer.Exit(1)
print(f"[yellow]Wallet '{sender}' address: {address}")
except Exception:
# load_keystore already prints an error; exit here to avoid duplication
raise typer.Exit(1)
🤖 Prompt for AI Agents
In hetu_pycli/src/hetu/subnet.py around lines 110 to 121, replace the manual
startswith/len check with Web3 address validation: import Web3 and use
Web3.is_address(sender) to detect valid addresses and
Web3.to_checksum_address(...) to normalize; if sender is not a valid address,
attempt to load_keystore(sender, wallet_path) and if that call fails or returns
no "address" then raise typer.Exit(1) with a single error path (do not print the
error twice); ensure exceptions from load_keystore are caught only to produce
the unified exit (no prior print), and when you get an address normalize it to
checksum form before assigning to address.

Comment on lines +191 to +199
from hetu_pycli.src.hetu.whetu import load_whetu
whetu_contract = get_contract_address(ctx, "whetu_address", None)
whetu = load_whetu(whetu_contract, rpc)

# Get decimals and convert to human readable format
decimals = whetu.decimals()
lock_cost_human = lock_cost_raw / (10 ** decimals)
lock_cost_str = f"{lock_cost_human:,.{decimals}f}".rstrip('0').rstrip('.')
print(f"[green]Network lock cost: {lock_cost_str} WHETU")
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Subnet register: using config whetu_address instead of the contract’s hetuToken; risk of wrong balance/allowance checks

You correctly read whetuToken() into whetu_token_address but then ignore it and load WHETU from config. This can silently check/approve the wrong token.

Apply this diff:

-    from hetu_pycli.src.hetu.whetu import load_whetu
-    whetu_contract = get_contract_address(ctx, "whetu_address", None)
-    whetu = load_whetu(whetu_contract, rpc)
+    from hetu_pycli.src.hetu.whetu import load_whetu
+    whetu = load_whetu(whetu_token_address, rpc)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
from hetu_pycli.src.hetu.whetu import load_whetu
whetu_contract = get_contract_address(ctx, "whetu_address", None)
whetu = load_whetu(whetu_contract, rpc)
# Get decimals and convert to human readable format
decimals = whetu.decimals()
lock_cost_human = lock_cost_raw / (10 ** decimals)
lock_cost_str = f"{lock_cost_human:,.{decimals}f}".rstrip('0').rstrip('.')
print(f"[green]Network lock cost: {lock_cost_str} WHETU")
from hetu_pycli.src.hetu.whetu import load_whetu
whetu = load_whetu(whetu_token_address, rpc)
# Get decimals and convert to human readable format
decimals = whetu.decimals()
lock_cost_human = lock_cost_raw / (10 ** decimals)
lock_cost_str = f"{lock_cost_human:,.{decimals}f}".rstrip('0').rstrip('.')
print(f"[green]Network lock cost: {lock_cost_str} WHETU")
🤖 Prompt for AI Agents
In hetu_pycli/src/hetu/subnet.py around lines 191 to 199, the code loads the
WHETU contract using the config key whetu_address instead of the token address
returned by the contract call (whetu_token_address), which can cause incorrect
balance/allowance checks; replace the line that sets whetu_contract to use the
previously read whetu_token_address (or a safe fallback) when calling load_whetu
so the actual hetuToken contract address is loaded, and keep the rest of the
decimals/formatting logic unchanged.

Comment on lines +12 to +18
def getDendronCount(self, netuid):
"""
Call batchUpdateStakeAllocations(netuid, accounts, newStakes)
Call getDendronCount(netuid)
:param netuid: uint16 (solidity name: 'netuid')
:param accounts: address[] (solidity name: 'accounts')
:param newStakes: uint256[] (solidity name: 'newStakes')
:return: []
"""
return self.contract.functions.batchUpdateStakeAllocations(netuid, accounts, newStakes).call()

def canRegisterNeuron(self, user, netuid, isValidatorRole):
"""
Call canRegisterNeuron(user, netuid, isValidatorRole)
:param user: address (solidity name: 'user')
:param netuid: uint16 (solidity name: 'netuid')
:param isValidatorRole: bool (solidity name: 'isValidatorRole')
:return: [{'internalType': 'bool', 'name': '', 'type': 'bool'}]
"""
return self.contract.functions.canRegisterNeuron(user, netuid, isValidatorRole).call()

def deregisterNeuron(self, netuid):
"""
Call deregisterNeuron(netuid)
:param netuid: uint16 (solidity name: 'netuid')
:return: []
"""
return self.contract.functions.deregisterNeuron(netuid).call()

def distributeRewards(self, netuid, accounts, amounts):
"""
Call distributeRewards(netuid, accounts, amounts)
:param netuid: uint16 (solidity name: 'netuid')
:param accounts: address[] (solidity name: 'accounts')
:param amounts: uint256[] (solidity name: 'amounts')
:return: []
:return: [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}]
"""
return self.contract.functions.distributeRewards(netuid, accounts, amounts).call()
return self.contract.functions.getDendronCount(netuid).call()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent method name mapping

The getDendronCount method calls getDendronCount on the contract, but based on the pattern in other methods and the DendronManager.abi, this should likely call getNeuronCount for compatibility with the underlying contract.

    def getDendronCount(self, netuid):
        """
        Call getDendronCount(netuid)
        :param netuid: uint16 (solidity name: 'netuid')
        :return: [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}]
        """
-       return self.contract.functions.getDendronCount(netuid).call()
+       return self.contract.functions.getNeuronCount(netuid).call()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def getDendronCount(self, netuid):
"""
Call batchUpdateStakeAllocations(netuid, accounts, newStakes)
Call getDendronCount(netuid)
:param netuid: uint16 (solidity name: 'netuid')
:param accounts: address[] (solidity name: 'accounts')
:param newStakes: uint256[] (solidity name: 'newStakes')
:return: []
"""
return self.contract.functions.batchUpdateStakeAllocations(netuid, accounts, newStakes).call()
def canRegisterNeuron(self, user, netuid, isValidatorRole):
"""
Call canRegisterNeuron(user, netuid, isValidatorRole)
:param user: address (solidity name: 'user')
:param netuid: uint16 (solidity name: 'netuid')
:param isValidatorRole: bool (solidity name: 'isValidatorRole')
:return: [{'internalType': 'bool', 'name': '', 'type': 'bool'}]
"""
return self.contract.functions.canRegisterNeuron(user, netuid, isValidatorRole).call()
def deregisterNeuron(self, netuid):
"""
Call deregisterNeuron(netuid)
:param netuid: uint16 (solidity name: 'netuid')
:return: []
"""
return self.contract.functions.deregisterNeuron(netuid).call()
def distributeRewards(self, netuid, accounts, amounts):
"""
Call distributeRewards(netuid, accounts, amounts)
:param netuid: uint16 (solidity name: 'netuid')
:param accounts: address[] (solidity name: 'accounts')
:param amounts: uint256[] (solidity name: 'amounts')
:return: []
:return: [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}]
"""
return self.contract.functions.distributeRewards(netuid, accounts, amounts).call()
return self.contract.functions.getDendronCount(netuid).call()
def getDendronCount(self, netuid):
"""
Call getDendronCount(netuid)
:param netuid: uint16 (solidity name: 'netuid')
:return: [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}]
"""
return self.contract.functions.getNeuronCount(netuid).call()
🤖 Prompt for AI Agents
hetu_pycli/src/hetu/wrapper/dendron_mgr.py lines 12-18: the method
getDendronCount currently calls self.contract.functions.getDendronCount(netuid)
but per ABI and project naming patterns it should call getNeuronCount for
compatibility with the underlying contract; update the function call to
self.contract.functions.getNeuronCount(netuid).call(), adjust the docstring to
reflect getNeuronCount and keep the same parameters and return handling.

Comment on lines +9 to +17
def setWeights(self, netuid, newWeights):
"""
Call setWeights(netuid, newWeights)
:param netuid: uint16 (solidity name: 'netuid')
:param newWeights: Weight[] (solidity name: 'newWeights')
:return: []
"""
return self.contract.functions.setWeights(netuid, newWeights).call()

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Critical: setWeights is state-changing but uses .call(); this never persists on-chain.

Use a transaction path. Either build and return a tx for external signing or send via .transact when an unlocked account is available.

Apply:

-    def setWeights(self, netuid, newWeights):
+    def setWeights(self, netuid, newWeights, tx_params: dict | None = None, build_only: bool = True):
         """
-        Call setWeights(netuid, newWeights)
+        Build or send setWeights(netuid, newWeights)
         :param netuid: uint16 (solidity name: 'netuid')
         :param newWeights: Weight[] (solidity name: 'newWeights')
-        :return: []
+        :param tx_params: dict with at least 'from' when building/sending
+        :param build_only: if True, returns unsigned tx dict; if False, sends via .transact
+        :return: dict (unsigned tx) when build_only=True, else tx hash hex string
         """
-        return self.contract.functions.setWeights(netuid, newWeights).call()
+        # Normalize inputs and checksum addresses
+        fn = self.contract.functions.setWeights(
+            int(netuid),
+            [
+                (self.web3.to_checksum_address(w["dest"]), int(w["weight"]))
+                for w in newWeights
+            ],
+        )
+        if build_only:
+            if not tx_params or "from" not in tx_params:
+                raise ValueError("tx_params with a 'from' address is required to build the transaction")
+            from_addr = self.web3.to_checksum_address(tx_params["from"])
+            base = {"from": from_addr}
+            base["nonce"] = tx_params.get("nonce", self.web3.eth.get_transaction_count(from_addr))
+            # Optional EIP-1559 fields passthrough
+            for k in ("gas", "chainId", "maxFeePerGas", "maxPriorityFeePerGas", "gasPrice", "value"):
+                if k in tx_params:
+                    base[k] = tx_params[k]
+            return fn.build_transaction(base)
+        else:
+            if not tx_params:
+                raise ValueError("tx_params required to send transaction (must include 'from').")
+            return fn.transact(tx_params)

Note: Returning a built tx aligns with your existing sign_tx flow.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def setWeights(self, netuid, newWeights):
"""
Call setWeights(netuid, newWeights)
:param netuid: uint16 (solidity name: 'netuid')
:param newWeights: Weight[] (solidity name: 'newWeights')
:return: []
"""
return self.contract.functions.setWeights(netuid, newWeights).call()
def setWeights(self, netuid, newWeights, tx_params: dict | None = None, build_only: bool = True):
"""
Build or send setWeights(netuid, newWeights)
:param netuid: uint16 (solidity name: 'netuid')
:param newWeights: Weight[] (solidity name: 'newWeights')
:param tx_params: dict with at least 'from' when building/sending
:param build_only: if True, returns unsigned tx dict; if False, sends via .transact
:return: dict (unsigned tx) when build_only=True, else tx hash hex string
"""
# Normalize inputs and checksum addresses
fn = self.contract.functions.setWeights(
int(netuid),
[
(self.web3.to_checksum_address(w["dest"]), int(w["weight"]))
for w in newWeights
],
)
if build_only:
if not tx_params or "from" not in tx_params:
raise ValueError("tx_params with a 'from' address is required to build the transaction")
from_addr = self.web3.to_checksum_address(tx_params["from"])
base = {"from": from_addr}
base["nonce"] = tx_params.get("nonce", self.web3.eth.get_transaction_count(from_addr))
# Optional EIP-1559 fields passthrough
for k in ("gas", "chainId", "maxFeePerGas", "maxPriorityFeePerGas", "gasPrice", "value"):
if k in tx_params:
base[k] = tx_params[k]
return fn.build_transaction(base)
else:
if not tx_params:
raise ValueError("tx_params required to send transaction (must include 'from').")
return fn.transact(tx_params)
🤖 Prompt for AI Agents
In hetu_pycli/src/hetu/wrapper/weights.py around lines 9 to 17, the setWeights
method currently uses .call() which is read-only and does not persist state;
change it to produce a state-changing transaction instead — either build and
return a transaction dict (using
contract.functions.setWeights(...).buildTransaction(...) with appropriate
nonce/gas/from fields) so the caller can sign/send via your existing sign_tx
flow, or if an unlocked account is expected, send directly with .transact(...)
and return the tx hash; ensure required tx fields (from, nonce, gas, gasPrice or
maxFee/maxPriority) are set.

Comment on lines +143 to 152
```bash
# Check subnet status
hetucli subnet subnet-info --netuid <netuid>

# Activate the subnet
hetucli subnet activate-subnet --netuid <netuid> --sender <wallet_name>

# Verify activation status
hetucli subnet subnet-info --netuid <netuid>
```
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Broken code fences: nested ```bash block breaks rendering

There is an opening bash at Line 131 and a second bash at Line 143 without closing the first block. This will render incorrectly on GitHub and can hide content beneath.

Apply this diff to close the first fence before starting the “Check subnet status” block:

-# Activate the subnet using the returned netuid
-hetucli subnet activate-subnet --netuid <returned_netuid> --sender test0
-
-
-```bash
+# Activate the subnet using the returned netuid
+hetucli subnet activate-subnet --netuid <returned_netuid> --sender test0
+```
+
+```bash
 # Check subnet status
 hetucli subnet subnet-info --netuid <netuid>
🤖 Prompt for AI Agents
In README.md around lines 143 to 152, the markdown has nested/open code-fence
issues: close the previous ```bash fence before starting the “Check subnet
status” block by inserting the missing activation command line(s) and a closing
``` on the prior block, then open a fresh ```bash block for the "Check subnet
status" snippet so each fenced block is properly terminated and rendered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant