Skip to content
This repository was archived by the owner on Oct 2, 2024. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 59 additions & 2 deletions api/metaplex_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@
from cryptography.fernet import Fernet
import base58
from solana.keypair import Keypair
from metaplex.transactions import deploy, topup, mint, send, burn, update_token_metadata
from metaplex.transactions import (
deploy,
topup,
mint,
send,
burn,
update_token_metadata,
creators_sign,
update_primary_sale_happened
)
from utils.execution_engine import execute

class MetaplexAPI():
Expand Down Expand Up @@ -132,7 +141,55 @@ def send(self, api_endpoint, contract_key, sender_key, dest_key, encrypted_priva
return json.dumps(resp)
except:
return json.dumps({"status": 400})


def creators_sign(self, api_endpoint, contract_key, encrypted_private_key, max_retries=3, skip_confirmation=False, max_timeout=60, target=20, finalized=True):
"""
Unverified creators to sign minted tokens. requires the unverfied creators private keys.
May require a private key, if so this will be provided encrypted using Fernet: https://cryptography.io/en/latest/fernet/
Return a status flag of success or fail and the native transaction data.
"""
try:
private_key = list(self.cipher.decrypt(encrypted_private_key))
tx, signers = creators_sign(api_endpoint, contract_key, private_key)
resp = execute(
api_endpoint,
tx,
signers,
max_retries=max_retries,
skip_confirmation=skip_confirmation,
max_timeout=max_timeout,
target=target,
finalized=finalized,
)
resp["status"] = 200
return json.dumps(resp)
except:
return json.dumps({"status": 400})

def update_primary_sale_happened(self, api_endpoint, contract_key, encrypted_private_key, max_retries=3, skip_confirmation=False, max_timeout=60, target=20, finalized=True):
"""
Updates primary sale happend to secondary sale. requires the owner's private keys
May require a private key, if so this will be provided encrypted using Fernet: https://cryptography.io/en/latest/fernet/
Return a status flag of success or fail and the native transaction data.
"""
try:
private_key = list(self.cipher.decrypt(encrypted_private_key))
tx, signers = update_primary_sale_happened(api_endpoint, contract_key, private_key)
resp = execute(
api_endpoint,
tx,
signers,
max_retries=max_retries,
skip_confirmation=skip_confirmation,
max_timeout=max_timeout,
target=target,
finalized=finalized,
)
resp["status"] = 200
return json.dumps(resp)
except:
return json.dumps({"status": 400})

def burn(self, api_endpoint, contract_key, owner_key, encrypted_private_key, max_retries=3, skip_confirmation=False, max_timeout=60, target=20, finalized=True):
"""
Burn a token, permanently removing it from the blockchain.
Expand Down
38 changes: 37 additions & 1 deletion metaplex/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@
MAX_URI_LENGTH = 200
MAX_CREATOR_LENGTH = 34
MAX_CREATOR_LIMIT = 5

class InstructionType(IntEnum):
CREATE_METADATA = 0
UPDATE_METADATA = 1
PRIMARY_SALE_HAPPENED = 4
SIGN_METADATA = 7

METADATA_PROGRAM_ID = PublicKey('metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s')
SYSTEM_PROGRAM_ID = PublicKey('11111111111111111111111111111111')
Expand Down Expand Up @@ -108,7 +111,6 @@ 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),
Expand Down Expand Up @@ -204,6 +206,40 @@ def update_metadata_instruction(data, update_authority, mint_key):
]
return TransactionInstruction(keys=keys, program_id=METADATA_PROGRAM_ID, data=data)

def update_primary_sale_happened_instruction(update_authority, mint_key, token):
data_struct = cStruct(
"instruction_type" / Int8ul
)
data = data_struct.build(
dict(
instruction_type=InstructionType.PRIMARY_SALE_HAPPENED
)
)
metadata_account = get_metadata_account(mint_key)
keys = [
AccountMeta(pubkey=metadata_account, is_signer=False, is_writable=True),
AccountMeta(pubkey=update_authority, is_signer=True, is_writable=False),
AccountMeta(pubkey=token, is_signer=False, is_writable=False),
]
return TransactionInstruction(keys=keys, program_id=METADATA_PROGRAM_ID, data=data)

def creators_sign_metadata_instruction(mint_key, creator):
data_struct = cStruct(
"instruction_type" / Int8ul
)
data = data_struct.build(
dict(
instruction_type=InstructionType.SIGN_METADATA
)
)
metadata_account = get_metadata_account(mint_key)
keys = [
AccountMeta(pubkey=metadata_account, is_signer=False, is_writable=True),
AccountMeta(pubkey=creator, is_signer=True, is_writable=False),
]
return TransactionInstruction(keys=keys, program_id=METADATA_PROGRAM_ID, data=data)


def create_master_edition_instruction(
mint: PublicKey,
update_authority: PublicKey,
Expand Down
54 changes: 54 additions & 0 deletions metaplex/transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
update_metadata_instruction,
ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID,
TOKEN_PROGRAM_ID,
update_primary_sale_happened_instruction,
creators_sign_metadata_instruction,
)


Expand Down Expand Up @@ -263,6 +265,58 @@ def send(api_endpoint, source_account, contract_key, sender_key, dest_key, priva
return tx, signers


def creators_sign(api_endpoint, contract_key, creator_private_key):
"""
Sign the token by unverified creators
Requires to be run by they keypair of the creator that are unverified
Return a status flag of success or fail and the native transaction data.
"""
# Initialize Client
client = Client(api_endpoint)
# List signers
creator_wallet_key = Keypair(creator_private_key)
signers = [creator_wallet_key]
# List accounts
creator_account = PublicKey(creator_wallet_key.public_address)
mint_account = PublicKey(contract_key)
# Start transaction
tx = Transaction()
# Create instruction and add
creators_sign_metadata_ix = creators_sign_metadata_instruction(
mint_key=mint_pubkey,
creator=creator_pubkey
)
txn = txn.add(creators_sign_metadata_ix)

return tx, signers

def update_primary_sale_happened(api_endpoint, contract_key, owner_private_key):
"""
Updates primary sale happened
Return a status flag of success or fail and the native transaction data.
"""
# Initialize Client
client = Client(api_endpoint)
# List signers
owner_wallet_key = Keypair(owner_private_key)
signers = [owner_wallet_key]
# List accounts
owner_account = PublicKey(owner_private_key.public_address)
mint_account = PublicKey(contract_key)

# Create instruction and add
associated_token_account = get_associated_token_address(owner_account, mint_account)

update_primary_sale_happened_ix = update_primary_sale_happened_instruction(
update_authority=owner_account,
mint_key=mint_account,
token=associated_token_account
)

tx = tx.add(update_primary_sale_happened_ix)

return tx, signers

def burn(api_endpoint, contract_key, owner_key, private_key):
"""
Burn a token, permanently removing it from the blockchain.
Expand Down