hippius 0.2.0__py3-none-any.whl → 0.2.1__py3-none-any.whl
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.
- {hippius-0.2.0.dist-info → hippius-0.2.1.dist-info}/METADATA +2 -1
- hippius-0.2.1.dist-info/RECORD +12 -0
- hippius_sdk/__init__.py +1 -1
- hippius_sdk/cli.py +414 -43
- hippius_sdk/ipfs.py +124 -20
- hippius_sdk/substrate.py +468 -10
- hippius-0.2.0.dist-info/RECORD +0 -12
- {hippius-0.2.0.dist-info → hippius-0.2.1.dist-info}/WHEEL +0 -0
- {hippius-0.2.0.dist-info → hippius-0.2.1.dist-info}/entry_points.txt +0 -0
hippius_sdk/substrate.py
CHANGED
@@ -1,23 +1,22 @@
|
|
1
|
-
|
2
|
-
Substrate operations for the Hippius SDK.
|
3
|
-
|
4
|
-
Note: This functionality is coming soon and not implemented yet.
|
5
|
-
"""
|
6
|
-
|
1
|
+
import datetime
|
7
2
|
import json
|
8
3
|
import os
|
9
4
|
import tempfile
|
5
|
+
import time
|
10
6
|
import uuid
|
11
|
-
from typing import Any, Dict, List, Optional,
|
7
|
+
from typing import Any, Dict, List, Optional, Union
|
12
8
|
|
13
9
|
from dotenv import load_dotenv
|
10
|
+
from mnemonic import Mnemonic
|
14
11
|
from substrateinterface import Keypair, SubstrateInterface
|
15
12
|
|
16
13
|
from hippius_sdk.config import (
|
17
14
|
get_account_address,
|
18
15
|
get_active_account,
|
16
|
+
get_all_config,
|
19
17
|
get_config_value,
|
20
18
|
get_seed_phrase,
|
19
|
+
set_active_account,
|
21
20
|
set_seed_phrase,
|
22
21
|
)
|
23
22
|
from hippius_sdk.utils import hex_to_ipfs_cid
|
@@ -181,6 +180,300 @@ class SubstrateClient:
|
|
181
180
|
print(f"Warning: Could not get seed phrase from config: {e}")
|
182
181
|
return False
|
183
182
|
|
183
|
+
def generate_mnemonic(self) -> str:
|
184
|
+
"""
|
185
|
+
Generate a new random 12-word mnemonic phrase.
|
186
|
+
|
187
|
+
Returns:
|
188
|
+
str: A 12-word mnemonic seed phrase
|
189
|
+
"""
|
190
|
+
try:
|
191
|
+
mnemo = Mnemonic("english")
|
192
|
+
return mnemo.generate(strength=128) # 128 bits = 12 words
|
193
|
+
except Exception as e:
|
194
|
+
raise ValueError(f"Error generating mnemonic: {e}")
|
195
|
+
|
196
|
+
def create_account(
|
197
|
+
self, name: str, encode: bool = False, password: Optional[str] = None
|
198
|
+
) -> Dict[str, Any]:
|
199
|
+
"""
|
200
|
+
Create a new account with a generated seed phrase.
|
201
|
+
|
202
|
+
Args:
|
203
|
+
name: Name for the new account
|
204
|
+
encode: Whether to encrypt the seed phrase with a password
|
205
|
+
password: Optional password for encryption (will prompt if not provided and encode=True)
|
206
|
+
|
207
|
+
Returns:
|
208
|
+
Dict[str, Any]: Dictionary with new account details
|
209
|
+
"""
|
210
|
+
# Check if account name already exists
|
211
|
+
config = get_all_config()
|
212
|
+
if name in config["substrate"].get("accounts", {}):
|
213
|
+
raise ValueError(f"Account with name '{name}' already exists")
|
214
|
+
|
215
|
+
# Generate a new mnemonic seed phrase
|
216
|
+
mnemonic = self.generate_mnemonic()
|
217
|
+
|
218
|
+
# Create a keypair from the mnemonic
|
219
|
+
keypair = Keypair.create_from_mnemonic(mnemonic)
|
220
|
+
ss58_address = keypair.ss58_address
|
221
|
+
|
222
|
+
# Save the seed phrase to configuration
|
223
|
+
if encode:
|
224
|
+
result = set_seed_phrase(
|
225
|
+
mnemonic, encode=True, password=password, account_name=name
|
226
|
+
)
|
227
|
+
else:
|
228
|
+
result = set_seed_phrase(mnemonic, encode=False, account_name=name)
|
229
|
+
|
230
|
+
if not result:
|
231
|
+
raise RuntimeError("Failed to save account to configuration")
|
232
|
+
|
233
|
+
# Set this as the active account
|
234
|
+
set_active_account(name)
|
235
|
+
|
236
|
+
# Update the client's state to use this account
|
237
|
+
self._account_name = name
|
238
|
+
self._account_address = ss58_address
|
239
|
+
self._seed_phrase = mnemonic
|
240
|
+
self._keypair = keypair
|
241
|
+
self._read_only = False
|
242
|
+
|
243
|
+
# Return the new account details
|
244
|
+
return {
|
245
|
+
"name": name,
|
246
|
+
"address": ss58_address,
|
247
|
+
"mnemonic": mnemonic,
|
248
|
+
"is_active": True,
|
249
|
+
"creation_date": datetime.datetime.now().isoformat(),
|
250
|
+
}
|
251
|
+
|
252
|
+
def export_account(
|
253
|
+
self, account_name: Optional[str] = None, file_path: Optional[str] = None
|
254
|
+
) -> str:
|
255
|
+
"""
|
256
|
+
Export an account to a JSON file.
|
257
|
+
|
258
|
+
Args:
|
259
|
+
account_name: Name of the account to export (uses active account if None)
|
260
|
+
file_path: Path to save the exported account file (auto-generated if None)
|
261
|
+
|
262
|
+
Returns:
|
263
|
+
str: Path to the exported account file
|
264
|
+
"""
|
265
|
+
# Determine which account to export
|
266
|
+
name_to_use = account_name or self._account_name or get_active_account()
|
267
|
+
if not name_to_use:
|
268
|
+
raise ValueError("No account specified and no active account")
|
269
|
+
|
270
|
+
# Get the seed phrase and address
|
271
|
+
seed_phrase = get_seed_phrase(account_name=name_to_use)
|
272
|
+
if not seed_phrase:
|
273
|
+
raise ValueError(
|
274
|
+
f"Could not retrieve seed phrase for account '{name_to_use}'"
|
275
|
+
)
|
276
|
+
|
277
|
+
address = get_account_address(name_to_use)
|
278
|
+
if not address:
|
279
|
+
# Generate the address from the seed phrase
|
280
|
+
keypair = Keypair.create_from_mnemonic(seed_phrase)
|
281
|
+
address = keypair.ss58_address
|
282
|
+
|
283
|
+
# Create the export data structure
|
284
|
+
export_data = {
|
285
|
+
"name": name_to_use,
|
286
|
+
"address": address,
|
287
|
+
"mnemonic": seed_phrase,
|
288
|
+
"meta": {
|
289
|
+
"exported_at": datetime.datetime.now().isoformat(),
|
290
|
+
"description": "Hippius SDK exported account",
|
291
|
+
},
|
292
|
+
}
|
293
|
+
|
294
|
+
# Determine the file path if not provided
|
295
|
+
if not file_path:
|
296
|
+
file_path = f"{name_to_use}_account_export_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
297
|
+
|
298
|
+
# Write the export file
|
299
|
+
try:
|
300
|
+
with open(file_path, "w") as f:
|
301
|
+
json.dump(export_data, f, indent=2)
|
302
|
+
print(f"Account '{name_to_use}' exported to {file_path}")
|
303
|
+
return file_path
|
304
|
+
except Exception as e:
|
305
|
+
raise ValueError(f"Failed to export account: {e}")
|
306
|
+
|
307
|
+
def import_account(
|
308
|
+
self, file_path: str, password: Optional[str] = None
|
309
|
+
) -> Dict[str, Any]:
|
310
|
+
"""
|
311
|
+
Import an account from a JSON file.
|
312
|
+
|
313
|
+
Args:
|
314
|
+
file_path: Path to the account export file
|
315
|
+
password: Optional password to use for encrypting the imported seed phrase
|
316
|
+
|
317
|
+
Returns:
|
318
|
+
Dict[str, Any]: Dictionary with imported account details
|
319
|
+
"""
|
320
|
+
try:
|
321
|
+
# Read the export file
|
322
|
+
with open(file_path, "r") as f:
|
323
|
+
import_data = json.load(f)
|
324
|
+
|
325
|
+
# Validate the import data structure
|
326
|
+
required_fields = ["name", "address", "mnemonic"]
|
327
|
+
for field in required_fields:
|
328
|
+
if field not in import_data:
|
329
|
+
raise ValueError(
|
330
|
+
f"Invalid account file format: missing '{field}' field"
|
331
|
+
)
|
332
|
+
|
333
|
+
# Extract account details
|
334
|
+
name = import_data["name"]
|
335
|
+
address = import_data["address"]
|
336
|
+
mnemonic = import_data["mnemonic"]
|
337
|
+
|
338
|
+
# Check if the account name already exists
|
339
|
+
config = get_all_config()
|
340
|
+
if name in config["substrate"].get("accounts", {}):
|
341
|
+
# Modify the name to avoid conflicts
|
342
|
+
original_name = name
|
343
|
+
name = f"{name}_imported_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
344
|
+
print(
|
345
|
+
f"Account name '{original_name}' already exists, using '{name}' instead"
|
346
|
+
)
|
347
|
+
|
348
|
+
# Save the account to configuration
|
349
|
+
if password:
|
350
|
+
# Encrypt the seed phrase with the provided password
|
351
|
+
result = set_seed_phrase(
|
352
|
+
mnemonic, encode=True, password=password, account_name=name
|
353
|
+
)
|
354
|
+
else:
|
355
|
+
# Store the seed phrase in plain text
|
356
|
+
result = set_seed_phrase(mnemonic, encode=False, account_name=name)
|
357
|
+
|
358
|
+
if not result:
|
359
|
+
raise RuntimeError("Failed to save imported account to configuration")
|
360
|
+
|
361
|
+
# Set this as the active account
|
362
|
+
set_active_account(name)
|
363
|
+
|
364
|
+
# Update the client's state to use this account
|
365
|
+
self._account_name = name
|
366
|
+
self._account_address = address
|
367
|
+
self._seed_phrase = mnemonic
|
368
|
+
self._keypair = Keypair.create_from_mnemonic(mnemonic)
|
369
|
+
self._read_only = False
|
370
|
+
|
371
|
+
# Return the imported account details
|
372
|
+
return {
|
373
|
+
"name": name,
|
374
|
+
"address": address,
|
375
|
+
"is_active": True,
|
376
|
+
"imported_at": datetime.datetime.now().isoformat(),
|
377
|
+
"original_name": import_data.get("name"),
|
378
|
+
}
|
379
|
+
except Exception as e:
|
380
|
+
raise ValueError(f"Failed to import account: {e}")
|
381
|
+
|
382
|
+
async def get_account_info(
|
383
|
+
self, account_name: Optional[str] = None, include_history: bool = False
|
384
|
+
) -> Dict[str, Any]:
|
385
|
+
"""
|
386
|
+
Get detailed information about an account.
|
387
|
+
|
388
|
+
Args:
|
389
|
+
account_name: Name of the account to get info for (uses active account if None)
|
390
|
+
include_history: Whether to include usage history in the results
|
391
|
+
|
392
|
+
Returns:
|
393
|
+
Dict[str, Any]: Detailed account information
|
394
|
+
"""
|
395
|
+
# Determine which account to get info for
|
396
|
+
name_to_use = account_name or self._account_name or get_active_account()
|
397
|
+
if not name_to_use:
|
398
|
+
raise ValueError("No account specified and no active account")
|
399
|
+
|
400
|
+
# Get the configuration to extract account data
|
401
|
+
config = get_all_config()
|
402
|
+
|
403
|
+
# Check if the account exists
|
404
|
+
if name_to_use not in config["substrate"].get("accounts", {}):
|
405
|
+
raise ValueError(f"Account '{name_to_use}' not found")
|
406
|
+
|
407
|
+
# Get account data from config
|
408
|
+
account_data = config["substrate"]["accounts"][name_to_use]
|
409
|
+
is_active = name_to_use == config["substrate"].get("active_account")
|
410
|
+
is_encoded = account_data.get("seed_phrase_encoded", False)
|
411
|
+
address = account_data.get("ss58_address")
|
412
|
+
|
413
|
+
# Create the account info object
|
414
|
+
account_info = {
|
415
|
+
"name": name_to_use,
|
416
|
+
"address": address,
|
417
|
+
"is_active": is_active,
|
418
|
+
"seed_phrase_encrypted": is_encoded,
|
419
|
+
}
|
420
|
+
|
421
|
+
# Query storage statistics for this account
|
422
|
+
try:
|
423
|
+
# Get files stored by this account - use await since this is an async method
|
424
|
+
files = await self.get_user_files_from_profile(address)
|
425
|
+
|
426
|
+
# Calculate storage statistics
|
427
|
+
total_files = len(files)
|
428
|
+
total_size_bytes = sum(file.get("file_size", 0) for file in files)
|
429
|
+
|
430
|
+
# Add storage stats to account info
|
431
|
+
account_info["storage_stats"] = {
|
432
|
+
"files": total_files,
|
433
|
+
"bytes_used": total_size_bytes,
|
434
|
+
"size_formatted": self._format_size(total_size_bytes)
|
435
|
+
if total_size_bytes
|
436
|
+
else "0 B",
|
437
|
+
}
|
438
|
+
|
439
|
+
# Include file list if requested
|
440
|
+
if include_history:
|
441
|
+
account_info["files"] = files
|
442
|
+
|
443
|
+
# Try to get account balance
|
444
|
+
try:
|
445
|
+
account_info["balance"] = await self.get_account_balance(address)
|
446
|
+
except Exception as e:
|
447
|
+
# Ignore balance errors, it's optional information
|
448
|
+
print(f"Could not fetch balance: {e}")
|
449
|
+
pass
|
450
|
+
|
451
|
+
# Try to get free credits
|
452
|
+
try:
|
453
|
+
account_info["free_credits"] = await self.get_free_credits(address)
|
454
|
+
except Exception as e:
|
455
|
+
# Ignore credits errors, it's optional information
|
456
|
+
print(f"Could not fetch free credits: {e}")
|
457
|
+
pass
|
458
|
+
except Exception as e:
|
459
|
+
# Add a note about the error but don't fail the whole operation
|
460
|
+
account_info["storage_stats"] = {
|
461
|
+
"error": f"Could not fetch storage statistics: {str(e)}"
|
462
|
+
}
|
463
|
+
|
464
|
+
return account_info
|
465
|
+
|
466
|
+
def _format_size(self, size_bytes: int) -> str:
|
467
|
+
"""Format file size in human-readable format"""
|
468
|
+
if size_bytes < 1024:
|
469
|
+
return f"{size_bytes} B"
|
470
|
+
elif size_bytes < 1024 * 1024:
|
471
|
+
return f"{size_bytes / 1024:.2f} KB"
|
472
|
+
elif size_bytes < 1024 * 1024 * 1024:
|
473
|
+
return f"{size_bytes / (1024 * 1024):.2f} MB"
|
474
|
+
else:
|
475
|
+
return f"{size_bytes / (1024 * 1024 * 1024):.2f} GB"
|
476
|
+
|
184
477
|
def set_seed_phrase(self, seed_phrase: str) -> None:
|
185
478
|
"""
|
186
479
|
Set or update the seed phrase used for signing transactions.
|
@@ -454,7 +747,7 @@ class SubstrateClient:
|
|
454
747
|
"""
|
455
748
|
raise NotImplementedError("Substrate functionality is not implemented yet.")
|
456
749
|
|
457
|
-
def get_account_balance(
|
750
|
+
async def get_account_balance(
|
458
751
|
self, account_address: Optional[str] = None
|
459
752
|
) -> Dict[str, float]:
|
460
753
|
"""
|
@@ -466,9 +759,174 @@ class SubstrateClient:
|
|
466
759
|
Returns:
|
467
760
|
Dict[str, float]: Account balances (free, reserved, total)
|
468
761
|
"""
|
469
|
-
|
762
|
+
try:
|
763
|
+
# Initialize Substrate connection if not already connected
|
764
|
+
if not hasattr(self, "_substrate") or self._substrate is None:
|
765
|
+
print("Initializing Substrate connection...")
|
766
|
+
self._substrate = SubstrateInterface(
|
767
|
+
url=self.url,
|
768
|
+
ss58_format=42, # Substrate default
|
769
|
+
type_registry_preset="substrate-node-template",
|
770
|
+
)
|
771
|
+
print(f"Connected to Substrate node at {self.url}")
|
772
|
+
|
773
|
+
# Use provided account address or default to keypair/configured address
|
774
|
+
if not account_address:
|
775
|
+
if self._account_address:
|
776
|
+
account_address = self._account_address
|
777
|
+
print(f"Using account address: {account_address}")
|
778
|
+
else:
|
779
|
+
# Try to get the address from the keypair (requires seed phrase)
|
780
|
+
if not self._ensure_keypair():
|
781
|
+
raise ValueError("No account address available")
|
782
|
+
account_address = self._keypair.ss58_address
|
783
|
+
print(f"Using keypair address: {account_address}")
|
784
|
+
|
785
|
+
# Query the blockchain for account balance
|
786
|
+
print(f"Querying balance for account: {account_address}")
|
787
|
+
result = self._substrate.query(
|
788
|
+
module="System",
|
789
|
+
storage_function="Account",
|
790
|
+
params=[account_address],
|
791
|
+
)
|
792
|
+
|
793
|
+
# If account exists, extract the balance information
|
794
|
+
if result.value:
|
795
|
+
data = result.value
|
796
|
+
print(data)
|
797
|
+
# Extract balance components
|
798
|
+
free_balance = data.get("data", {}).get("free", 0)
|
799
|
+
reserved_balance = data.get("data", {}).get("reserved", 0)
|
800
|
+
frozen_balance = data.get("data", {}).get("frozen", 0)
|
801
|
+
|
802
|
+
# Convert from blockchain units to float (divide by 10^18)
|
803
|
+
divisor = 1_000_000_000_000_000_000 # 18 zeros for decimals
|
804
|
+
|
805
|
+
free = float(free_balance) / divisor
|
806
|
+
reserved = float(reserved_balance) / divisor
|
807
|
+
frozen = float(frozen_balance) / divisor
|
808
|
+
|
809
|
+
# Calculate total (free + reserved - frozen)
|
810
|
+
total = free + reserved - frozen
|
811
|
+
|
812
|
+
return {
|
813
|
+
"free": free,
|
814
|
+
"reserved": reserved,
|
815
|
+
"frozen": frozen,
|
816
|
+
"total": total,
|
817
|
+
"raw": {
|
818
|
+
"free": free_balance,
|
819
|
+
"reserved": reserved_balance,
|
820
|
+
"frozen": frozen_balance,
|
821
|
+
},
|
822
|
+
}
|
823
|
+
else:
|
824
|
+
print(f"No account data found for: {account_address}")
|
825
|
+
return {
|
826
|
+
"free": 0.0,
|
827
|
+
"reserved": 0.0,
|
828
|
+
"frozen": 0.0,
|
829
|
+
"total": 0.0,
|
830
|
+
"raw": {"free": 0, "reserved": 0, "frozen": 0},
|
831
|
+
}
|
832
|
+
|
833
|
+
except Exception as e:
|
834
|
+
error_msg = f"Error querying account balance: {str(e)}"
|
835
|
+
print(error_msg)
|
836
|
+
raise ValueError(error_msg)
|
837
|
+
|
838
|
+
async def watch_account_balance(
|
839
|
+
self, account_address: Optional[str] = None, interval: int = 5
|
840
|
+
) -> None:
|
841
|
+
"""
|
842
|
+
Watch account balance in real-time, updating at specified intervals.
|
843
|
+
|
844
|
+
The function runs until interrupted with Ctrl+C.
|
845
|
+
|
846
|
+
Args:
|
847
|
+
account_address: Substrate account address (uses keypair address if not specified)
|
848
|
+
interval: Polling interval in seconds (default: 5)
|
849
|
+
"""
|
850
|
+
try:
|
851
|
+
# Use provided account address or default to keypair/configured address
|
852
|
+
if not account_address:
|
853
|
+
if self._account_address:
|
854
|
+
account_address = self._account_address
|
855
|
+
else:
|
856
|
+
# Try to get the address from the keypair (requires seed phrase)
|
857
|
+
if not self._ensure_keypair():
|
858
|
+
raise ValueError("No account address available")
|
859
|
+
account_address = self._keypair.ss58_address
|
860
|
+
|
861
|
+
print(f"Watching balance for account: {account_address}")
|
862
|
+
print(f"Updates every {interval} seconds. Press Ctrl+C to stop.")
|
863
|
+
print("-" * 80)
|
864
|
+
|
865
|
+
# Keep track of previous balance to show changes
|
866
|
+
previous_balance = None
|
867
|
+
|
868
|
+
try:
|
869
|
+
while True:
|
870
|
+
# Get current time for display
|
871
|
+
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
872
|
+
|
873
|
+
# Get current balance
|
874
|
+
try:
|
875
|
+
balance = await self.get_account_balance(account_address)
|
876
|
+
|
877
|
+
# Clear screen (ANSI escape sequence)
|
878
|
+
print("\033c", end="")
|
879
|
+
|
880
|
+
# Display header
|
881
|
+
print(f"Account Balance Watch for: {account_address}")
|
882
|
+
print(f"Last update: {current_time}")
|
883
|
+
print("-" * 80)
|
884
|
+
|
885
|
+
# Display current balance
|
886
|
+
print(f"Free: {balance['free']:.6f}")
|
887
|
+
print(f"Reserved: {balance['reserved']:.6f}")
|
888
|
+
print(f"Frozen: {balance['frozen']:.6f}")
|
889
|
+
print(f"Total: {balance['total']:.6f}")
|
890
|
+
|
891
|
+
# Show changes since last update if available
|
892
|
+
if previous_balance:
|
893
|
+
print("\nChanges since last update:")
|
894
|
+
free_change = balance["free"] - previous_balance["free"]
|
895
|
+
reserved_change = (
|
896
|
+
balance["reserved"] - previous_balance["reserved"]
|
897
|
+
)
|
898
|
+
total_change = balance["total"] - previous_balance["total"]
|
899
|
+
|
900
|
+
# Format changes with + or - sign
|
901
|
+
print(f"Free: {free_change:+.6f}")
|
902
|
+
print(f"Reserved: {reserved_change:+.6f}")
|
903
|
+
print(f"Total: {total_change:+.6f}")
|
904
|
+
|
905
|
+
# Store current balance for next comparison
|
906
|
+
previous_balance = balance
|
907
|
+
|
908
|
+
# Show instructions at the bottom
|
909
|
+
print(
|
910
|
+
"\nUpdating every",
|
911
|
+
interval,
|
912
|
+
"seconds. Press Ctrl+C to stop.",
|
913
|
+
)
|
914
|
+
|
915
|
+
except Exception as e:
|
916
|
+
# Show error but continue watching
|
917
|
+
print(f"Error: {e}")
|
918
|
+
print(f"Will try again in {interval} seconds...")
|
919
|
+
|
920
|
+
# Wait for next update
|
921
|
+
time.sleep(interval)
|
922
|
+
|
923
|
+
except KeyboardInterrupt:
|
924
|
+
print("\nBalance watch stopped.")
|
925
|
+
|
926
|
+
except Exception as e:
|
927
|
+
print(f"Error in watch_account_balance: {e}")
|
470
928
|
|
471
|
-
def get_free_credits(self, account_address: Optional[str] = None) -> float:
|
929
|
+
async def get_free_credits(self, account_address: Optional[str] = None) -> float:
|
472
930
|
"""
|
473
931
|
Get the free credits available for an account in the marketplace.
|
474
932
|
|
hippius-0.2.0.dist-info/RECORD
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
hippius_sdk/__init__.py,sha256=KzTJTpSpFP1L1euDEkGnXh2pt1ReHGqyFn3atHA1Tfs,1391
|
2
|
-
hippius_sdk/cli.py,sha256=L6_ZypipD9rqUYNqtmq4mB46eAcC4k170NNbnfx41ks,90051
|
3
|
-
hippius_sdk/client.py,sha256=mMKX_m2ZwfbGVAU3zasHZQF0ddToqypkxGKTylruB3Y,14901
|
4
|
-
hippius_sdk/config.py,sha256=WqocYwx-UomLeZ-iFUNDjg9vRcagOBA1Th68XELbuTs,22950
|
5
|
-
hippius_sdk/ipfs.py,sha256=fFcDMR1-XUcSx1stwV_dD4Rr5fdlTGuSl1Zxag3eSSw,49437
|
6
|
-
hippius_sdk/ipfs_core.py,sha256=w6ljgFdUzL-ffKxr4x_W-aYZTNpgyJg5HOFDrLKPILA,6690
|
7
|
-
hippius_sdk/substrate.py,sha256=8Jq6IAGJR22WKQFFmafNOE85Q467JoVyQDK8udVU2d8,42021
|
8
|
-
hippius_sdk/utils.py,sha256=-N0w0RfXhwxJgSkSroxqFMw-0zJQXvcmxM0OS5UtWEY,4145
|
9
|
-
hippius-0.2.0.dist-info/METADATA,sha256=oHxH4kpLHWeDKInHLtKxA1HJWHOEJJWcPc5fMvl_orU,27993
|
10
|
-
hippius-0.2.0.dist-info/WHEEL,sha256=Zb28QaM1gQi8f4VCBhsUklF61CTlNYfs9YAZn-TOGFk,88
|
11
|
-
hippius-0.2.0.dist-info/entry_points.txt,sha256=b1lo60zRXmv1ud-c5BC-cJcAfGE5FD4qM_nia6XeQtM,98
|
12
|
-
hippius-0.2.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|