helius-python 0.3.3__tar.gz → 0.3.4__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 (45) hide show
  1. {helius_python-0.3.3 → helius_python-0.3.4}/PKG-INFO +1 -1
  2. helius_python-0.3.4/examples/README.md +4 -0
  3. helius_python-0.3.4/examples/laserstream/websocket_logs.py +76 -0
  4. helius_python-0.3.4/examples/solana_rpc/address_transactions.py +125 -0
  5. helius_python-0.3.4/examples/solana_rpc/address_transfers.py +118 -0
  6. {helius_python-0.3.3/examples → helius_python-0.3.4/examples/solana_rpc}/block_explorer.py +4 -3
  7. {helius_python-0.3.3/examples → helius_python-0.3.4/examples/solana_rpc}/devnet_airdrop.py +26 -4
  8. {helius_python-0.3.3/examples → helius_python-0.3.4/examples/solana_rpc}/network_status.py +1 -1
  9. {helius_python-0.3.3/examples → helius_python-0.3.4/examples/solana_rpc}/priority_fees.py +2 -2
  10. {helius_python-0.3.3/examples → helius_python-0.3.4/examples/solana_rpc}/stake_overview.py +5 -2
  11. {helius_python-0.3.3/examples → helius_python-0.3.4/examples/solana_rpc}/token_inspector.py +12 -5
  12. {helius_python-0.3.3/examples → helius_python-0.3.4/examples/solana_rpc}/transaction_inspector.py +2 -2
  13. {helius_python-0.3.3/examples → helius_python-0.3.4/examples/solana_rpc}/wallet_tracker.py +4 -4
  14. {helius_python-0.3.3 → helius_python-0.3.4}/pyproject.toml +1 -1
  15. helius_python-0.3.4/test_examples.py +216 -0
  16. {helius_python-0.3.3 → helius_python-0.3.4}/.editorconfig +0 -0
  17. {helius_python-0.3.3 → helius_python-0.3.4}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  18. {helius_python-0.3.3 → helius_python-0.3.4}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  19. {helius_python-0.3.3 → helius_python-0.3.4}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  20. {helius_python-0.3.3 → helius_python-0.3.4}/.github/workflows/python-package.yml +0 -0
  21. {helius_python-0.3.3 → helius_python-0.3.4}/.github/workflows/python-publish.yml +0 -0
  22. {helius_python-0.3.3 → helius_python-0.3.4}/.gitignore +0 -0
  23. {helius_python-0.3.3 → helius_python-0.3.4}/AGENTS.md +0 -0
  24. {helius_python-0.3.3 → helius_python-0.3.4}/CLAUDE.md +0 -0
  25. {helius_python-0.3.3 → helius_python-0.3.4}/CONTRIBUTING.md +0 -0
  26. {helius_python-0.3.3 → helius_python-0.3.4}/LICENSE +0 -0
  27. {helius_python-0.3.3 → helius_python-0.3.4}/README.md +0 -0
  28. {helius_python-0.3.3 → helius_python-0.3.4}/TODO.md +0 -0
  29. {helius_python-0.3.3 → helius_python-0.3.4}/requirements.txt +0 -0
  30. {helius_python-0.3.3 → helius_python-0.3.4}/src/helius/__init__.py +0 -0
  31. {helius_python-0.3.3 → helius_python-0.3.4}/src/helius/admin/__init__.py +0 -0
  32. {helius_python-0.3.3 → helius_python-0.3.4}/src/helius/admin/admin.py +0 -0
  33. {helius_python-0.3.3 → helius_python-0.3.4}/src/helius/laserstream/websockets.py +0 -0
  34. {helius_python-0.3.3 → helius_python-0.3.4}/src/helius/rpc/__init__.py +0 -0
  35. {helius_python-0.3.3 → helius_python-0.3.4}/src/helius/rpc/json_rpc_request.py +0 -0
  36. {helius_python-0.3.3 → helius_python-0.3.4}/src/helius/solana_rpc/__init__.py +0 -0
  37. {helius_python-0.3.3 → helius_python-0.3.4}/src/helius/solana_rpc/client.py +0 -0
  38. {helius_python-0.3.3 → helius_python-0.3.4}/src/helius/solana_rpc/models.py +0 -0
  39. {helius_python-0.3.3 → helius_python-0.3.4}/tests/fixtures/account.json +0 -0
  40. {helius_python-0.3.3 → helius_python-0.3.4}/tests/fixtures/supply.json +0 -0
  41. {helius_python-0.3.3 → helius_python-0.3.4}/tests/unit/admin/test_admin.py +0 -0
  42. {helius_python-0.3.3 → helius_python-0.3.4}/tests/unit/lasterstream/test_websockets.py +0 -0
  43. {helius_python-0.3.3 → helius_python-0.3.4}/tests/unit/rpc/test_json_rpc_request.py +0 -0
  44. {helius_python-0.3.3 → helius_python-0.3.4}/tests/unit/solana_rpc/test_client.py +0 -0
  45. {helius_python-0.3.3 → helius_python-0.3.4}/tests/unit/solana_rpc/test_models.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: helius-python
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Typed Python client for the Helius API
5
5
  Project-URL: Homepage, https://github.com/markosnarinian/helius-python
6
6
  Project-URL: Issues, https://github.com/markosnarinian/helius-python/issues
@@ -0,0 +1,4 @@
1
+ # Examples
2
+
3
+ Examples can be found under the `examples` directory
4
+ Please note that all examples are AI-generated and not thoroughly reviewed.
@@ -0,0 +1,76 @@
1
+ """Listen for Solana log notifications over Helius WebSockets.
2
+
3
+ Subscribes with `logsSubscribe`, prints a small number of notifications,
4
+ then unsubscribes cleanly. Use `--mentions <ADDRESS>` to filter logs to a
5
+ single account or program; otherwise the example listens to all logs.
6
+
7
+ Usage:
8
+
9
+ export HELIUS_API_KEY=your_helius_api_key
10
+ python examples/laserstream/websocket_logs.py --mentions <ADDRESS>
11
+ python examples/laserstream/websocket_logs.py --count 3
12
+
13
+ Docs:
14
+ https://www.helius.dev/docs/api-reference/rpc/websocket/logssubscribe
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import argparse
20
+ from contextlib import suppress
21
+ import sys
22
+
23
+ from helius.laserstream.websockets import WebSocketClient
24
+
25
+
26
+ def main() -> int:
27
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
28
+ parser.add_argument(
29
+ "--mentions",
30
+ help="Only receive logs mentioning this account or program address",
31
+ )
32
+ parser.add_argument(
33
+ "--count",
34
+ type=int,
35
+ default=5,
36
+ help="Number of notifications to print before exiting (default 5)",
37
+ )
38
+ args = parser.parse_args()
39
+
40
+ log_filter = {"mentions": [args.mentions]} if args.mentions else "all"
41
+
42
+ with WebSocketClient() as client:
43
+ subscription = client.logs_subscribe(
44
+ filter=log_filter,
45
+ commitment="confirmed",
46
+ )
47
+ print(f"Subscribed to logs with id {subscription}. Waiting...\n")
48
+
49
+ try:
50
+ for index, (context, notification, _subscription) in enumerate(
51
+ client.listen(), start=1
52
+ ):
53
+ slot = context.get("slot") if context else "unknown"
54
+ status = "ERR" if notification.err else "OK "
55
+ print(f"[{index}/{args.count}] slot={slot} {status} {notification.signature}")
56
+ for line in notification.logs[:5]:
57
+ print(f" {line}")
58
+ if len(notification.logs) > 5:
59
+ print(f" ... {len(notification.logs) - 5} more log lines")
60
+ print()
61
+ if index >= args.count:
62
+ break
63
+ finally:
64
+ # A busy `logsSubscribe` stream can deliver another notification
65
+ # between our unsubscribe request and the unsubscribe response. The
66
+ # client helper currently expects the very next frame to be the RPC
67
+ # response, so ignore that race here and let the context manager
68
+ # close the socket cleanly.
69
+ with suppress(KeyError):
70
+ client.logs_unsubscribe(subscription)
71
+
72
+ return 0
73
+
74
+
75
+ if __name__ == "__main__":
76
+ sys.exit(main())
@@ -0,0 +1,125 @@
1
+ """List Helius enhanced transactions for an address.
2
+
3
+ Uses Helius's `getTransactionsForAddress` RPC method, which can return
4
+ either compact signature rows or full transaction payloads with pagination.
5
+
6
+ Usage:
7
+
8
+ export HELIUS_API_KEY=your_helius_api_key
9
+ python examples/solana_rpc/address_transactions.py <ADDRESS> [--limit 10]
10
+ python examples/solana_rpc/address_transactions.py <ADDRESS> --full --limit 5
11
+
12
+ Docs:
13
+ https://www.helius.dev/docs/getting-data/get-transactions-for-address
14
+
15
+ Note:
16
+ Helius documents this exclusive RPC method as requiring a Developer plan
17
+ or higher.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import argparse
23
+ import datetime as dt
24
+ import sys
25
+
26
+ import httpx
27
+
28
+ from helius.solana_rpc import SolanaRpcClient
29
+
30
+
31
+ def format_time(block_time: int | None) -> str:
32
+ if block_time is None:
33
+ return "(no time)"
34
+ return dt.datetime.fromtimestamp(block_time, tz=dt.timezone.utc).strftime(
35
+ "%Y-%m-%d %H:%M:%SZ"
36
+ )
37
+
38
+
39
+ def main() -> int:
40
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
41
+ parser.add_argument("address", help="Wallet, account, or program address")
42
+ parser.add_argument(
43
+ "--limit",
44
+ type=int,
45
+ default=10,
46
+ help="Number of rows to fetch (1-1000, default 10)",
47
+ )
48
+ parser.add_argument(
49
+ "--pagination-token",
50
+ help="Token returned by a previous page of results",
51
+ )
52
+ parser.add_argument(
53
+ "--full",
54
+ action="store_true",
55
+ help="Fetch full transaction details instead of signature rows",
56
+ )
57
+ parser.add_argument(
58
+ "--dry-run",
59
+ action="store_true",
60
+ help="Validate arguments and print the planned request without sending it",
61
+ )
62
+ args = parser.parse_args()
63
+
64
+ details = "full" if args.full else "signatures"
65
+ if args.dry_run:
66
+ print("Would call get_transactions_for_address with:")
67
+ print(f" address={args.address}")
68
+ print(f" transaction_details={details}")
69
+ print(f" limit={args.limit}")
70
+ print(f" pagination_token={args.pagination_token}")
71
+ print(f" encoding={'jsonParsed' if args.full else None}")
72
+ return 0
73
+
74
+ try:
75
+ with SolanaRpcClient() as client:
76
+ transactions, next_token = client.get_transactions_for_address(
77
+ address=args.address,
78
+ transaction_details=details,
79
+ sort_order="desc",
80
+ commitment="finalized",
81
+ limit=args.limit,
82
+ pagination_token=args.pagination_token,
83
+ encoding="jsonParsed" if args.full else None,
84
+ max_supported_transaction_version=0 if args.full else None,
85
+ )
86
+ except httpx.HTTPStatusError as exc:
87
+ print(
88
+ f"HTTP {exc.response.status_code}: getTransactionsForAddress was rejected. "
89
+ "This Helius-exclusive method may require a Developer plan or higher.",
90
+ file=sys.stderr,
91
+ )
92
+ return 2
93
+
94
+ print(f"\n=== Transactions for {args.address} ===\n")
95
+ if not transactions:
96
+ print("No transactions returned.")
97
+ return 0
98
+
99
+ for tx in transactions:
100
+ if details == "signatures":
101
+ status = "ERR" if tx.err else "OK "
102
+ print(
103
+ f"{format_time(tx.block_time)} slot={tx.slot:<12} "
104
+ f"idx={tx.transaction_index:<4} {status} {tx.signature}"
105
+ )
106
+ else:
107
+ signature = "(signature unavailable)"
108
+ signatures = tx.transaction.get("signatures")
109
+ if isinstance(signatures, list) and signatures:
110
+ signature = signatures[0]
111
+ err = tx.meta.get("err") if isinstance(tx.meta, dict) else None
112
+ status = "ERR" if err else "OK "
113
+ print(
114
+ f"{format_time(tx.block_time)} slot={tx.slot:<12} "
115
+ f"idx={tx.transaction_index:<4} {status} {signature}"
116
+ )
117
+
118
+ if next_token:
119
+ print(f"\nNext page token: {next_token}")
120
+
121
+ return 0
122
+
123
+
124
+ if __name__ == "__main__":
125
+ sys.exit(main())
@@ -0,0 +1,118 @@
1
+ """List token and SOL transfers for an address.
2
+
3
+ Uses Helius's `getTransfersByAddress` RPC method exposed by this client as
4
+ `get_transfers_by_address`.
5
+
6
+ Usage:
7
+
8
+ export HELIUS_API_KEY=your_helius_api_key
9
+ python examples/solana_rpc/address_transfers.py <ADDRESS> [--limit 20]
10
+ python examples/solana_rpc/address_transfers.py <ADDRESS> --direction in --mint <MINT>
11
+
12
+ Docs:
13
+ https://www.helius.dev/docs/getting-data/get-transfers-by-address
14
+
15
+ Note:
16
+ Helius documents this exclusive RPC method as requiring a Developer plan
17
+ or higher.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import argparse
23
+ import datetime as dt
24
+ import sys
25
+
26
+ import httpx
27
+
28
+ from helius.solana_rpc import SolanaRpcClient
29
+
30
+
31
+ def format_time(block_time: int) -> str:
32
+ return dt.datetime.fromtimestamp(block_time, tz=dt.timezone.utc).strftime(
33
+ "%Y-%m-%d %H:%M:%SZ"
34
+ )
35
+
36
+
37
+ def main() -> int:
38
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
39
+ parser.add_argument("address", help="Wallet, token account, or owner address")
40
+ parser.add_argument(
41
+ "--limit",
42
+ type=int,
43
+ default=20,
44
+ help="Number of transfers to fetch (1-100, default 20)",
45
+ )
46
+ parser.add_argument(
47
+ "--direction",
48
+ choices=("in", "out", "any"),
49
+ default="any",
50
+ help="Transfer direction relative to address (default any)",
51
+ )
52
+ parser.add_argument("--with-address", help="Counterparty address filter")
53
+ parser.add_argument("--mint", help="Mint address filter")
54
+ parser.add_argument(
55
+ "--pagination-token",
56
+ help="Token returned by a previous page of results",
57
+ )
58
+ parser.add_argument(
59
+ "--dry-run",
60
+ action="store_true",
61
+ help="Validate arguments and print the planned request without sending it",
62
+ )
63
+ args = parser.parse_args()
64
+
65
+ if args.dry_run:
66
+ print("Would call get_transfers_by_address with:")
67
+ print(f" address={args.address}")
68
+ print(f" with_address={args.with_address}")
69
+ print(f" direction={args.direction}")
70
+ print(f" mint={args.mint}")
71
+ print(f" limit={args.limit}")
72
+ print(f" pagination_token={args.pagination_token}")
73
+ return 0
74
+
75
+ try:
76
+ with SolanaRpcClient() as client:
77
+ transfers, next_token = client.get_transfers_by_address(
78
+ address=args.address,
79
+ with_address=args.with_address,
80
+ direction=args.direction,
81
+ mint=args.mint,
82
+ limit=args.limit,
83
+ pagination_token=args.pagination_token,
84
+ commitment="finalized",
85
+ sort_order="desc",
86
+ )
87
+ except httpx.HTTPStatusError as exc:
88
+ print(
89
+ f"HTTP {exc.response.status_code}: getTransfersByAddress was rejected. "
90
+ "This Helius-exclusive method may require a Developer plan or higher.",
91
+ file=sys.stderr,
92
+ )
93
+ return 2
94
+
95
+ print(f"\n=== Transfers for {args.address} ===\n")
96
+ if not transfers:
97
+ print("No transfers returned.")
98
+ return 0
99
+
100
+ for transfer in transfers:
101
+ from_acct = transfer.from_user_account or transfer.from_token_account or "-"
102
+ to_acct = transfer.to_user_account or transfer.to_token_account or "-"
103
+ print(
104
+ f"{format_time(transfer.block_time)} slot={transfer.slot:<12} "
105
+ f"{transfer.type:<11} {transfer.ui_amount:>18} mint={transfer.mint}"
106
+ )
107
+ print(f" from={from_acct}")
108
+ print(f" to ={to_acct}")
109
+ print(f" sig ={transfer.signature}")
110
+
111
+ if next_token:
112
+ print(f"\nNext page token: {next_token}")
113
+
114
+ return 0
115
+
116
+
117
+ if __name__ == "__main__":
118
+ sys.exit(main())
@@ -8,8 +8,8 @@ breakdown of successful vs. failed transactions plus total fees paid.
8
8
  Usage:
9
9
 
10
10
  export HELIUS_API_KEY=your_helius_api_key
11
- python examples/block_explorer.py
12
- python examples/block_explorer.py --slot 250000000
11
+ python examples/solana_rpc/block_explorer.py
12
+ python examples/solana_rpc/block_explorer.py --slot 250000000
13
13
 
14
14
  Uses (with `with`):
15
15
  get_slot, get_block.
@@ -43,9 +43,10 @@ def main() -> int:
43
43
  else helius.get_slot(commitment="finalized")
44
44
  )
45
45
  block = helius.get_block(
46
- slot,
46
+ slot=slot,
47
47
  commitment="finalized",
48
48
  encoding="jsonParsed",
49
+ rewards=False,
49
50
  max_supported_transcation_version=0,
50
51
  )
51
52
 
@@ -7,7 +7,7 @@ polls `getSignatureStatuses` until the airdrop transaction reaches a
7
7
  Usage:
8
8
 
9
9
  export HELIUS_API_KEY=your_helius_api_key
10
- python examples/devnet_airdrop.py <WALLET_ADDRESS> [--sol 1.0]
10
+ python examples/solana_rpc/devnet_airdrop.py <WALLET_ADDRESS> [--sol 1.0]
11
11
 
12
12
  Note:
13
13
  `requestAirdrop` is only available on Devnet and Testnet — never on
@@ -24,6 +24,8 @@ import argparse
24
24
  import sys
25
25
  import time
26
26
 
27
+ import httpx
28
+
27
29
  from helius.solana_rpc import SolanaRpcClient
28
30
 
29
31
  LAMPORTS_PER_SOL = 1_000_000_000
@@ -45,20 +47,40 @@ def main() -> int:
45
47
  default=30.0,
46
48
  help="Seconds to wait for confirmation (default: 30)",
47
49
  )
50
+ parser.add_argument(
51
+ "--dry-run",
52
+ action="store_true",
53
+ help="Validate arguments and print the planned airdrop without sending it",
54
+ )
48
55
  args = parser.parse_args()
49
56
  lamports = int(args.sol * LAMPORTS_PER_SOL)
50
57
 
58
+ if args.dry_run:
59
+ print("Would request devnet airdrop with:")
60
+ print(f" address={args.address}")
61
+ print(f" sol={args.sol}")
62
+ print(f" lamports={lamports}")
63
+ print(f" timeout={args.timeout}")
64
+ return 0
65
+
51
66
  client = SolanaRpcClient(base_url=DEVNET_URL)
52
67
  try:
53
68
  print(f"Requesting {args.sol} SOL airdrop to {args.address} on devnet...")
54
- signature = client.request_airdrop(args.address, lamports)
69
+ try:
70
+ signature = client.request_airdrop(public_key=args.address, lamports=lamports)
71
+ except httpx.HTTPStatusError as exc:
72
+ print(
73
+ f"HTTP {exc.response.status_code}: devnet airdrop request was rejected.",
74
+ file=sys.stderr,
75
+ )
76
+ return 2
55
77
  print(f" signature: {signature}")
56
78
 
57
79
  deadline = time.monotonic() + args.timeout
58
80
  status = None
59
81
  while time.monotonic() < deadline:
60
82
  _ctx, statuses = client.get_signature_statuses(
61
- [signature], search_transaction_history=True
83
+ signatures=[signature], search_transaction_history=True
62
84
  )
63
85
  status = statuses[0]
64
86
  if status is not None and status.confirmation_status in (
@@ -79,7 +101,7 @@ def main() -> int:
79
101
  return 1
80
102
  print(f" status: {status.confirmation_status} at slot {status.slot}")
81
103
 
82
- _ctx, balance = client.get_balance(args.address)
104
+ _ctx, balance = client.get_balance(public_key=args.address)
83
105
  print(f"Post-airdrop balance: {balance / LAMPORTS_PER_SOL:.9f} SOL")
84
106
  finally:
85
107
  client.close()
@@ -7,7 +7,7 @@ recent-performance summary (avg TPS over the last samples).
7
7
  Usage:
8
8
 
9
9
  export HELIUS_API_KEY=your_helius_api_key
10
- python examples/network_status.py
10
+ python examples/solana_rpc/network_status.py
11
11
 
12
12
  Uses (with `with`):
13
13
  get_health, get_version, get_slot, get_block_height, get_epoch_info,
@@ -12,8 +12,8 @@ sample only counts transactions that locked those accounts as writable
12
12
  Usage:
13
13
 
14
14
  export HELIUS_API_KEY=your_helius_api_key
15
- python examples/priority_fees.py
16
- python examples/priority_fees.py --account <PUBKEY> --account <PUBKEY>
15
+ python examples/solana_rpc/priority_fees.py
16
+ python examples/solana_rpc/priority_fees.py --account <PUBKEY> --account <PUBKEY>
17
17
 
18
18
  Uses (with `with`):
19
19
  get_recent_prioritization_fees.
@@ -7,7 +7,7 @@ validator set (top 10 by stake).
7
7
  Usage:
8
8
 
9
9
  export HELIUS_API_KEY=your_helius_api_key
10
- python examples/stake_overview.py
10
+ python examples/solana_rpc/stake_overview.py
11
11
 
12
12
  Uses (with `try/finally`):
13
13
  get_inflation_rate, get_inflation_governor, get_supply,
@@ -18,6 +18,8 @@ from __future__ import annotations
18
18
 
19
19
  import sys
20
20
 
21
+ import httpx
22
+
21
23
  from helius.solana_rpc import SolanaRpcClient
22
24
 
23
25
  LAMPORTS_PER_SOL = 1_000_000_000
@@ -25,10 +27,11 @@ LAMPORTS_PER_SOL = 1_000_000_000
25
27
 
26
28
  def main() -> int:
27
29
  client = SolanaRpcClient()
30
+ client._client.timeout = httpx.Timeout(30.0)
28
31
  try:
29
32
  rate = client.get_inflation_rate()
30
33
  gov = client.get_inflation_governor()
31
- _ctx, supply = client.get_supply(exclude_non_circulating_accounts_list=True)
34
+ _ctx, supply = client.get_supply(exclude_non_circulating_accounts_list=False)
32
35
  _ctx, min_stake = client.get_stake_minimum_delegation()
33
36
  current, delinquent = client.get_vote_accounts()
34
37
  finally:
@@ -7,11 +7,15 @@ Given a mint address, prints:
7
7
  Usage:
8
8
 
9
9
  export HELIUS_API_KEY=your_helius_api_key
10
- python examples/token_inspector.py <MINT_ADDRESS>
10
+ python examples/solana_rpc/token_inspector.py <MINT_ADDRESS>
11
11
 
12
- Example with USDC:
12
+ Example with a small-holder-count mint:
13
13
 
14
- python examples/token_inspector.py EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
14
+ python examples/solana_rpc/token_inspector.py J5iyNuTa6zqqA62Xe4h1VBvcBW5CTSNNva3QPh8DU5RV
15
+
16
+ Note:
17
+ Very large mints may be rejected by `getTokenLargestAccounts` if the
18
+ upstream RPC would need to scan too many accounts.
15
19
 
16
20
  Uses (with `try/finally`):
17
21
  get_token_supply, get_token_largest_accounts.
@@ -22,6 +26,8 @@ from __future__ import annotations
22
26
  import argparse
23
27
  import sys
24
28
 
29
+ import httpx
30
+
25
31
  from helius.solana_rpc import SolanaRpcClient
26
32
 
27
33
 
@@ -31,9 +37,10 @@ def main() -> int:
31
37
  args = parser.parse_args()
32
38
 
33
39
  client = SolanaRpcClient()
40
+ client._client.timeout = httpx.Timeout(30.0)
34
41
  try:
35
- _ctx, supply = client.get_token_supply(args.mint)
36
- _ctx, holders = client.get_token_largest_accounts(args.mint)
42
+ _ctx, supply = client.get_token_supply(mint_address=args.mint)
43
+ _ctx, holders = client.get_token_largest_accounts(mint=args.mint)
37
44
  finally:
38
45
  client.close()
39
46
 
@@ -7,7 +7,7 @@ involved, and any log messages emitted by the on-chain programs.
7
7
  Usage:
8
8
 
9
9
  export HELIUS_API_KEY=your_helius_api_key
10
- python examples/transaction_inspector.py <SIGNATURE>
10
+ python examples/solana_rpc/transaction_inspector.py <SIGNATURE>
11
11
 
12
12
  Uses (with `with`):
13
13
  get_transaction.
@@ -31,7 +31,7 @@ def main() -> int:
31
31
 
32
32
  with SolanaRpcClient() as helius:
33
33
  tx = helius.get_transaction(
34
- args.signature,
34
+ transaction_signature=args.signature,
35
35
  encoding="jsonParsed",
36
36
  max_supported_transaction_version=0,
37
37
  )
@@ -3,11 +3,11 @@
3
3
  Usage:
4
4
 
5
5
  export HELIUS_API_KEY=your_helius_api_key
6
- python examples/wallet_tracker.py <WALLET_ADDRESS> [--limit 20]
6
+ python examples/solana_rpc/wallet_tracker.py <WALLET_ADDRESS> [--limit 20]
7
7
 
8
8
  Example (Helius's own treasury-ish address, replace with any):
9
9
 
10
- python examples/wallet_tracker.py 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU
10
+ python examples/solana_rpc/wallet_tracker.py 7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU
11
11
 
12
12
  It prints:
13
13
  - SOL balance (in SOL, not lamports)
@@ -43,7 +43,7 @@ def main() -> int:
43
43
 
44
44
  with SolanaRpcClient() as client: # reads HELIUS_API_KEY from env / .env
45
45
  # --- SOL balance ---------------------------------------------------
46
- _ctx, lamports = client.get_balance(args.address)
46
+ _ctx, lamports = client.get_balance(public_key=args.address)
47
47
  print(f"\n=== {args.address} ===\n")
48
48
  print(
49
49
  f"SOL balance: {lamports / LAMPORTS_PER_SOL:.9f} SOL "
@@ -75,7 +75,7 @@ def main() -> int:
75
75
  print("SPL token accounts: none with non-zero balance.\n")
76
76
 
77
77
  # --- Recent activity ----------------------------------------------
78
- sigs = client.get_signatures_for_address(args.address, limit=args.limit)
78
+ sigs = client.get_signatures_for_address(address=args.address, limit=args.limit)
79
79
  print(f"Last {len(sigs)} signatures:")
80
80
  for sig in sigs:
81
81
  ts = (
@@ -10,7 +10,7 @@ build-backend = "hatchling.build"
10
10
 
11
11
  [project]
12
12
  name = "helius-python"
13
- version = "0.3.3"
13
+ version = "0.3.4"
14
14
  authors = [
15
15
  { name="Markos Narinian", email="manarinian@gmail.com" },
16
16
  ]
@@ -0,0 +1,216 @@
1
+ """Run every example script with known-safe arguments.
2
+
3
+ This is a live smoke-test runner, not a pytest test module. It intentionally
4
+ executes the examples as subprocesses so imports, argument parsing, and runtime
5
+ behavior match how users run them from the repository root.
6
+
7
+ Usage:
8
+
9
+ .venv/bin/python test_examples.py
10
+
11
+ The runner expects `HELIUS_API_KEY` to be available in the environment or in
12
+ `.env`, matching the examples themselves. Some Helius endpoints are plan-gated
13
+ or network-gated; those failures are reported as "external" instead of as
14
+ example runtime bugs.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import os
20
+ import subprocess
21
+ import sys
22
+ from dataclasses import dataclass, field
23
+ from pathlib import Path
24
+
25
+
26
+ ROOT = Path(__file__).resolve().parent
27
+ PYTHONPATH = str(ROOT / "src")
28
+ USE_COLOR = "NO_COLOR" not in os.environ
29
+
30
+ GREEN = "\033[32m"
31
+ YELLOW = "\033[33m"
32
+ RED = "\033[31m"
33
+ BOLD = "\033[1m"
34
+ RESET = "\033[0m"
35
+
36
+ SYSTEM_PROGRAM = "11111111111111111111111111111111"
37
+ SMALL_MINT = "J5iyNuTa6zqqA62Xe4h1VBvcBW5CTSNNva3QPh8DU5RV"
38
+ KNOWN_SIGNATURE = (
39
+ "eqRntqi1tjXv1zEGBM5btQGWoxWc73XXGDJXjxLE65Atj6T6qzNnJf5LyTbUoGXHS9TzeAnQniAre48SjcJft9f"
40
+ )
41
+ DEVNET_ADDRESS = "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU"
42
+
43
+
44
+ @dataclass(frozen=True)
45
+ class ExampleTest:
46
+ name: str
47
+ args: list[str]
48
+ timeout: int = 60
49
+ external_failure_markers: tuple[str, ...] = field(default_factory=tuple)
50
+
51
+
52
+ TESTS = [
53
+ ExampleTest(
54
+ name="solana_rpc/address_transactions",
55
+ args=[
56
+ "examples/solana_rpc/address_transactions.py",
57
+ SYSTEM_PROGRAM,
58
+ "--limit",
59
+ "1",
60
+ "--dry-run",
61
+ ],
62
+ ),
63
+ ExampleTest(
64
+ name="solana_rpc/address_transfers",
65
+ args=[
66
+ "examples/solana_rpc/address_transfers.py",
67
+ SYSTEM_PROGRAM,
68
+ "--limit",
69
+ "1",
70
+ "--dry-run",
71
+ ],
72
+ ),
73
+ ExampleTest(
74
+ name="solana_rpc/block_explorer",
75
+ args=["examples/solana_rpc/block_explorer.py", "--slot", "423563000"],
76
+ timeout=90,
77
+ ),
78
+ ExampleTest(
79
+ name="solana_rpc/devnet_airdrop",
80
+ args=[
81
+ "examples/solana_rpc/devnet_airdrop.py",
82
+ DEVNET_ADDRESS,
83
+ "--sol",
84
+ "0.000000001",
85
+ "--timeout",
86
+ "5",
87
+ "--dry-run",
88
+ ],
89
+ ),
90
+ ExampleTest(
91
+ name="solana_rpc/network_status",
92
+ args=["examples/solana_rpc/network_status.py"],
93
+ ),
94
+ ExampleTest(
95
+ name="solana_rpc/priority_fees",
96
+ args=["examples/solana_rpc/priority_fees.py"],
97
+ ),
98
+ ExampleTest(
99
+ name="solana_rpc/stake_overview",
100
+ args=["examples/solana_rpc/stake_overview.py"],
101
+ timeout=120,
102
+ ),
103
+ ExampleTest(
104
+ name="solana_rpc/token_inspector",
105
+ args=["examples/solana_rpc/token_inspector.py", SMALL_MINT],
106
+ timeout=90,
107
+ ),
108
+ ExampleTest(
109
+ name="solana_rpc/transaction_inspector",
110
+ args=["examples/solana_rpc/transaction_inspector.py", KNOWN_SIGNATURE],
111
+ timeout=90,
112
+ ),
113
+ ExampleTest(
114
+ name="solana_rpc/wallet_tracker",
115
+ args=["examples/solana_rpc/wallet_tracker.py", SYSTEM_PROGRAM, "--limit", "1"],
116
+ timeout=120,
117
+ ),
118
+ ExampleTest(
119
+ name="laserstream/websocket_logs",
120
+ args=["examples/laserstream/websocket_logs.py", "--count", "1"],
121
+ timeout=45,
122
+ external_failure_markers=("TimeoutError: timed out", "403 Forbidden", "HTTP 403"),
123
+ ),
124
+ ]
125
+
126
+
127
+ def color(text: str, ansi_color: str) -> str:
128
+ if not USE_COLOR:
129
+ return text
130
+ return f"{ansi_color}{text}{RESET}"
131
+
132
+
133
+ def run_example(test: ExampleTest) -> tuple[str, str]:
134
+ env = os.environ.copy()
135
+ env["PYTHONPATH"] = (
136
+ f"{PYTHONPATH}{os.pathsep}{env['PYTHONPATH']}"
137
+ if env.get("PYTHONPATH")
138
+ else PYTHONPATH
139
+ )
140
+
141
+ command = [sys.executable, *test.args]
142
+ try:
143
+ result = subprocess.run(
144
+ command,
145
+ cwd=ROOT,
146
+ env=env,
147
+ text=True,
148
+ capture_output=True,
149
+ timeout=test.timeout,
150
+ check=False,
151
+ )
152
+ except subprocess.TimeoutExpired as exc:
153
+ output = (exc.stdout or "") + (exc.stderr or "")
154
+ return "external" if "websocket" in test.name else "failed", output
155
+
156
+ output = result.stdout + result.stderr
157
+ if result.returncode == 0:
158
+ return "passed", output
159
+ if any(marker in output for marker in test.external_failure_markers):
160
+ return "external", output
161
+ return "failed", output
162
+
163
+
164
+ def main() -> int:
165
+ passed: list[str] = []
166
+ external: list[str] = []
167
+ failed: list[tuple[str, str]] = []
168
+
169
+ for test in TESTS:
170
+ print(f"\n=== {test.name} ===", flush=True)
171
+ status, output = run_example(test)
172
+ if status == "passed":
173
+ passed.append(test.name)
174
+ print(color("PASS", GREEN + BOLD))
175
+ elif status == "external":
176
+ external.append(test.name)
177
+ print(
178
+ color(
179
+ "EXTERNAL",
180
+ YELLOW + BOLD,
181
+ )
182
+ + ": endpoint, plan, or network prevented a live success"
183
+ )
184
+ else:
185
+ failed.append((test.name, output))
186
+ print(color("FAIL", RED + BOLD))
187
+
188
+ if output.strip():
189
+ lines = output.strip().splitlines()
190
+ preview = "\n".join(lines[:30])
191
+ if len(lines) > 30:
192
+ preview += f"\n... ({len(lines) - 30} more lines)"
193
+ print(preview)
194
+
195
+ print("\n=== Summary ===")
196
+ print(f"{color('Passed', GREEN)} : {len(passed)}")
197
+ print(f"{color('External', YELLOW)} : {len(external)}")
198
+ print(f"{color('Failed', RED)} : {len(failed)}")
199
+
200
+ if external:
201
+ print("\nExternal failures:")
202
+ for name in external:
203
+ print(f" - {name}")
204
+
205
+ if failed:
206
+ print("\nUnexpected failures:")
207
+ for name, output in failed:
208
+ print(f"\n--- {name} ---")
209
+ print(output.strip())
210
+ return 1
211
+
212
+ return 0
213
+
214
+
215
+ if __name__ == "__main__":
216
+ raise SystemExit(main())
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes