shadowPaySDK 0.1.0__py3-none-any.whl → 0.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.
- shadowPaySDK/__init__.py +27 -2
- shadowPaySDK/const.py +639 -0
- shadowPaySDK/interface/__init__.py +4 -0
- shadowPaySDK/interface/erc20.py +70 -52
- shadowPaySDK/interface/sol.py +225 -0
- shadowPaySDK/types/EVMcheque.py +495 -0
- shadowPaySDK/types/SOLcheque.py +282 -0
- shadowPaySDK/types/__init__.py +4 -0
- shadowPaySDK/utils/__init__.py +8 -0
- shadowPaySDK/utils/utils.py +28 -0
- {shadowpaysdk-0.1.0.dist-info → shadowpaysdk-0.1.dist-info}/METADATA +23 -7
- shadowpaysdk-0.1.dist-info/RECORD +16 -0
- shadowpaysdk-0.1.0.dist-info/RECORD +0 -9
- {shadowpaysdk-0.1.0.dist-info → shadowpaysdk-0.1.dist-info}/WHEEL +0 -0
- {shadowpaysdk-0.1.0.dist-info → shadowpaysdk-0.1.dist-info}/licenses/LICENSE +0 -0
- {shadowpaysdk-0.1.0.dist-info → shadowpaysdk-0.1.dist-info}/top_level.txt +0 -0
shadowPaySDK/interface/erc20.py
CHANGED
@@ -1,74 +1,85 @@
|
|
1
|
+
import shadowPaySDK
|
1
2
|
from web3 import Web3
|
2
3
|
import json
|
3
4
|
from typing import Union, Optional
|
4
|
-
|
5
|
-
ERC20_ABI = json.loads("""[
|
6
|
-
{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"type":"function"},
|
7
|
-
{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"type":"function"},
|
8
|
-
{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"type":"function"},
|
9
|
-
{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"type":"function"},
|
10
|
-
{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"type":"function"},
|
11
|
-
{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"type":"function"},
|
12
|
-
{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"type":"function"},
|
13
|
-
{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"type":"function"},
|
14
|
-
{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"type":"function"}
|
15
|
-
]""")
|
5
|
+
from shadowPaySDK.const import __ERC20_ABI__
|
16
6
|
|
17
7
|
|
18
8
|
class ERC20Token:
|
19
|
-
def __init__(self,
|
20
|
-
self.web3 =
|
9
|
+
def __init__(self, w3: Optional[Web3] = None, explorer: Optional[str] = None):
|
10
|
+
self.web3 = w3
|
21
11
|
self.explorer = explorer
|
22
|
-
|
23
|
-
self.
|
12
|
+
|
13
|
+
self.address = None
|
14
|
+
self.contract = None
|
15
|
+
|
16
|
+
def set_params(self, token_address: Optional[str] = None, w3:Optional[Web3] = None):
|
17
|
+
if w3:
|
18
|
+
self.web3 = w3
|
19
|
+
if token_address:
|
20
|
+
self.address = Web3.to_checksum_address(token_address)
|
21
|
+
self.contract = self.web3.eth.contract(address=self.address, abi=__ERC20_ABI__)
|
22
|
+
|
23
|
+
def _ensure_contract(self):
|
24
|
+
if not self.contract:
|
25
|
+
raise ValueError("Token address is not set. Use set_params first.")
|
24
26
|
|
25
27
|
def _format_tx(self, tx_hash: str) -> str:
|
26
28
|
if self.explorer:
|
27
29
|
return f"{self.explorer.rstrip('/')}/tx/{tx_hash}"
|
28
30
|
return tx_hash
|
29
|
-
|
30
|
-
|
31
|
-
|
31
|
+
def gen_wallet(self) -> str:
|
32
|
+
account = self.web3.eth.account.create()
|
33
|
+
return account
|
32
34
|
def get_decimals(self) -> int:
|
35
|
+
self._ensure_contract()
|
36
|
+
|
33
37
|
return self.contract.functions.decimals().call()
|
34
38
|
|
35
39
|
def get_symbol(self) -> str:
|
40
|
+
self._ensure_contract()
|
36
41
|
return self.contract.functions.symbol().call()
|
37
42
|
|
38
43
|
def get_balance(self, wallet_address: str) -> float:
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
return raw_balance / (10 ** self.get_decimals())
|
43
|
-
|
44
|
+
self._ensure_contract()
|
45
|
+
raw = self.contract.functions.balanceOf(Web3.to_checksum_address(wallet_address)).call()
|
46
|
+
return raw
|
44
47
|
|
45
48
|
|
46
49
|
def allowance(self, owner: str, spender: str) -> float:
|
50
|
+
self._ensure_contract()
|
47
51
|
raw = self.contract.functions.allowance(
|
48
52
|
Web3.to_checksum_address(owner),
|
49
53
|
Web3.to_checksum_address(spender)
|
50
54
|
).call()
|
51
|
-
return raw
|
55
|
+
return raw
|
52
56
|
|
53
|
-
def ensure_allowance(self, private_key: str, spender: str, amount:
|
57
|
+
def ensure_allowance(self, private_key: str, spender: str, amount, converted_amount: bool = False) -> Union[bool, str]:
|
58
|
+
self._ensure_contract()
|
54
59
|
account = self.web3.eth.account.from_key(private_key)
|
55
|
-
|
56
|
-
if
|
60
|
+
current = self.allowance(account.address, spender)
|
61
|
+
if current == amount:
|
57
62
|
return True
|
58
|
-
return self.approve(private_key, spender, amount)
|
59
|
-
|
63
|
+
return self.approve(private_key, spender, amount, conveted_amount=converted_amount)
|
64
|
+
|
60
65
|
def transfer(self, private_key: str, to: str, amount: float) -> str:
|
66
|
+
self._ensure_contract()
|
61
67
|
account = self.web3.eth.account.from_key(private_key)
|
62
|
-
|
63
|
-
|
64
|
-
|
68
|
+
|
69
|
+
estimated_gas = self.contract.functions.transfer(
|
70
|
+
Web3.to_checksum_address(to),
|
71
|
+
amount
|
72
|
+
).estimate_gas({
|
73
|
+
'from': account.address,
|
74
|
+
'gasPrice': self.web3.to_wei('5', 'gwei'),
|
75
|
+
})
|
65
76
|
txn = self.contract.functions.transfer(
|
66
77
|
Web3.to_checksum_address(to),
|
67
|
-
|
78
|
+
amount
|
68
79
|
).build_transaction({
|
69
80
|
'from': account.address,
|
70
81
|
'nonce': self.web3.eth.get_transaction_count(account.address),
|
71
|
-
'gas':
|
82
|
+
'gas': estimated_gas,
|
72
83
|
'gasPrice': self.web3.to_wei('5', 'gwei'),
|
73
84
|
})
|
74
85
|
|
@@ -76,27 +87,34 @@ class ERC20Token:
|
|
76
87
|
tx_hash = self.web3.eth.send_raw_transaction(signed.raw_transaction)
|
77
88
|
return self._format_tx(self.web3.to_hex(tx_hash))
|
78
89
|
|
79
|
-
def approve(self,
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
90
|
+
def approve(self, spender: str, amount: float,address:Optional[str] = None, private_key: Optional[str] = None, conveted_amount: bool = True) -> str:
|
91
|
+
|
92
|
+
self._ensure_contract()
|
93
|
+
key = private_key
|
94
|
+
|
95
|
+
if key:
|
96
|
+
address = Web3.to_checksum_address(self.web3.eth.account.from_key(key).address)
|
97
|
+
|
98
|
+
elif self.address:
|
99
|
+
address = Web3.to_checksum_address(self.address)
|
100
|
+
else:
|
101
|
+
raise ValueError("No private key or address provided")
|
84
102
|
txn = self.contract.functions.approve(
|
85
103
|
Web3.to_checksum_address(spender),
|
86
|
-
|
104
|
+
amount
|
87
105
|
).build_transaction({
|
88
|
-
'from':
|
89
|
-
'nonce': self.web3.eth.get_transaction_count(
|
90
|
-
'gas':
|
91
|
-
|
106
|
+
'from': address,
|
107
|
+
'nonce': self.web3.eth.get_transaction_count(address),
|
108
|
+
'gas': 60000,
|
109
|
+
|
110
|
+
'gasPrice': self.web3.eth.gas_price,
|
92
111
|
})
|
112
|
+
|
93
113
|
|
94
|
-
signed = self.web3.eth.account.sign_transaction(txn,
|
114
|
+
signed = self.web3.eth.account.sign_transaction(txn, key)
|
95
115
|
tx_hash = self.web3.eth.send_raw_transaction(signed.raw_transaction)
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
116
|
+
tx_receipt = self.web3.eth.wait_for_transaction_receipt(tx_hash)
|
117
|
+
if tx_receipt.status != 1:
|
118
|
+
raise ValueError(f"aaprove fail.\n {self._format_tx(self.web3.to_hex(tx_hash))}")
|
119
|
+
return f"{self._format_tx(self.web3.to_hex(tx_hash))}"
|
102
120
|
|
@@ -0,0 +1,225 @@
|
|
1
|
+
import asyncio
|
2
|
+
import solana
|
3
|
+
from solana.rpc.async_api import AsyncClient, GetTokenAccountsByOwnerResp
|
4
|
+
from solders.transaction import Transaction
|
5
|
+
from solders.system_program import TransferParams as p
|
6
|
+
import spl
|
7
|
+
import spl.token
|
8
|
+
import spl.token.constants
|
9
|
+
from spl.token.instructions import get_associated_token_address, create_associated_token_account, transfer, close_account, TransferParams
|
10
|
+
from solders.system_program import transfer as ts
|
11
|
+
from solders.system_program import TransferParams as tsf
|
12
|
+
from spl.token.constants import TOKEN_PROGRAM_ID, ASSOCIATED_TOKEN_PROGRAM_ID
|
13
|
+
from solana.rpc.types import TxOpts, TokenAccountOpts
|
14
|
+
from solana.rpc.types import TxOpts
|
15
|
+
import solders
|
16
|
+
from solders.message import Message
|
17
|
+
|
18
|
+
# from solders.pubkey import Pubkey
|
19
|
+
# from solders.keypair import Keypair
|
20
|
+
# from solders.signature import Signature
|
21
|
+
# from solders.transaction import Transaction
|
22
|
+
from spl.token.async_client import AsyncToken
|
23
|
+
|
24
|
+
|
25
|
+
from solana.rpc.commitment import Confirmed
|
26
|
+
from solana.rpc.async_api import AsyncClient
|
27
|
+
import anchorpy
|
28
|
+
from anchorpy import Provider, Wallet, Idl
|
29
|
+
from typing import Optional, Union
|
30
|
+
import pprint
|
31
|
+
import httpx
|
32
|
+
import base64
|
33
|
+
import re
|
34
|
+
|
35
|
+
|
36
|
+
LAMPORTS_PER_SOL = 1_000_000_000 # 1 SOL = 1,000,000,000 lamports
|
37
|
+
|
38
|
+
class SOL:
|
39
|
+
def __init__(self, rpc_url = "https://api.mainnet-beta.solana.com", KEYPAIR: Optional[Union[str, solders.keypair.Keypair]] = None,TOKEN_MINT: Optional[str] = None):
|
40
|
+
self.rpc_url = rpc_url
|
41
|
+
|
42
|
+
self.client = AsyncClient(rpc_url)
|
43
|
+
self.KEYPAIR = None
|
44
|
+
self.PROGRAM_ID = TOKEN_PROGRAM_ID # Default to the SPL Token Program ID
|
45
|
+
self.TOKEN_MINT = TOKEN_MINT
|
46
|
+
self.WRAPED_SOL_ID = spl.token.constants.WRAPPED_SOL_MINT
|
47
|
+
if KEYPAIR:
|
48
|
+
self.set_keypair(KEYPAIR)
|
49
|
+
|
50
|
+
def set_keypair(self, KEYPAIR: Union[str, solders.keypair.Keypair]):
|
51
|
+
if isinstance(KEYPAIR, str):
|
52
|
+
try:
|
53
|
+
self.KEYPAIR = solders.keypair.Keypair.from_base58_string(KEYPAIR)
|
54
|
+
except Exception as e:
|
55
|
+
raise ValueError(f"Invalid Keypair string: {e}")
|
56
|
+
elif isinstance(KEYPAIR, solders.keypair.Keypair):
|
57
|
+
self.KEYPAIR = KEYPAIR
|
58
|
+
else:
|
59
|
+
raise ValueError("KEYPAIR must be a Keypair instance or a base58 encoded string.")
|
60
|
+
|
61
|
+
def set_params(self, rpc_url: Optional[str] = None, KEYPAIR: Optional[Union[str, solders.keypair.Keypair]] = None,TOKEN_MINT: Optional[str] = None):
|
62
|
+
if rpc_url:
|
63
|
+
self.rpc_url = rpc_url
|
64
|
+
self.client = AsyncClient(rpc_url)
|
65
|
+
if KEYPAIR:
|
66
|
+
self.set_keypair(KEYPAIR)
|
67
|
+
if TOKEN_MINT:
|
68
|
+
self.TOKEN_MINT = TOKEN_MINT
|
69
|
+
|
70
|
+
def get_pubkey(self, returnString: Optional[bool] = None):
|
71
|
+
|
72
|
+
|
73
|
+
if self.KEYPAIR:
|
74
|
+
pubkey = self.KEYPAIR.pubkey()
|
75
|
+
pubkey_str = str(pubkey)
|
76
|
+
if returnString:
|
77
|
+
return pubkey_str
|
78
|
+
return pubkey
|
79
|
+
|
80
|
+
raise ValueError("Keypair not set")
|
81
|
+
|
82
|
+
def gen_wallet(self):
|
83
|
+
return solders.keypair.Keypair()
|
84
|
+
async def get_balance(self):
|
85
|
+
resp = await self.client.get_balance(self.get_pubkey())
|
86
|
+
lamports = resp.value
|
87
|
+
sol_balance = lamports / LAMPORTS_PER_SOL
|
88
|
+
return sol_balance
|
89
|
+
async def get_token_accounts_by_owner(self,owner_pubkey: Optional[str] = None):
|
90
|
+
if not owner_pubkey:
|
91
|
+
print("No owner pubkey provided, using the wallet's pubkey.")
|
92
|
+
owner_pubkey = self.get_pubkey(returnString=True)
|
93
|
+
headers = {
|
94
|
+
"Content-Type": "application/json",
|
95
|
+
}
|
96
|
+
body = {
|
97
|
+
"jsonrpc": "2.0",
|
98
|
+
"id": 1,
|
99
|
+
"method": "getTokenAccountsByOwner",
|
100
|
+
"params": [
|
101
|
+
str(owner_pubkey),
|
102
|
+
{"programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},
|
103
|
+
{"encoding": "jsonParsed"}
|
104
|
+
]
|
105
|
+
}
|
106
|
+
async with httpx.AsyncClient() as client:
|
107
|
+
response = await client.post(self.rpc_url, headers=headers, json=body)
|
108
|
+
result = response.json()
|
109
|
+
accounts = result["result"]["value"]
|
110
|
+
|
111
|
+
token_data = {}
|
112
|
+
for acc in accounts:
|
113
|
+
parsed = acc["account"]["data"]["parsed"]["info"]
|
114
|
+
mint = parsed["mint"]
|
115
|
+
ui_amount = parsed["tokenAmount"]["uiAmount"]
|
116
|
+
token_data[mint] = {"amount": ui_amount}
|
117
|
+
|
118
|
+
|
119
|
+
|
120
|
+
return token_data
|
121
|
+
|
122
|
+
async def fetch_metadata_raw(self,mint_address: str):
|
123
|
+
METADATA_PROGRAM_ID = solders.pubkey.Pubkey.from_string("metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s")
|
124
|
+
mint = solders.pubkey.Pubkey.from_string(mint_address)
|
125
|
+
seeds = [
|
126
|
+
b"metadata",
|
127
|
+
bytes(METADATA_PROGRAM_ID),
|
128
|
+
bytes(mint),
|
129
|
+
]
|
130
|
+
pda, _ = solders.pubkey.Pubkey.find_program_address(seeds, METADATA_PROGRAM_ID)
|
131
|
+
|
132
|
+
payload = {
|
133
|
+
"jsonrpc": "2.0",
|
134
|
+
"id": 1,
|
135
|
+
"method": "getAccountInfo",
|
136
|
+
"params": [
|
137
|
+
str(pda),
|
138
|
+
{"encoding": "base64"}
|
139
|
+
]
|
140
|
+
}
|
141
|
+
|
142
|
+
async with httpx.AsyncClient() as client:
|
143
|
+
r = await client.post(self.rpc_url, json=payload)
|
144
|
+
data = r.json()
|
145
|
+
|
146
|
+
if not data["result"]["value"]:
|
147
|
+
return None
|
148
|
+
|
149
|
+
b64_data = data["result"]["value"]["data"][0]
|
150
|
+
raw_bytes = base64.b64decode(b64_data)
|
151
|
+
|
152
|
+
name = raw_bytes[1+32+32 : 1+32+32+32].decode("utf-8").rstrip("\x00")
|
153
|
+
name = re.sub(r'[^\x20-\x7E]', '', name)
|
154
|
+
return {
|
155
|
+
"mint": mint_address,
|
156
|
+
"name": name,
|
157
|
+
|
158
|
+
|
159
|
+
}
|
160
|
+
async def transfer_token(self, to: str, amount: float):
|
161
|
+
|
162
|
+
if not self.TOKEN_MINT:
|
163
|
+
raise ValueError("not set TOKEN_MINT.")
|
164
|
+
if not self.KEYPAIR:
|
165
|
+
raise ValueError("not set KEYPAIR.")
|
166
|
+
|
167
|
+
sender_pubkey = self.get_pubkey()
|
168
|
+
receiver_pubkey = solders.pubkey.Pubkey.from_string(to)
|
169
|
+
token_pubkey = solders.pubkey.Pubkey.from_string(self.TOKEN_MINT)
|
170
|
+
|
171
|
+
token = AsyncToken(self.client, token_pubkey, TOKEN_PROGRAM_ID, self.KEYPAIR)
|
172
|
+
sender_ata = get_associated_token_address(sender_pubkey, token_pubkey)
|
173
|
+
receiver_ata = get_associated_token_address(receiver_pubkey, token_pubkey)
|
174
|
+
|
175
|
+
tx = Transaction()
|
176
|
+
|
177
|
+
res = await self.client.get_account_info(receiver_ata)
|
178
|
+
if res.value is None:
|
179
|
+
tx.add(
|
180
|
+
create_associated_token_account(
|
181
|
+
payer=sender_pubkey,
|
182
|
+
owner=receiver_pubkey,
|
183
|
+
mint=token_pubkey
|
184
|
+
)
|
185
|
+
)
|
186
|
+
|
187
|
+
decimals = (await token.get_mint_info()).decimals
|
188
|
+
real_amount = int(amount * (10 ** decimals))
|
189
|
+
params = TransferParams(
|
190
|
+
program_id=TOKEN_PROGRAM_ID,
|
191
|
+
source=sender_ata,
|
192
|
+
dest=receiver_ata,
|
193
|
+
owner=sender_pubkey,
|
194
|
+
amount=real_amount
|
195
|
+
)
|
196
|
+
|
197
|
+
tx.add(transfer(params))
|
198
|
+
|
199
|
+
resp = await self.client.send_transaction(tx, self.KEYPAIR, opts=TxOpts(skip_preflight=True, preflight_commitment=Confirmed))
|
200
|
+
return resp.value
|
201
|
+
|
202
|
+
|
203
|
+
async def transfer_native(self, to:str, amount: int):
|
204
|
+
if not self.KEYPAIR:
|
205
|
+
raise ValueError("not set KEYPAIR.")
|
206
|
+
|
207
|
+
sender_pubkey = self.get_pubkey()
|
208
|
+
receiver_pubkey = solders.pubkey.Pubkey.from_string(to)
|
209
|
+
ixns = [
|
210
|
+
ts(tsf(
|
211
|
+
from_pubkey=sender_pubkey,
|
212
|
+
to_pubkey=receiver_pubkey,
|
213
|
+
lamports=int(amount * LAMPORTS_PER_SOL)
|
214
|
+
))
|
215
|
+
]
|
216
|
+
msg = Message(ixns, self.get_pubkey())
|
217
|
+
latest_blockhash_resp = await self.client.get_latest_blockhash()
|
218
|
+
|
219
|
+
blockhash_str = latest_blockhash_resp.value.blockhash
|
220
|
+
tx = Transaction([self.KEYPAIR], msg, blockhash_str)
|
221
|
+
resp = await self.client.send_transaction(tx)
|
222
|
+
return resp.value
|
223
|
+
|
224
|
+
|
225
|
+
|