py-near 1.1.53__py3-none-any.whl → 1.1.54__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.
py_near/account.py CHANGED
@@ -4,6 +4,8 @@ import json
4
4
  import sys
5
5
  from typing import List, Union, Dict, Optional
6
6
 
7
+ from nacl.signing import VerifyKey
8
+
7
9
  if sys.version_info.major == 3 and sys.version_info.minor >= 10:
8
10
  from collections.abc import MutableSet, MutableMapping
9
11
 
@@ -86,8 +88,8 @@ class Account(object):
86
88
  logger.error(f"Can't decode private key {pk[:10]}")
87
89
  continue
88
90
  private_key = signing.SigningKey(pk[:32], encoder=encoding.RawEncoder)
89
- public_key = private_key.verify_key
90
- self._signer_by_pk[public_key] = pk
91
+ public_key_b58 = base58.b58encode(private_key.verify_key.encode()).decode("utf-8")
92
+ self._signer_by_pk[public_key_b58] = pk
91
93
  self._free_signers.put_nowait(pk)
92
94
  self._signers.append(pk)
93
95
 
@@ -206,8 +208,6 @@ class Account(object):
206
208
 
207
209
  private_key = signing.SigningKey(pk[:32], encoder=encoding.RawEncoder)
208
210
  public_key = private_key.verify_key
209
- public_key.to_curve25519_public_key()
210
-
211
211
  resp = await self._provider.get_access_key(
212
212
  self.account_id, base58.b58encode(public_key.encode()).decode("utf8")
213
213
  )
@@ -449,14 +449,14 @@ class Account(object):
449
449
  await self._update_last_block_hash()
450
450
 
451
451
  private_key = signing.SigningKey(pk[:32], encoder=encoding.RawEncoder)
452
- verifying_key = private_key.verify_key
452
+ verifying_key: VerifyKey = private_key.verify_key
453
453
  return DelegateActionModel(
454
454
  sender_id=self.account_id,
455
455
  receiver_id=receiver_id,
456
456
  actions=actions,
457
457
  nonce=access_key.nonce + 1,
458
458
  max_block_height=self._latest_block_height + 1000,
459
- public_key=base58.b58encode(verifying_key.to_bytes()).decode("utf-8"),
459
+ public_key=base58.b58encode(verifying_key.encode()).decode("utf-8"),
460
460
  )
461
461
 
462
462
  def sign_delegate_transaction(
File without changes
File without changes
@@ -0,0 +1,97 @@
1
+ import datetime
2
+ from hashlib import sha256
3
+ from typing import Optional
4
+
5
+ import base58
6
+ from nacl import signing
7
+ from pydantic import BaseModel
8
+
9
+ from py_near.account import Account
10
+ from py_near.mpc.auth.base import AuthContract
11
+
12
+
13
+ class Wallet2FA(BaseModel):
14
+ wallet_id: str
15
+ public_key: str
16
+ delay_to_remove: int
17
+ cancellation_at: int
18
+ conditions: Optional[str] = None
19
+
20
+ @classmethod
21
+ def build(cls, data: dict, wallet_id):
22
+ return cls(
23
+ wallet_id=wallet_id,
24
+ public_key=data["public_key"],
25
+ cancellation_at=data["cancellation_at"],
26
+ delay_to_remove=data["delay_to_remove"],
27
+ conditions=data.get("conditions"),
28
+ )
29
+
30
+
31
+ class AuthContract2FA(AuthContract):
32
+ root_pk: signing.SigningKey
33
+ contract_id: str = "2fa.auth.hot.tg"
34
+
35
+ def __init__(self, root_pk: signing.SigningKey, near_account: Account = None):
36
+ self.root_pk = root_pk
37
+ self.near_account = near_account
38
+ super().__init__()
39
+
40
+ def generate_user_payload(self, msg_hash: bytes):
41
+ auth_signature = base58.b58encode(self.root_pk.sign(msg_hash).signature).decode(
42
+ "utf-8"
43
+ )
44
+ return auth_signature
45
+
46
+ async def get_2fa_for_wallet(self, wallet_id: str) -> Optional[Wallet2FA]:
47
+ if not self.near_account:
48
+ raise ValueError("Near account is required")
49
+ res = (
50
+ await self.near_account.view_function(
51
+ self.contract_id,
52
+ "get_wallet_2fa",
53
+ {
54
+ "wallet_id": wallet_id,
55
+ },
56
+ )
57
+ ).result
58
+ if res:
59
+ return Wallet2FA.build(res, wallet_id)
60
+
61
+ async def cancel_2fa(
62
+ self, user_pk: signing.SigningKey, wallet_id: str, rule_id: int = 0
63
+ ):
64
+ if not self.near_account:
65
+ raise ValueError("Near account is required")
66
+ two_fa = await self.get_2fa_for_wallet(wallet_id)
67
+ ts = int(datetime.datetime.utcnow().timestamp() * 10**9)
68
+
69
+ if two_fa.cancellation_at == 0:
70
+ msg_hash = sha256(f"CANCEL_2FA:{wallet_id}:{ts}".encode("utf-8")).digest()
71
+ auth_signature = base58.b58encode(user_pk.sign(msg_hash).signature).decode(
72
+ "utf-8"
73
+ )
74
+ return await self.near_account.function_call(
75
+ self.contract_id,
76
+ "cancel_2fa",
77
+ {
78
+ "wallet_id": wallet_id,
79
+ "user_signature": auth_signature,
80
+ "timestamp": ts,
81
+ },
82
+ included=True,
83
+ )
84
+ else:
85
+ if two_fa.cancellation_at < ts:
86
+ raise ValueError(
87
+ f"2FA already cancelled at {datetime.datetime.fromtimestamp(two_fa.cancellation_at / 10**9)}"
88
+ )
89
+ return await self.near_account.function_call(
90
+ self.contract_id,
91
+ "complete_cancel_2fa",
92
+ {
93
+ "wallet_id": wallet_id,
94
+ "rule_id": rule_id,
95
+ },
96
+ included=True,
97
+ )
@@ -0,0 +1,23 @@
1
+ from py_near.account import Account
2
+ from py_near.mpc.models import WalletAccessModel
3
+
4
+
5
+ class AuthContract:
6
+ contract_id: str
7
+
8
+ def __init__(self, near_account: Account = None):
9
+ self.near_account = near_account
10
+
11
+ async def grant_access(
12
+ self,
13
+ wallet_id: str,
14
+ access: WalletAccessModel,
15
+ near_account,
16
+ wallet_auth_method=0,
17
+ ):
18
+ raise NotImplementedError("Grant access is not implemented")
19
+
20
+ async def revoke_assess(
21
+ self, wallet_id: str, near_account: Account, access_id: int
22
+ ):
23
+ raise NotImplementedError("Revoke access is not implemented")
File without changes
@@ -0,0 +1,29 @@
1
+ import json
2
+
3
+ import base58
4
+ from nacl import signing
5
+
6
+ from py_near.mpc.auth.base import AuthContract
7
+
8
+
9
+ class DefaultAuthContract(AuthContract):
10
+ root_pk: signing.SigningKey
11
+ contract_id: str = "default.auth.hot.tg"
12
+
13
+ def __init__(self, root_pk: signing.SigningKey):
14
+ self.root_pk = root_pk
15
+ super().__init__()
16
+
17
+ def generate_user_payload(self, msg_hash: bytes):
18
+ auth_signature = base58.b58encode(self.root_pk.sign(msg_hash).signature).decode(
19
+ "utf-8"
20
+ )
21
+ return json.dumps(
22
+ dict(
23
+ signature=auth_signature,
24
+ public_key=base58.b58encode(
25
+ bytes(self.root_pk.verify_key.encode())
26
+ ).decode(),
27
+ )
28
+ )
29
+
@@ -0,0 +1,83 @@
1
+ import json
2
+ from hashlib import sha256
3
+ from typing import List
4
+
5
+ import base58
6
+ from nacl import signing
7
+
8
+ from py_near.account import Account
9
+ from py_near.mpc.auth.base import AuthContract
10
+ from py_near.mpc.models import WalletAccessModel
11
+
12
+
13
+ class KeysAuthContract(AuthContract):
14
+ auth_keys: List[signing.SigningKey]
15
+ auth_method: int
16
+ contract_id: str = "keys.auth.hot.tg"
17
+
18
+ def __init__(
19
+ self,
20
+ auth_keys: List[signing.SigningKey],
21
+ auth_method=0,
22
+ near_account: Account = None,
23
+ ):
24
+ self.auth_keys = auth_keys
25
+ self.auth_method = auth_method
26
+ super().__init__(near_account)
27
+
28
+ def generate_user_payload(self, msg_hash: bytes):
29
+ signatures = []
30
+ for key in self.auth_keys:
31
+ signatures.append(
32
+ base58.b58encode(key.sign(msg_hash).signature).decode("utf-8")
33
+ )
34
+ return json.dumps(
35
+ dict(
36
+ signatures=signatures,
37
+ auth_method=0,
38
+ )
39
+ )
40
+
41
+ async def grant_access(
42
+ self,
43
+ wallet_id: str,
44
+ access: WalletAccessModel,
45
+ near_account,
46
+ wallet_auth_method=0,
47
+ ):
48
+ access = access.model_dump()
49
+ proof_hash = sha256(
50
+ f"{json.dumps(access).replace(' ', '')}.{wallet_id}".encode("utf-8")
51
+ ).digest()
52
+ signatures = []
53
+ for pk in self.auth_keys:
54
+ signature = base58.b58encode(pk.sign(proof_hash).signature).decode("utf-8")
55
+ signatures.append(signature)
56
+
57
+ res = await near_account.function_call(
58
+ self.contract_id,
59
+ "add_wallet_auth_account_id",
60
+ {
61
+ "wallet_id": wallet_id,
62
+ "access": access,
63
+ "signatures": signatures,
64
+ "wallet_auth_method": wallet_auth_method,
65
+ "auth_method": self.auth_method,
66
+ },
67
+ included=True,
68
+ )
69
+ return res
70
+
71
+ async def get_rules(self, wallet_id: str):
72
+ if not self.near_account:
73
+ raise ValueError("Near account is required")
74
+ res = (
75
+ await self.near_account.view_function(
76
+ self.contract_id,
77
+ "get_wallet",
78
+ {
79
+ "wallet_id": wallet_id,
80
+ },
81
+ )
82
+ ).result
83
+ return res
py_near/mpc/models.py ADDED
@@ -0,0 +1,32 @@
1
+ from enum import Enum
2
+ from typing import Optional, List
3
+
4
+ from pydantic import BaseModel
5
+
6
+
7
+ class CurveType(int, Enum):
8
+ SECP256K1 = 0
9
+ ED25519 = 1
10
+
11
+
12
+ _WALLET_REGISTER = "mpc.hot.tg"
13
+
14
+
15
+ class WalletAccessModel(BaseModel):
16
+ account_id: str
17
+ metadata: Optional[str] = None
18
+ chain_id: int
19
+ msg: Optional[str] = None
20
+
21
+
22
+ class WalletModel(BaseModel):
23
+ access_list: List[WalletAccessModel]
24
+ key_gen: int = 0
25
+
26
+ @classmethod
27
+ def build(cls, data: dict):
28
+ return cls(
29
+ access_list=[WalletAccessModel(**x) for x in data["access_list"]],
30
+ block_height=data["block_height"],
31
+ key_gen=data["key_gen"],
32
+ )
py_near/mpc/wallet.py ADDED
@@ -0,0 +1,261 @@
1
+ import json
2
+ from hashlib import sha256
3
+ from typing import List, Optional
4
+
5
+ import base58
6
+ import httpx
7
+ from eth_keys.datatypes import Signature
8
+ from eth_keys.exceptions import BadSignature
9
+ from eth_utils import keccak
10
+ from loguru import logger
11
+ from nacl import encoding, signing
12
+
13
+ from py_near.account import Account
14
+ from py_near.mpc.auth.auth_2fa import AuthContract2FA
15
+ from py_near.mpc.auth.base import AuthContract
16
+ from py_near.mpc.auth.default_auth import DefaultAuthContract
17
+ from py_near.mpc.auth.keys_auth import KeysAuthContract
18
+ from py_near.mpc.models import WalletAccessModel, WalletModel, CurveType
19
+
20
+ _WALLET_REGISTER = "mpc.hot.tg"
21
+
22
+ AUTH_CLASS = {
23
+ "default.auth.hot.tg": DefaultAuthContract,
24
+ "keys.auth.hot.tg": KeysAuthContract,
25
+ "2fa.auth.hot.tg": AuthContract2FA,
26
+ }
27
+
28
+
29
+ class MPCWallet:
30
+ derive: bytes
31
+ near_account: Account
32
+ hot_rpc: str
33
+
34
+ def __init__(
35
+ self,
36
+ near_account: Account,
37
+ default_root_pk: Optional[bytes] = None,
38
+ hot_rpc: str = "https://rpc1.hotdao.ai",
39
+ derive: Optional[bytes] = None,
40
+ ):
41
+ """
42
+ :param near_account: Near account
43
+ :param derive: Derived key for wallet
44
+ :param hot_rpc: Hot RPC url
45
+ :param default_root_pk: Default auth key for HOT Protocol, if provided, derive = sha256(default_root_pk.public_key).digest()
46
+ """
47
+ self.near_account = near_account
48
+ if len(default_root_pk) != 32:
49
+ raise ValueError("Default root pk should be 32 bytes")
50
+ if default_root_pk:
51
+ self.default_root_pk = default_root_pk
52
+ private_key_obj = self.derive_private_key(0)
53
+ derive = sha256(private_key_obj.verify_key.encode()).digest()
54
+ elif derive is None:
55
+ logger.warning(
56
+ "Its recomended to use default_root_pk to generate derives, v0 wallets support will be deprecated"
57
+ )
58
+ raise ValueError("Derive is required")
59
+ self.derive = derive
60
+ self.hot_rpc = hot_rpc
61
+ self._client = httpx.AsyncClient()
62
+
63
+ def derive_private_key(self, gen=0):
64
+ private_key = self.default_root_pk
65
+ for _ in range(gen):
66
+ private_key = sha256(private_key).digest()
67
+ return signing.SigningKey(
68
+ private_key,
69
+ encoder=encoding.RawEncoder,
70
+ )
71
+
72
+ @property
73
+ def wallet_id(self):
74
+ return base58.b58encode(sha256(self.derive).digest()).decode("utf-8")
75
+
76
+ async def get_wallet(self):
77
+ wallet = await self.near_account.view_function(
78
+ _WALLET_REGISTER, "get_wallet", args={"wallet_id": self.wallet_id}
79
+ )
80
+ if wallet.result:
81
+ return WalletModel.build(wallet.result)
82
+
83
+ async def create_wallet_with_keys_auth(self, public_key: bytes, key_gen=1):
84
+ """
85
+ Create wallet with keys.auth.hot.tg auth method.
86
+ :param public_key: Public key for auth future signs on keys.auth.hot.tg
87
+ """
88
+ wallet = await self.get_wallet()
89
+ if wallet and wallet.access_list[0].account_id != "default.auth.hot.tg":
90
+ raise ValueError(
91
+ "MPCWallet already exists with different auth method, please use another wallet_id"
92
+ )
93
+ auth_to_add_msg = json.dumps(
94
+ dict(public_keys=[base58.b58encode(public_key).decode()], rules=[])
95
+ ).replace(" ", "")
96
+ return await self.create_wallet(
97
+ "keys.auth.hot.tg", None, auth_to_add_msg, key_gen
98
+ )
99
+
100
+ async def create_wallet(
101
+ self, auth_account_id: str, metadata: str = "", auth_to_add_msg="", key_gen=1
102
+ ):
103
+ """
104
+ Create wallet with keys.auth.hot.tg auth method.
105
+ :param public_key: Public key for auth future signs on keys.auth.hot.tg
106
+ """
107
+ wallet = await self.get_wallet()
108
+ if wallet and wallet.access_list[0].account_id != "default.auth.hot.tg":
109
+ raise ValueError(
110
+ "MPCWallet already exists with different auth method, please use another wallet_id"
111
+ )
112
+ root_pk = self.derive_private_key(0)
113
+ proof_hash = sha256(
114
+ f"CREATE_WALLET:{self.wallet_id}:{auth_account_id}:{metadata}:{auth_to_add_msg}".encode(
115
+ "utf-8"
116
+ )
117
+ ).digest()
118
+ signature = base58.b58encode(root_pk.sign(proof_hash).signature).decode("utf-8")
119
+
120
+ s = await self._client.post(
121
+ f"{self.hot_rpc}/create_wallet",
122
+ json=dict(
123
+ wallet_id=self.wallet_id,
124
+ key_gen=key_gen,
125
+ signature=signature,
126
+ wallet_derive_public_key=base58.b58encode(
127
+ root_pk.verify_key.encode()
128
+ ).decode(),
129
+ auth={
130
+ "auth_account_id": auth_account_id,
131
+ "msg": auth_to_add_msg,
132
+ "metadata": metadata or None,
133
+ },
134
+ ),
135
+ timeout=30,
136
+ )
137
+ return s.json()
138
+
139
+ async def get_ecdsa_public_key(self) -> bytes:
140
+ resp = (
141
+ await self._client.post(
142
+ f"{self.hot_rpc}/public_key",
143
+ json=dict(wallet_derive=base58.b58encode(self.derive).decode()),
144
+ timeout=10,
145
+ follow_redirects=True,
146
+ )
147
+ ).json()
148
+ return bytes.fromhex(resp["ecdsa"])
149
+
150
+ @property
151
+ def public_key(self):
152
+ # TODO Calculate with Rust Code
153
+ raise NotImplementedError("Public key calculation is not implemented yet")
154
+
155
+ async def sign_message(
156
+ self,
157
+ msg_hash: bytes,
158
+ message_body: Optional[bytes] = None,
159
+ curve_type: CurveType = CurveType.SECP256K1,
160
+ auth_methods: List[AuthContract] = None,
161
+ ):
162
+ if not self.default_root_pk:
163
+ raise ValueError("Default auth key is required")
164
+ wallet = await self.get_wallet()
165
+ user_payloads = []
166
+ if len(auth_methods) != len(wallet.access_list):
167
+ raise ValueError("Auth methods count should be equal to wallet access list")
168
+
169
+ for auth_contract, auth_method in zip(auth_methods, wallet.access_list):
170
+ user_payloads.append(auth_contract.generate_user_payload(msg_hash))
171
+
172
+ proof = {
173
+ "auth_id": 0,
174
+ "curve_type": curve_type,
175
+ "user_payloads": user_payloads,
176
+ "message_body": message_body.hex() if message_body else "",
177
+ }
178
+
179
+ resp = await self._client.post(
180
+ f"{self.hot_rpc}/sign_raw",
181
+ json=dict(
182
+ uid=self.derive.hex(),
183
+ message=msg_hash.hex(),
184
+ proof=proof,
185
+ key_type=curve_type,
186
+ ),
187
+ timeout=10,
188
+ follow_redirects=True,
189
+ )
190
+ resp = resp.json()
191
+ if "Ecdsa" not in resp:
192
+ raise ValueError(f"Invalid response from server: {resp}")
193
+ resp = resp["Ecdsa"]
194
+ r = int(resp["big_r"][2:], 16)
195
+ s = int(resp["signature"], 16)
196
+
197
+ pk = await self.get_ecdsa_public_key()
198
+ for v in (0, 1):
199
+ sig = Signature(vrs=(v, r, s))
200
+ recovered = sig.recover_public_key_from_msg_hash(msg_hash)
201
+ if recovered.to_compressed_bytes() == pk:
202
+ return (
203
+ r.to_bytes(32, "big") + s.to_bytes(32, "big") + bytes([v])
204
+ ).hex()
205
+
206
+ raise BadSignature("Cannot recover public key from signature")
207
+
208
+ async def add_new_access_rule(
209
+ self, access: WalletAccessModel, auth_contracts: List[AuthContract]
210
+ ):
211
+ wallet = await self.get_wallet()
212
+ access_json = access.model_dump_json()
213
+ message_body = f"ADD_AUTH_METHOD:{access_json}:{self.wallet_id}".encode("utf-8")
214
+ msg_hash = sha256(message_body).digest()
215
+ user_payloads = []
216
+ for auth_contract, auth_method in zip(auth_contracts, wallet.access_list):
217
+ auth_class = AUTH_CLASS[auth_method.account_id]
218
+ if not isinstance(auth_contract, auth_class):
219
+ raise ValueError(
220
+ f"Auth method {auth_method.account_id} is not supported for this auth class"
221
+ )
222
+ user_payloads.append(auth_contract.generate_user_payload(msg_hash))
223
+ return await self.near_account.function_call(
224
+ _WALLET_REGISTER,
225
+ "add_access_rule",
226
+ {
227
+ "wallet_id": self.wallet_id,
228
+ "rule": access.model_dump(),
229
+ "user_payloads": user_payloads,
230
+ },
231
+ included=True,
232
+ )
233
+
234
+ async def remove_access_rule(
235
+ self, access_id: int, auth_contracts: List[AuthContract]
236
+ ):
237
+ wallet = await self.get_wallet()
238
+ message_body = f"REMOVE_AUTH_METHOD:{access_id}:{self.wallet_id}".encode(
239
+ "utf-8"
240
+ )
241
+ msg_hash = sha256(message_body).digest()
242
+ user_payloads = []
243
+ if len(auth_contracts) != len(wallet.access_list):
244
+ raise ValueError("Auth methods count should be equal to wallet access list")
245
+ for auth_contract, auth_method in zip(auth_contracts, wallet.access_list):
246
+ auth_class = AUTH_CLASS[auth_method.account_id]
247
+ if not isinstance(auth_contract, auth_class):
248
+ raise ValueError(
249
+ f"Auth method {auth_method.account_id} is not supported for this auth class"
250
+ )
251
+ user_payloads.append(auth_contract.generate_user_payload(msg_hash))
252
+ return await self.near_account.function_call(
253
+ _WALLET_REGISTER,
254
+ "remove_access_rule",
255
+ {
256
+ "wallet_id": self.wallet_id,
257
+ "rule_id": access_id,
258
+ "user_payloads": user_payloads,
259
+ },
260
+ included=True,
261
+ )
@@ -1,23 +1,45 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: py-near
3
- Version: 1.1.53
3
+ Version: 1.1.54
4
4
  Summary: Pretty simple and fully asynchronous framework for working with NEAR blockchain
5
- Author: pvolnov
6
- Author-email: petr@herewallet.app
7
- Requires-Python: >=3.8,<3.12
5
+ Author-email: pvolnov <petr@herewallet.app>
6
+ License: Permission is hereby granted, free of charge, to any
7
+ person obtaining a copy of this software and associated
8
+ documentation files (the "Software"), to deal in the
9
+ Software without restriction, including without
10
+ limitation the rights to use, copy, modify, merge,
11
+ publish, distribute, sublicense, and/or sell copies of
12
+ the Software, and to permit persons to whom the Software
13
+ is furnished to do so, subject to the following
14
+ conditions:
15
+
16
+ The above copyright notice and this permission notice
17
+ shall be included in all copies or substantial portions
18
+ of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
21
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
22
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
23
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
24
+ SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
25
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
27
+ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28
+ DEALINGS IN THE SOFTWARE.
29
+
30
+ Project-URL: homepage, https://github.com/pvolnov/py-near
31
+ Project-URL: documentation, https://py-near.readthedocs.io/en/latest
32
+ Project-URL: repository, https://github.com/pvolnov/py-near
33
+ Project-URL: changelog, https://github.com/pvolnov/py-near/blob/main/CHANGELOG.md
34
+ Project-URL: funding, https://opencollective.com/py-near
35
+ Project-URL: twitter, https://twitter.com/p_volnov
36
+ Keywords: python,near,async
8
37
  Classifier: Programming Language :: Python :: 3
9
- Classifier: Programming Language :: Python :: 3.8
10
- Classifier: Programming Language :: Python :: 3.9
11
- Classifier: Programming Language :: Python :: 3.10
12
- Classifier: Programming Language :: Python :: 3.11
13
- Requires-Dist: base58 (>=2.1.1,<3.0.0)
14
- Requires-Dist: frozenlist (>=1.4.1,<2.0.0)
15
- Requires-Dist: httpx (>=0.26.0)
16
- Requires-Dist: loguru (>=0.7.2,<0.8.0)
17
- Requires-Dist: py-near-primitives (==0.2.4)
18
- Requires-Dist: pydantic (>=2.6.2,<3.0.0)
19
- Requires-Dist: pynacl (>=1.5.0,<2.0.0)
38
+ Classifier: License :: OSI Approved :: MIT License
39
+ Classifier: Operating System :: OS Independent
20
40
  Description-Content-Type: text/markdown
41
+ License-File: LICENSE
42
+ Dynamic: license-file
21
43
 
22
44
  # py-near
23
45
 
@@ -141,4 +163,3 @@ for t in task:
141
163
 
142
164
  This project exists thanks to all the people who contribute. [[Code of conduct](CODE_OF_CONDUCT.md)].
143
165
  <a href="https://github.com/pvolnov/py-near/graphs/contributors"><img src="https://opencollective.com/py-near/contributors.svg?width=890&button=false" /></a>
144
-
@@ -1,13 +1,17 @@
1
1
  py_near/__init__.py,sha256=t5fAxjaU8dN8xpQR2vz0ZGhfTkdVy2RCbkhJhZFglk4,50
2
- py_near/account.py,sha256=ICLWOzRE7sfEU_ilLGPd0kyzhsMDmS1CWURFFCbk3Io,18200
2
+ py_near/account.py,sha256=J9JSOXrglDr-91Lrr1cc2CgH845vGurLFdy91ty-cRA,18249
3
3
  py_near/constants.py,sha256=inaWIuwmF1EB5JSB0ynnZY5rKY_QsxhF9KuCOhPsM6k,164
4
+ py_near/models.py,sha256=85ynmLVJnzWvFKq-Z8iIO0waU-2iP-CSkaOGX4mYoL0,11590
5
+ py_near/providers.py,sha256=X_GfddFzmafBatqFJH41Dp_lYvTNcz1Q3Ko2tEnN0VU,16193
6
+ py_near/transactions.py,sha256=QwEP3qOGxb8OB2vZA_rU20jvkPA0FmJowUKDxbuzOuI,3251
7
+ py_near/utils.py,sha256=FirRH93ydH1cwjn0-sNrZeIn3BRD6QHedrP2VkAdJ6g,126
4
8
  py_near/dapps/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
5
9
  py_near/dapps/core.py,sha256=LtN9aW2gw2mvEdhzQcQJIidtjv-XL1xjb0LK8DzqtqE,231
10
+ py_near/dapps/fts.py,sha256=MFe_wC96K_RfpFc7soeZr-ejuMWPl7SJqaX2mTbZHy0,959
6
11
  py_near/dapps/ft/__init__.py,sha256=hx8qh4yEs37Ul_JhDWn8LHd-9lzsaATivxKOJPaUqTE,29
7
12
  py_near/dapps/ft/async_client.py,sha256=Oir5EQOGueq0lsNn0ybe_1uvMnmV5K1m_mHFuFWIUu4,6717
8
13
  py_near/dapps/ft/exceptions.py,sha256=UjXLFsDQX0vDGS9CGO7HE9XpLD0vovFNUzCb11WKAtc,92
9
14
  py_near/dapps/ft/models.py,sha256=_URS8nXZvfuxjU7Ov2E-nteBak5qKtPKN3bLOR7sC9k,186
10
- py_near/dapps/fts.py,sha256=MFe_wC96K_RfpFc7soeZr-ejuMWPl7SJqaX2mTbZHy0,959
11
15
  py_near/dapps/keypom/__init__.py,sha256=6fWGxsVr_lb_wTf_MHrbwBl1fgf78b5ezmsVa3JeFjI,32
12
16
  py_near/dapps/keypom/async_client.py,sha256=DS594S00xhcyvVtD1W0PjmtmT3YhvXTZZ3hskbn1-1w,1422
13
17
  py_near/dapps/keypom/exceptions.py,sha256=pGz0w1Ubh2A1aHEWvUX4dVaCcO9r2Y-h2JwhBY5KJbU,45
@@ -19,11 +23,17 @@ py_near/dapps/staking/models.py,sha256=zC5M_pc1oMqHq4GaYif1uwFbW6acD2BsiA9rbyiaU
19
23
  py_near/exceptions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
24
  py_near/exceptions/exceptions.py,sha256=DEFipaAHm0y7oCuN2QKzHsiQvUTUQVl-Ce36Ag7n7hs,5509
21
25
  py_near/exceptions/provider.py,sha256=K-wexgjPJ8sw42JePwaP7R5dJEIn9DoFJRvVcURsx6s,7718
22
- py_near/models.py,sha256=85ynmLVJnzWvFKq-Z8iIO0waU-2iP-CSkaOGX4mYoL0,11590
23
- py_near/providers.py,sha256=X_GfddFzmafBatqFJH41Dp_lYvTNcz1Q3Ko2tEnN0VU,16193
24
- py_near/transactions.py,sha256=QwEP3qOGxb8OB2vZA_rU20jvkPA0FmJowUKDxbuzOuI,3251
25
- py_near/utils.py,sha256=FirRH93ydH1cwjn0-sNrZeIn3BRD6QHedrP2VkAdJ6g,126
26
- py_near-1.1.53.dist-info/LICENSE,sha256=I_GOA9xJ35FiL-KnYXZJdATkbO2KcV2dK2enRGVxzKM,1023
27
- py_near-1.1.53.dist-info/METADATA,sha256=aE8cvfs6Axphip2sszcwmHLdd1cXmXq1d3W54CjgMe4,4763
28
- py_near-1.1.53.dist-info/WHEEL,sha256=Nq82e9rUAnEjt98J6MlVmMCZb-t9cYE2Ir1kpBmnWfs,88
29
- py_near-1.1.53.dist-info/RECORD,,
26
+ py_near/mpc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ py_near/mpc/models.py,sha256=9MSfBDqfqkaJu6eJxnwyjgihsexZboehbJ6vAGa4BTU,663
28
+ py_near/mpc/wallet.py,sha256=sNr9UlcyVDPF_Zho_4iTupbUoZct3paQlxxDHlHK67k,9768
29
+ py_near/mpc/auth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
30
+ py_near/mpc/auth/auth_2fa.py,sha256=ZxO8mGO95ixb0qXnKrfmVxP7ivpDKerH4-udV_vUmbs,3124
31
+ py_near/mpc/auth/base.py,sha256=IoFdRWUQcerYZq8TCkpjXJQRcENnzA9WwFbVPgmHgcs,629
32
+ py_near/mpc/auth/core.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
+ py_near/mpc/auth/default_auth.py,sha256=i70412yXYx3MlFSsPxHLKbF0CXTjC39Qtl34et8kAEw,751
34
+ py_near/mpc/auth/keys_auth.py,sha256=DJPi37DnV6r6cSskHEhHBiR_QiaAQJkg0AEsYp6cePA,2359
35
+ py_near-1.1.54.dist-info/licenses/LICENSE,sha256=I_GOA9xJ35FiL-KnYXZJdATkbO2KcV2dK2enRGVxzKM,1023
36
+ py_near-1.1.54.dist-info/METADATA,sha256=WQeR_r0t--SwIfopnYnunfnheZpEGRZZClY42XPYIFo,6012
37
+ py_near-1.1.54.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
+ py_near-1.1.54.dist-info/top_level.txt,sha256=p8CZgfKEVmXnRfVX5VHR0Ls7OVFC2ZVEwthvNB8Rb2Q,8
39
+ py_near-1.1.54.dist-info/RECORD,,
@@ -1,4 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 1.9.1
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ py_near