diff --git a/README.md b/README.md index 490fd18..8470d14 100644 --- a/README.md +++ b/README.md @@ -77,13 +77,14 @@ Args: Example: ```python ->>> account = Account() ->>> cfg = {"PRIVATE_KEY": base58.b58encode(account.secret_key()).decode("ascii"), "PUBLIC_KEY": str(account.public_key()), "DECRYPTION_KEY": Fernet.generate_key().decode("ascii")} +>>> account = KeyPair() +>>> cfg = {"PRIVATE_KEY": base58.b58encode(account.seed).decode("ascii"), "PUBLIC_KEY": str(account.public_key), "DECRYPTION_KEY": Fernet.generate_key().decode("ascii")} >>> api_endpoint = "https://api.devnet.solana.com/" ->>> Client(api_endpoint).request_airdrop(account.public_key(), int(1e10)) +>>> Client(api_endpoint).request_airdrop(account.public_key, int(1e10)) {'jsonrpc': '2.0', 'result': '4ojKmAAesmKtqJkNLRtEjdgg4CkmowuTAjRSpp3K36UvQQvEXwhirV85E8cvWYAD42c3UyFdCtzydMgWokH2mbM', 'id': 1} >>> metaplex_api = MetaplexAPI(cfg) ->>> metaplex_api.deploy(api_endpoint, "A"*32, "A"*10) +>>> seller_basis_fees = 0 # value in 10000 +>>> metaplex_api.deploy(api_endpoint, "A"*32, "A"*10, seller_basis_fees) '{"status": 200, "contract": "7bxe7t1aGdum8o97bkuFeeBTcbARaBn9Gbv5sBd9DZPG", "msg": "Successfully created mint 7bxe7t1aGdum8o97bkuFeeBTcbARaBn9Gbv5sBd9DZPG", "tx": "2qmiWoVi2PNeAjppe2cNbY32zZCJLXMYgdS1zRVFiKJUHE41T5b1WfaZtR2QdFJUXadrqrjbkpwRN5aG2J3KQrQx"}' >>> ``` @@ -197,16 +198,16 @@ https://explorer.solana.com/tx/5kd5g4mNBSjoTVYwAasWZx6iB8ijaELfBukKrNYBeDvLomK7i This is the sequential code from the previous section. These accounts will need to change if you want to do your own test. ``` -account = Account() -cfg = {"PRIVATE_KEY": base58.b58encode(account.secret_key()).decode("ascii"), "PUBLIC_KEY": str(account.public_key()), "DECRYPTION_KEY": Fernet.generate_key().decode("ascii")} +account = KeyPair() +cfg = {"PRIVATE_KEY": base58.b58encode(account.seed).decode("ascii"), "PUBLIC_KEY": str(account.public_key), "DECRYPTION_KEY": Fernet.generate_key().decode("ascii")} api_endpoint = "https://api.devnet.solana.com/" -Client(api_endpoint).request_airdrop(account.public_key(), int(1e10)) +Client(api_endpoint).request_airdrop(account.public_key, int(1e10)) # Create API metaplex_api = MetaplexAPI(cfg) # Deploy -metaplex_api.deploy(api_endpoint, "A"*32, "A"*10) +metaplex_api.deploy(api_endpoint, "A"*32, "A"*10, 0) # Topup VtdBygLSt1EJF5M3nUk5CRxuNNTyZFUsKJ4yUVcC6hh metaplex_api.topup(api_endpoint, "VtdBygLSt1EJF5M3nUk5CRxuNNTyZFUsKJ4yUVcC6hh") diff --git a/api/metaplex_api.py b/api/metaplex_api.py index 4983ca3..2bee099 100644 --- a/api/metaplex_api.py +++ b/api/metaplex_api.py @@ -1,7 +1,7 @@ import json from cryptography.fernet import Fernet import base58 -from solana.account import Account +from solana.keypair import Keypair from metaplex.transactions import deploy, topup, mint, send, burn, update_token_metadata from utils.execution_engine import execute @@ -10,14 +10,14 @@ class MetaplexAPI(): def __init__(self, cfg): self.private_key = list(base58.b58decode(cfg["PRIVATE_KEY"]))[:32] self.public_key = cfg["PUBLIC_KEY"] - self.account = Account(self.private_key) + self.keypair = Keypair(self.private_key) self.cipher = Fernet(cfg["DECRYPTION_KEY"]) def wallet(self): """ Generate a wallet and return the address and private key. """ - account = Account() - pub_key = account.public_key() - private_key = list(account.secret_key()[:32]) + keypair = Keypair() + pub_key = keypair.public_key + private_key = list(keypair.seed) return json.dumps( { 'address': str(pub_key), @@ -25,13 +25,13 @@ def wallet(self): } ) - def deploy(self, api_endpoint, name, symbol, max_retries=3, skip_confirmation=False, max_timeout=60, target=20, finalized=True): + def deploy(self, api_endpoint, name, symbol, fees, max_retries=3, skip_confirmation=False, max_timeout=60, target=20, finalized=True): """ Deploy a contract to the blockchain (on network that support contracts). Takes the network ID and contract name, plus initialisers of name and symbol. Process may vary significantly between blockchains. Returns status code of success or fail, the contract address, and the native transaction data. """ try: - tx, signers, contract = deploy(api_endpoint, self.account, name, symbol) + tx, signers, contract = deploy(api_endpoint, self.keypair, name, symbol, fees) print(contract) resp = execute( api_endpoint, @@ -54,7 +54,7 @@ def topup(self, api_endpoint, to, amount=None, max_retries=3, skip_confirmation= Send a small amount of native currency to the specified wallet to handle gas fees. Return a status flag of success or fail and the native transaction data. """ try: - tx, signers = topup(api_endpoint, self.account, to, amount=amount) + tx, signers = topup(api_endpoint, self.keypair, to, amount=amount) resp = execute( api_endpoint, tx, @@ -74,7 +74,7 @@ def mint(self, api_endpoint, contract_key, dest_key, link, max_retries=3, skip_c """ Mints an NFT to an account, updates the metadata and creates a master edition """ - tx, signers = mint(api_endpoint, self.account, contract_key, dest_key, link, supply=supply) + tx, signers = mint(api_endpoint, self.keypair, contract_key, dest_key, link, supply=supply) resp = execute( api_endpoint, tx, @@ -94,7 +94,7 @@ def update_token_metadata(self, api_endpoint, mint_token_id, link, data, creato """ Updates the json metadata for a given mint token id. """ - tx, signers = update_token_metadata(api_endpoint, self.account, mint_token_id, link, data, fee, creators_addresses, creators_verified, creators_share) + tx, signers = update_token_metadata(api_endpoint, self.keypair, mint_token_id, link, data, fee, creators_addresses, creators_verified, creators_share) resp = execute( api_endpoint, tx, @@ -117,7 +117,7 @@ def send(self, api_endpoint, contract_key, sender_key, dest_key, encrypted_priva """ try: private_key = list(self.cipher.decrypt(encrypted_private_key)) - tx, signers = send(api_endpoint, self.account, contract_key, sender_key, dest_key, private_key) + tx, signers = send(api_endpoint, self.keypair, contract_key, sender_key, dest_key, private_key) resp = execute( api_endpoint, tx, diff --git a/metaplex/metadata.py b/metaplex/metadata.py index 09f2adf..5187dc6 100644 --- a/metaplex/metadata.py +++ b/metaplex/metadata.py @@ -108,6 +108,7 @@ def create_metadata_instruction_data(name, symbol, fee, creators): def create_metadata_instruction(data, update_authority, mint_key, mint_authority_key, payer): metadata_account = get_metadata_account(mint_key) + print(metadata_account) keys = [ AccountMeta(pubkey=metadata_account, is_signer=False, is_writable=True), AccountMeta(pubkey=mint_key, is_signer=False, is_writable=False), diff --git a/metaplex/transactions.py b/metaplex/transactions.py index 5e8d7f5..4718a16 100644 --- a/metaplex/transactions.py +++ b/metaplex/transactions.py @@ -2,7 +2,7 @@ import base64 from solana.publickey import PublicKey from solana.transaction import Transaction -from solana.account import Account +from solana.keypair import Keypair from solana.rpc.api import Client from solana.system_program import transfer, TransferParams, create_account, CreateAccountParams from spl.token._layouts import MINT_LAYOUT, ACCOUNT_LAYOUT @@ -25,11 +25,11 @@ ) -def deploy(api_endpoint, source_account, name, symbol): +def deploy(api_endpoint, source_account, name, symbol, fees): # Initalize Client client = Client(api_endpoint) # List non-derived accounts - mint_account = Account() + mint_account = Keypair() token_account = TOKEN_PROGRAM_ID # List signers signers = [source_account, mint_account] @@ -41,8 +41,8 @@ def deploy(api_endpoint, source_account, name, symbol): # Generate Mint create_mint_account_ix = create_account( CreateAccountParams( - from_pubkey=source_account.public_key(), - new_account_pubkey=mint_account.public_key(), + from_pubkey=source_account.public_key, + new_account_pubkey=mint_account.public_key, lamports=lamports, space=MINT_LAYOUT.sizeof(), program_id=token_account, @@ -53,29 +53,29 @@ def deploy(api_endpoint, source_account, name, symbol): InitializeMintParams( decimals=0, program_id=token_account, - mint=mint_account.public_key(), - mint_authority=source_account.public_key(), - freeze_authority=source_account.public_key(), + mint=mint_account.public_key, + mint_authority=source_account.public_key, + freeze_authority=source_account.public_key, ) ) tx = tx.add(initialize_mint_ix) # Create Token Metadata create_metadata_ix = create_metadata_instruction( - data=create_metadata_instruction_data(name, symbol, [str(source_account.public_key())]), - update_authority=source_account.public_key(), - mint_key=mint_account.public_key(), - mint_authority_key=source_account.public_key(), - payer=source_account.public_key(), + data=create_metadata_instruction_data(name, symbol, fees, [str(source_account.public_key)]), + update_authority=source_account.public_key, + mint_key=mint_account.public_key, + mint_authority_key=source_account.public_key, + payer=source_account.public_key, ) tx = tx.add(create_metadata_ix) - return tx, signers, str(mint_account.public_key()) + return tx, signers, str(mint_account.public_key) def wallet(): """ Generate a wallet and return the address and private key. """ - account = Account() - pub_key = account.public_key() - private_key = list(account.secret_key()[:32]) + account = Keypair() + pub_key = account.public_key + private_key = list(account.seed) return json.dumps( { 'address': str(pub_key), @@ -103,7 +103,7 @@ def topup(api_endpoint, sender_account, to, amount=None): else: lamports = int(amount) # Generate transaction - transfer_ix = transfer(TransferParams(from_pubkey=sender_account.public_key(), to_pubkey=dest_account, lamports=lamports)) + transfer_ix = transfer(TransferParams(from_pubkey=sender_account.public_key, to_pubkey=dest_account, lamports=lamports)) tx = tx.add(transfer_ix) return tx, signers @@ -126,7 +126,7 @@ def update_token_metadata(api_endpoint, source_account, mint_token_id, link, dat ) update_metadata_ix = update_metadata_instruction( update_metadata_data, - source_account.public_key(), + source_account.public_key, mint_account, ) tx = tx.add(update_metadata_ix) @@ -166,7 +166,7 @@ def mint(api_endpoint, source_account, contract_key, dest_key, link, supply=1): if account_state == 0: associated_token_account_ix = create_associated_token_account_instruction( associated_token_account=associated_token_account, - payer=source_account.public_key(), # signer + payer=source_account.public_key, # signer wallet_address=user_account, token_mint_address=mint_account, ) @@ -177,9 +177,9 @@ def mint(api_endpoint, source_account, contract_key, dest_key, link, supply=1): program_id=TOKEN_PROGRAM_ID, mint=mint_account, dest=associated_token_account, - mint_authority=source_account.public_key(), + mint_authority=source_account.public_key, amount=1, - signers=[source_account.public_key()], + signers=[source_account.public_key], ) ) tx = tx.add(mint_to_ix) @@ -188,22 +188,22 @@ def mint(api_endpoint, source_account, contract_key, dest_key, link, supply=1): metadata['data']['name'], metadata['data']['symbol'], link, + metadata['data']['seller_fee_basis_points'], metadata['data']['creators'], - metadata['data']['fee'], metadata['data']['verified'], metadata['data']['share'], ) update_metadata_ix = update_metadata_instruction( update_metadata_data, - source_account.public_key(), + source_account.public_key, mint_account, ) tx = tx.add(update_metadata_ix) create_master_edition_ix = create_master_edition_instruction( mint=mint_account, - update_authority=source_account.public_key(), - mint_authority=source_account.public_key(), - payer=source_account.public_key(), + update_authority=source_account.public_key, + mint_authority=source_account.public_key, + payer=source_account.public_key, supply=supply, ) tx = tx.add(create_master_edition_ix) @@ -219,7 +219,7 @@ def send(api_endpoint, source_account, contract_key, sender_key, dest_key, priva # Initialize Client client = Client(api_endpoint) # List non-derived accounts - owner_account = Account(private_key) # Owner of contract + owner_account = Keypair(private_key) # Owner of contract sender_account = PublicKey(sender_key) # Public key of `owner_account` token_account = TOKEN_PROGRAM_ID mint_account = PublicKey(contract_key) @@ -243,7 +243,7 @@ def send(api_endpoint, source_account, contract_key, sender_key, dest_key, priva if account_state == 0: associated_token_account_ix = create_associated_token_account_instruction( associated_token_account=associated_token_account, - payer=source_account.public_key(), # signer + payer=source_account.public_key, # signer wallet_address=dest_account, token_mint_address=mint_account, ) @@ -276,7 +276,7 @@ def burn(api_endpoint, contract_key, owner_key, private_key): token_account = TOKEN_PROGRAM_ID mint_account = PublicKey(contract_key) # List signers - signers = [Account(private_key)] + signers = [Keypair(private_key)] # Start transaction tx = Transaction() # Find PDA for sender diff --git a/requirements.txt b/requirements.txt index e70458c..3732285 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,35 +1,43 @@ +anyio==3.3.4 attrs==21.2.0 -base58==2.1.0 -certifi==2021.5.30 -cffi==1.14.5 +base58==2.1.1 +cachetools==4.2.4 +certifi==2021.10.8 +cffi==1.15.0 chardet==4.0.0 +charset-normalizer==2.0.7 cheroot==8.5.2 -CherryPy==18.6.0 +CherryPy==18.6.1 construct==2.10.67 -cryptography==3.4.7 +cryptography==35.0.0 ed25519==1.5 -idna==2.10 +h11==0.12.0 +httpcore==0.13.7 +httpx==0.20.0 +idna==3.3 iniconfig==1.1.1 jaraco.classes==3.2.1 -jaraco.collections==3.3.0 +jaraco.collections==3.4.0 jaraco.functools==3.3.0 -jaraco.text==3.5.0 -more-itertools==8.8.0 -packaging==20.9 -pluggy==0.13.1 -portend==2.7.1 +jaraco.text==3.5.1 +more-itertools==8.10.0 +packaging==21.2 +pluggy==1.0.0 +portend==3.0.0 py==1.10.0 pycparser==2.20 PyNaCl==1.4.0 pyparsing==2.4.7 -pytest==6.2.4 -pytz==2021.1 -requests==2.25.1 +pytest==6.2.5 +pytz==2021.3 +requests==2.26.0 +rfc3986==1.5.0 six==1.16.0 -solana==0.9.2 -tempora==4.1.1 +sniffio==1.2.0 +solana==0.18.1 +tempora==4.1.2 toml==0.10.2 -typing-extensions==3.10.0.0 -urllib3==1.26.6 -websockets==9.1 +typing-extensions==3.10.0.2 +urllib3==1.26.7 +websockets==10.0 zc.lockfile==2.0 diff --git a/test/test_api.py b/test/test_api.py index 122d2b4..988e91e 100644 --- a/test/test_api.py +++ b/test/test_api.py @@ -4,7 +4,7 @@ import json import time import base58 -from solana.account import Account +from solana.keypair import Keypair from solana.rpc.api import Client from metaplex.metadata import get_metadata from cryptography.fernet import Fernet @@ -26,17 +26,17 @@ def await_full_confirmation(client, txn, max_timeout=60): break def test(api_endpoint="https://api.devnet.solana.com/"): - account = Account() + keypair = Keypair() cfg = { - "PRIVATE_KEY": base58.b58encode(account.secret_key()).decode("ascii"), - "PUBLIC_KEY": str(account.public_key()), + "PRIVATE_KEY": base58.b58encode(keypair.seed).decode("ascii"), + "PUBLIC_KEY": str(keypair.public_key), "DECRYPTION_KEY": Fernet.generate_key().decode("ascii"), } api = MetaplexAPI(cfg) client = Client(api_endpoint) resp = {} while 'result' not in resp: - resp = client.request_airdrop(account.public_key(), int(1e9)) + resp = client.request_airdrop(keypair.public_key, int(1e9)) print("Request Airdrop:", resp) txn = resp['result'] await_full_confirmation(client, txn) @@ -45,7 +45,8 @@ def test(api_endpoint="https://api.devnet.solana.com/"): symbol = ''.join([random.choice(letters) for i in range(10)]) print("Name:", name) print("Symbol:", symbol) - deploy_response = json.loads(api.deploy(api_endpoint, name, symbol)) + # added seller_basis_fee_points + deploy_response = json.loads(api.deploy(api_endpoint, name, symbol, 0)) print("Deploy:", deploy_response) assert deploy_response["status"] == 200 contract = deploy_response.get("contract") diff --git a/utils/execution_engine.py b/utils/execution_engine.py index a7460df..2ae2049 100644 --- a/utils/execution_engine.py +++ b/utils/execution_engine.py @@ -1,11 +1,11 @@ import time -from solana.account import Account +from solana.keypair import Keypair from solana.rpc.api import Client from solana.rpc.types import TxOpts def execute(api_endpoint, tx, signers, max_retries=3, skip_confirmation=True, max_timeout=60, target=20, finalized=True): client = Client(api_endpoint) - signers = list(map(Account, set(map(lambda s: s.secret_key(), signers)))) + signers = list(map(Keypair, set(map(lambda s: s.seed, signers)))) for attempt in range(max_retries): try: result = client.send_transaction(tx, *signers, opts=TxOpts(skip_preflight=True))