tusdt-cli 0.2.4__tar.gz → 0.2.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/PKG-INFO +1 -1
  2. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/pyproject.toml +1 -1
  3. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/__init__.py +1 -1
  4. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/client.py +95 -0
  5. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/commands/auction.py +340 -1
  6. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/commands/governance.py +18 -11
  7. tusdt_cli-0.2.6/src/tusdt_cli/commands/token.py +538 -0
  8. tusdt_cli-0.2.4/src/tusdt_cli/commands/token.py +0 -245
  9. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/.gitignore +0 -0
  10. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/LICENSE +0 -0
  11. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/README.md +0 -0
  12. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/requirements.txt +0 -0
  13. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/abi/tusdt_auction.json +0 -0
  14. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/abi/tusdt_erc20.json +0 -0
  15. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/abi/tusdt_governance.json +0 -0
  16. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/abi/tusdt_oracle.json +0 -0
  17. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/abi/tusdt_treasury.json +0 -0
  18. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/abi/tusdt_vault.json +0 -0
  19. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/cli.py +0 -0
  20. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/commands/__init__.py +0 -0
  21. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/commands/oracle.py +0 -0
  22. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/commands/treasury.py +0 -0
  23. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/commands/vault.py +0 -0
  24. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/config.py +0 -0
  25. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/utils.py +0 -0
  26. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/src/tusdt_cli/wallet.py +0 -0
  27. {tusdt_cli-0.2.4 → tusdt_cli-0.2.6}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tusdt-cli
3
- Version: 0.2.4
3
+ Version: 0.2.6
4
4
  Summary: CLI for interacting with the TUSDT ink! smart contract system on subtensor(bittensor)
5
5
  Project-URL: Homepage, https://github.com/tensorusd/tusdt-cli
6
6
  Project-URL: Repository, https://github.com/tensorusd/tusdt-cli
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "tusdt-cli"
7
- version = "0.2.4"
7
+ version = "0.2.6"
8
8
  description = "CLI for interacting with the TUSDT ink! smart contract system on subtensor(bittensor)"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,3 +1,3 @@
1
1
  """TUSDT CLI - Command-line interface for the TUSDT ink! smart contract system."""
2
2
 
3
- __version__ = "0.2.4"
3
+ __version__ = "0.2.6"
@@ -722,6 +722,37 @@ class TUSDTClient:
722
722
  """Transfer *amount* TUSDT tokens to *to*."""
723
723
  return self._exec(self.token, keypair, "transfer", args={"to": to, "value": amount})
724
724
 
725
+ def token_controller(self, keypair: Keypair) -> str:
726
+ """Return the controller address of the TUSDT token contract."""
727
+ result = self._read(self.token, keypair, "controller")
728
+ return unwrap_plain(result)
729
+
730
+ def token_mint(self, keypair: Keypair, to: str, amount: int) -> dict[str, Any]:
731
+ """Mint *amount* TUSDT tokens to *to* (controller only)."""
732
+ return self._exec(self.token, keypair, "mint", args={"to": to, "value": amount})
733
+
734
+ def token_burn(self, keypair: Keypair, from_addr: str, amount: int) -> dict[str, Any]:
735
+ """Burn *amount* TUSDT tokens from *from_addr* (controller only)."""
736
+ return self._exec(self.token, keypair, "burn", args={"from": from_addr, "value": amount})
737
+
738
+ def token_increase_allowance(self, keypair: Keypair, spender: str, delta: int) -> dict[str, Any]:
739
+ """Increase *spender*'s allowance by *delta* TUSDT tokens."""
740
+ return self._exec(
741
+ self.token, keypair, "increase_allowance", args={"spender": spender, "delta_value": delta}
742
+ )
743
+
744
+ def token_decrease_allowance(self, keypair: Keypair, spender: str, delta: int) -> dict[str, Any]:
745
+ """Decrease *spender*'s allowance by *delta* TUSDT tokens."""
746
+ return self._exec(
747
+ self.token, keypair, "decrease_allowance", args={"spender": spender, "delta_value": delta}
748
+ )
749
+
750
+ def token_transfer_from(self, keypair: Keypair, from_addr: str, to: str, amount: int) -> dict[str, Any]:
751
+ """Transfer *amount* TUSDT tokens from *from_addr* to *to* (requires allowance)."""
752
+ return self._exec(
753
+ self.token, keypair, "transfer_from", args={"from": from_addr, "to": to, "value": amount}
754
+ )
755
+
725
756
  # ==================================================================
726
757
  # AUCTION OPERATIONS
727
758
  # ==================================================================
@@ -826,6 +857,70 @@ class TUSDTClient:
826
857
  return None
827
858
  return raw if isinstance(raw, dict) else raw
828
859
 
860
+ def get_auction_controller(self, keypair: Keypair) -> str:
861
+ """Return the auction controller address."""
862
+ result = self._read(self.auction, keypair, "controller")
863
+ return unwrap_plain(result)
864
+
865
+ def get_auction_governance(self, keypair: Keypair) -> str:
866
+ """Return the auction governance address."""
867
+ result = self._read(self.auction, keypair, "governance")
868
+ return unwrap_plain(result)
869
+
870
+ def get_auction_admin(self, keypair: Keypair) -> str | None:
871
+ """Return the auction admin address, or None if not set."""
872
+ result = self._read(self.auction, keypair, "admin")
873
+ return unwrap_option(result)
874
+
875
+ def create_auction(
876
+ self,
877
+ keypair: Keypair,
878
+ vault_owner: str,
879
+ vault_id: int,
880
+ collateral_balance: int,
881
+ debt_balance: int,
882
+ min_bid: int,
883
+ liquidation_price: int,
884
+ duration_ms: int,
885
+ ) -> dict[str, Any]:
886
+ """Create a new liquidation auction (controller only)."""
887
+ return self._exec(
888
+ self.auction,
889
+ keypair,
890
+ "create_auction",
891
+ args={
892
+ "vault_owner": vault_owner,
893
+ "vault_id": vault_id,
894
+ "collateral_balance": collateral_balance,
895
+ "debt_balance": debt_balance,
896
+ "min_bid": min_bid,
897
+ "liquidation_price": liquidation_price,
898
+ "duration_ms": duration_ms,
899
+ },
900
+ )
901
+
902
+ def auction_set_admin(self, keypair: Keypair, admin: str | None) -> dict[str, Any]:
903
+ """Set or clear the auction admin (governance only). Pass None to clear."""
904
+ return self._exec(self.auction, keypair, "set_admin", args={"admin": admin})
905
+
906
+ def auction_update_governance(self, keypair: Keypair, new_governance: str) -> dict[str, Any]:
907
+ """Transfer auction governance to a new account (controller only)."""
908
+ return self._exec(self.auction, keypair, "update_governance", args={"new_governance": new_governance})
909
+
910
+ def auction_transfer_winning_bid(
911
+ self,
912
+ keypair: Keypair,
913
+ auction_id: int,
914
+ recipient: str,
915
+ ) -> dict[str, Any]:
916
+ """Transfer the winning bid amount to a recipient (controller only)."""
917
+ return self._exec(
918
+ self.auction,
919
+ keypair,
920
+ "transfer_winning_bid",
921
+ args={"auction_id": auction_id, "recipient": recipient},
922
+ )
923
+
829
924
  # ==================================================================
830
925
  # ORACLE OPERATIONS
831
926
  # ==================================================================
@@ -32,7 +32,18 @@ _wallet_option = click.option(
32
32
  )
33
33
 
34
34
 
35
- _AUCTION_ADVANCED = {"list-all", "total-count", "vault-auction", "bids", "bid-info"}
35
+ _AUCTION_ADVANCED = {
36
+ "list-all",
37
+ "total-count",
38
+ "vault-auction",
39
+ "bids",
40
+ "bid-info",
41
+ "create",
42
+ "set-admin",
43
+ "update-governance",
44
+ "transfer-winning-bid",
45
+ }
46
+ _AUCTION_BASIC_READS = {"controller", "governance", "admin-address"}
36
47
 
37
48
 
38
49
  @click.group("auction", cls=ModeAwareGroup, advanced_commands=_AUCTION_ADVANCED)
@@ -605,3 +616,331 @@ def bid_info(ctx: click.Context, auction_id: int, bid_id: int, network: str | No
605
616
  "Withdrawn": data.get("is_withdrawn", False),
606
617
  },
607
618
  )
619
+
620
+
621
+ # ------------------------------------------------------------------
622
+ # controller
623
+ # ------------------------------------------------------------------
624
+
625
+
626
+ @auction_group.command("controller")
627
+ @_network_option
628
+ @click.pass_context
629
+ def auction_controller_cmd(ctx: click.Context, network: str | None) -> None:
630
+ """Show the controller address of the auction contract.
631
+
632
+ \b
633
+ Examples:
634
+ tusdt auction controller
635
+ tusdt auction controller --network testnet
636
+ """
637
+ config = load_config(network=network)
638
+
639
+ try:
640
+ keypair = get_reader_keypair(config)
641
+ client = TUSDTClient(config)
642
+ addr = client.get_auction_controller(keypair)
643
+ except Exception as exc:
644
+ print_error(str(exc))
645
+ return
646
+
647
+ print_dict("Auction Controller", {"Address": addr})
648
+
649
+
650
+ # ------------------------------------------------------------------
651
+ # governance
652
+ # ------------------------------------------------------------------
653
+
654
+
655
+ @auction_group.command("governance")
656
+ @_network_option
657
+ @click.pass_context
658
+ def auction_governance_cmd(ctx: click.Context, network: str | None) -> None:
659
+ """Show the governance address of the auction contract.
660
+
661
+ \b
662
+ Examples:
663
+ tusdt auction governance
664
+ tusdt auction governance --network testnet
665
+ """
666
+ config = load_config(network=network)
667
+
668
+ try:
669
+ keypair = get_reader_keypair(config)
670
+ client = TUSDTClient(config)
671
+ addr = client.get_auction_governance(keypair)
672
+ except Exception as exc:
673
+ print_error(str(exc))
674
+ return
675
+
676
+ print_dict("Auction Governance", {"Address": addr})
677
+
678
+
679
+ # ------------------------------------------------------------------
680
+ # admin-address
681
+ # ------------------------------------------------------------------
682
+
683
+
684
+ @auction_group.command("admin-address")
685
+ @_network_option
686
+ @click.pass_context
687
+ def auction_admin_cmd(ctx: click.Context, network: str | None) -> None:
688
+ """Show the admin address of the auction contract, if set.
689
+
690
+ \b
691
+ Examples:
692
+ tusdt auction admin-address
693
+ tusdt auction admin-address --network testnet
694
+ """
695
+ config = load_config(network=network)
696
+
697
+ try:
698
+ keypair = get_reader_keypair(config)
699
+ client = TUSDTClient(config)
700
+ admin = client.get_auction_admin(keypair)
701
+ except Exception as exc:
702
+ print_error(str(exc))
703
+ return
704
+
705
+ if admin is None:
706
+ print_info("No admin set")
707
+ else:
708
+ print_dict("Auction Admin", {"Address": admin})
709
+
710
+
711
+ # ------------------------------------------------------------------
712
+ # create
713
+ # ------------------------------------------------------------------
714
+
715
+
716
+ @auction_group.command("create")
717
+ @click.argument("vault_owner", type=str, metavar="<ss58-address>")
718
+ @click.argument("vault_id", type=int, metavar="<vault-id>")
719
+ @click.option(
720
+ "--collateral-balance",
721
+ type=str,
722
+ required=True,
723
+ help="Collateral balance in human-readable units (e.g. 10.0)",
724
+ )
725
+ @click.option(
726
+ "--debt-balance", type=str, required=True, help="Debt balance in human-readable units (e.g. 100.0)"
727
+ )
728
+ @click.option("--min-bid", type=str, required=True, help="Minimum bid in human-readable units (e.g. 10.0)")
729
+ @click.option(
730
+ "--liquidation-price", type=int, required=True, help="Liquidation price as raw integer (10^18 scale)"
731
+ )
732
+ @click.option("--duration-ms", type=int, required=True, help="Auction duration in milliseconds (integer)")
733
+ @_wallet_option
734
+ @_network_option
735
+ @click.pass_context
736
+ def create_auction_cmd(
737
+ ctx: click.Context,
738
+ vault_owner: str,
739
+ vault_id: int,
740
+ collateral_balance: str,
741
+ debt_balance: str,
742
+ min_bid: str,
743
+ liquidation_price: int,
744
+ duration_ms: int,
745
+ wallet_name: str | None,
746
+ network: str | None,
747
+ ) -> None:
748
+ """Create a new liquidation auction (controller only).
749
+
750
+ \b
751
+ VAULT_OWNER is the SS58 address of the vault owner.
752
+ VAULT_ID is the numeric vault ID (integer).
753
+ Examples:
754
+ tusdt auction create 5GrwvaEF... 0 --collateral-balance 10 --debt-balance 100 --min-bid 10 --liquidation-price 1500000000000000000 --duration-ms 86400000 --wallet-name MyWallet
755
+ """
756
+ config = load_config(network=network)
757
+ if wallet_name:
758
+ config["wallet_name"] = wallet_name
759
+ decimals = config.get("decimals", 9)
760
+
761
+ raw_collateral = parse_balance(collateral_balance, decimals)
762
+ raw_debt = parse_balance(debt_balance, decimals)
763
+ raw_min_bid = parse_balance(min_bid, decimals)
764
+
765
+ try:
766
+ vault_owner = resolve_ss58(vault_owner, config.get("wallet_path"))
767
+ except Exception as exc:
768
+ print_error(str(exc))
769
+
770
+ try:
771
+ keypair = get_signer_keypair(config)
772
+ except Exception as exc:
773
+ print_error(str(exc))
774
+
775
+ print_info(f"Signer: {keypair.ss58_address}")
776
+ print_info(f"Creating auction for vault {vault_id} (owner: {vault_owner})...")
777
+
778
+ try:
779
+ client = TUSDTClient(config)
780
+ result = client.create_auction(
781
+ keypair,
782
+ vault_owner,
783
+ vault_id,
784
+ raw_collateral,
785
+ raw_debt,
786
+ raw_min_bid,
787
+ liquidation_price,
788
+ duration_ms,
789
+ )
790
+ print_success("Auction created!")
791
+ print_tx_result(result, config.get("network", "finney"))
792
+ except Exception as exc:
793
+ print_error(str(exc))
794
+
795
+
796
+ # ------------------------------------------------------------------
797
+ # set-admin
798
+ # ------------------------------------------------------------------
799
+
800
+
801
+ @auction_group.command("set-admin")
802
+ @click.argument("address", type=str, metavar="<ss58-address>", required=False)
803
+ @click.option("--clear", is_flag=True, default=False, help="Clear the admin (set to None)")
804
+ @_wallet_option
805
+ @_network_option
806
+ @click.pass_context
807
+ def auction_set_admin_cmd(
808
+ ctx: click.Context,
809
+ address: str | None,
810
+ clear: bool,
811
+ wallet_name: str | None,
812
+ network: str | None,
813
+ ) -> None:
814
+ """Set or clear the auction admin (governance only).
815
+
816
+ \b
817
+ ADDRESS is the SS58 address of the admin, or omit and use --clear to remove.
818
+ Examples:
819
+ tusdt auction set-admin 5GrwvaEF... --wallet-name MyWallet
820
+ tusdt auction set-admin --clear --wallet-name MyWallet
821
+ """
822
+ config = load_config(network=network)
823
+ if wallet_name:
824
+ config["wallet_name"] = wallet_name
825
+
826
+ admin = None if clear else address
827
+ if not clear and not address:
828
+ print_error("Provide an SS58 address or use --clear to remove the admin")
829
+ return
830
+
831
+ try:
832
+ keypair = get_signer_keypair(config)
833
+ except Exception as exc:
834
+ print_error(str(exc))
835
+ return
836
+
837
+ action = "Clearing" if clear else f"Setting to {address}"
838
+ print_info(f"Signer: {keypair.ss58_address}")
839
+ print_info(f"{action} auction admin...")
840
+
841
+ try:
842
+ client = TUSDTClient(config)
843
+ result = client.auction_set_admin(keypair, admin)
844
+ print_success("Auction admin updated!")
845
+ print_tx_result(result, config.get("network", "finney"))
846
+ except Exception as exc:
847
+ print_error(str(exc))
848
+
849
+
850
+ # ------------------------------------------------------------------
851
+ # update-governance
852
+ # ------------------------------------------------------------------
853
+
854
+
855
+ @auction_group.command("update-governance")
856
+ @click.argument("address", type=str, metavar="<ss58-address>")
857
+ @_wallet_option
858
+ @_network_option
859
+ @click.pass_context
860
+ def auction_update_governance_cmd(
861
+ ctx: click.Context,
862
+ address: str,
863
+ wallet_name: str | None,
864
+ network: str | None,
865
+ ) -> None:
866
+ """Transfer auction governance to a new account (controller only).
867
+
868
+ \b
869
+ ADDRESS is the SS58 address of the new governance account.
870
+ Examples:
871
+ tusdt auction update-governance 5GrwvaEF... --wallet-name MyWallet
872
+ tusdt auction update-governance 5GrwvaEF... --wallet-name MyWallet --network testnet
873
+ """
874
+ config = load_config(network=network)
875
+ if wallet_name:
876
+ config["wallet_name"] = wallet_name
877
+
878
+ try:
879
+ keypair = get_signer_keypair(config)
880
+ except Exception as exc:
881
+ print_error(str(exc))
882
+ return
883
+
884
+ print_info(f"Signer: {keypair.ss58_address}")
885
+ print_info(f"Updating auction governance to {address}...")
886
+
887
+ try:
888
+ client = TUSDTClient(config)
889
+ result = client.auction_update_governance(keypair, address)
890
+ print_success("Auction governance updated!")
891
+ print_tx_result(result, config.get("network", "finney"))
892
+ except Exception as exc:
893
+ print_error(str(exc))
894
+
895
+
896
+ # ------------------------------------------------------------------
897
+ # transfer-winning-bid
898
+ # ------------------------------------------------------------------
899
+
900
+
901
+ @auction_group.command("transfer-winning-bid")
902
+ @click.argument("auction_id", type=int, metavar="<auction-id>")
903
+ @click.argument("recipient", type=str, metavar="<ss58-address>")
904
+ @_wallet_option
905
+ @_network_option
906
+ @click.pass_context
907
+ def transfer_winning_bid_cmd(
908
+ ctx: click.Context,
909
+ auction_id: int,
910
+ recipient: str,
911
+ wallet_name: str | None,
912
+ network: str | None,
913
+ ) -> None:
914
+ """Transfer the winning bid amount to a recipient (controller only).
915
+
916
+ \b
917
+ AUCTION_ID is the numeric auction ID (integer).
918
+ RECIPIENT is the SS58 address to receive the winning bid tokens.
919
+ Examples:
920
+ tusdt auction transfer-winning-bid 0 5GrwvaEF... --wallet-name MyWallet
921
+ tusdt auction transfer-winning-bid 5 5GrwvaEF... --wallet-name MyWallet --network testnet
922
+ """
923
+ config = load_config(network=network)
924
+ if wallet_name:
925
+ config["wallet_name"] = wallet_name
926
+
927
+ try:
928
+ recipient = resolve_ss58(recipient, config.get("wallet_path"))
929
+ except Exception as exc:
930
+ print_error(str(exc))
931
+
932
+ try:
933
+ keypair = get_signer_keypair(config)
934
+ except Exception as exc:
935
+ print_error(str(exc))
936
+
937
+ print_info(f"Signer: {keypair.ss58_address}")
938
+ print_info(f"Transferring winning bid for auction {auction_id} to {recipient}...")
939
+
940
+ try:
941
+ client = TUSDTClient(config)
942
+ result = client.auction_transfer_winning_bid(keypair, auction_id, recipient)
943
+ print_success("Winning bid transferred!")
944
+ print_tx_result(result, config.get("network", "finney"))
945
+ except Exception as exc:
946
+ print_error(str(exc))
@@ -1257,10 +1257,11 @@ def submit_proposal_cmd(
1257
1257
  )
1258
1258
  @click.option(
1259
1259
  "--proof",
1260
- type=str,
1260
+ "proofs",
1261
+ multiple=True,
1261
1262
  required=True,
1262
1263
  metavar="<hex-string>",
1263
- help="Merkle proof as hex-encoded bytes (e.g. 0x...)",
1264
+ help="Merkle proof hash as hex-encoded 32 bytes (repeatable, e.g. --proof 0xabc... --proof 0xdef...)",
1264
1265
  )
1265
1266
  @_wallet_option
1266
1267
  @_network_option
@@ -1272,7 +1273,7 @@ def vote_cmd(
1272
1273
  support: bool,
1273
1274
  balance: str,
1274
1275
  multiplier_bps: int,
1275
- proof: str,
1276
+ proofs: tuple[str, ...],
1276
1277
  wallet_name: str | None,
1277
1278
  network: str | None,
1278
1279
  ) -> None:
@@ -1280,9 +1281,10 @@ def vote_cmd(
1280
1281
 
1281
1282
  \b
1282
1283
  PROPOSAL_ID is the numeric proposal ID (integer).
1284
+ Each --proof is a hex-encoded 32-byte Merkle proof hash; repeat for multiple proof nodes.
1283
1285
  Examples:
1284
- tusdt governance vote 0 --hotkey 5GrwvaEF... --support --balance 1000 --multiplier-bps 10000 --proof 0xabcdef --wallet-name MyWallet
1285
- tusdt governance vote 0 --hotkey 5GrwvaEF... --oppose --balance 500 --multiplier-bps 10000 --proof 0xabcdef --wallet-name MyWallet
1286
+ tusdt governance vote 0 --hotkey 5GrwvaEF... --support --balance 1000 --multiplier-bps 10000 --proof 0x5637... --wallet-name MyWallet
1287
+ tusdt governance vote 0 --hotkey 5GrwvaEF... --oppose --balance 500 --multiplier-bps 10000 --proof 0x5637... --proof 0x8087... --wallet-name MyWallet
1286
1288
  """
1287
1289
  config = load_config(network=network)
1288
1290
  if wallet_name:
@@ -1290,12 +1292,17 @@ def vote_cmd(
1290
1292
  decimals = config.get("decimals", 9)
1291
1293
  raw_balance = parse_balance(balance, decimals)
1292
1294
 
1293
- # Convert hex proof to list of ints
1294
- proof_hex = proof.strip()
1295
- if proof_hex.startswith("0x"):
1296
- proof_hex = proof_hex[2:]
1297
- proof_bytes = bytes.fromhex(proof_hex)
1298
- proof_list = list(proof_bytes)
1295
+ # Convert each hex proof hash to a list of 32 bytes (MerkleHash = [u8; 32])
1296
+ proof_list = []
1297
+ for proof_hex in proofs:
1298
+ ph = proof_hex.strip()
1299
+ if ph.startswith("0x"):
1300
+ ph = ph[2:]
1301
+ proof_bytes = bytes.fromhex(ph)
1302
+ if len(proof_bytes) != 32:
1303
+ print_error(f"Each --proof must be exactly 32 bytes (64 hex chars), got {len(proof_bytes)} bytes")
1304
+ return
1305
+ proof_list.append(list(proof_bytes))
1299
1306
 
1300
1307
  try:
1301
1308
  keypair = get_signer_keypair(config)