ARC69 Minter and Modifier Part 2

Video Tutorial

Contract - User Mints and Claims Pokemon

Compile the contract using:

algokit compile arc69NFTmodifier.py

Python Code

from algopy import ARC4Contract, itxn, Global, GlobalState, UInt64, gtxn, Bytes, subroutine, String, BoxRef, Txn, op, LocalState, Account
from algopy.arc4 import abimethod, Struct, Address, Bool
from algopy.arc4 import UInt64 as arc4UInt64
from algopy.arc4 import String as arc4String

class availablePokemon(Struct):
    pokemon_name: arc4String
    pokemon_type: arc4String
    pokemon_description: arc4String
    pokemon_ipfs_hash: arc4String

class userPokemonInfo(Struct):
    uid: arc4UInt64
    owner: Address
    asset_id: arc4UInt64
    pokemon_id: arc4UInt64
    level: arc4UInt64
    exp: arc4UInt64
    training: Bool
    training_start_time: arc4UInt64

class arc69NFTmodifier(ARC4Contract):
    def __init__(self) -> None:
        self.pokemonUnitCounter = GlobalState(arc4UInt64(0))
        self.pokemonCreated = GlobalState(arc4UInt64(0))
        
    @subroutine
    def itoa(self, i: UInt64) -> Bytes:
        digits = Bytes(b"0123456789")
        radix = digits.length
        if i < radix:
            return digits[i]
        return self.itoa(i // radix) + digits[i % radix]
        
    @abimethod
    def registerNewPokemonData(
        self,
        pokemon_name: arc4String,
        pokemon_type: arc4String,
        pokemon_description: arc4String,
        pokemon_ipfs_hash: arc4String,
        payment_txn: gtxn.PaymentTransaction,
    ) -> arc4String:
        
        assert payment_txn.receiver == Global.current_application_address
        self.pokemonUnitCounter.value = arc4UInt64(self.pokemonUnitCounter.value.native + 1)
        
        box_ref = BoxRef(key=self.pokemonUnitCounter.value.bytes)
        value, exists = box_ref.maybe()
        assert not exists
        
        new_pokemon_info = availablePokemon(
            pokemon_name=pokemon_name,
            pokemon_type=pokemon_type,
            pokemon_description=pokemon_description,
            pokemon_ipfs_hash=pokemon_ipfs_hash
        )
        
        box_ref.create(size=new_pokemon_info.bytes.length)
        box_ref.put(new_pokemon_info.bytes)
        
        return arc4String('Pokemon Registered to Contract: ') + pokemon_name
    

Confirming User Mint

from algosdk.v2client.algod import AlgodClient
from algokit_utils import ApplicationClient
from algosdk.atomic_transaction_composer import AtomicTransactionComposer, AccountTransactionSigner, TransactionWithSigner
from algosdk.transaction import PaymentTxn
from algosdk.account import address_from_private_key
from pathlib import Path
from algosdk.abi import ABIType
from algosdk.encoding import decode_address
from dotenv import load_dotenv
from base64 import b64decode
import os

load_dotenv()

node_token = os.getenv('algod_token')
node_server = os.getenv('algod_server')
private_key = os.getenv('private_key')

algod_client = AlgodClient(node_token, node_server)
app_spec = Path(__file__).parent / './arc69NFTmodifier.arc32.json'
app_id = int(os.getenv('app_id'))
signer = AccountTransactionSigner(private_key=private_key)
address = address_from_private_key(private_key=private_key)
params = algod_client.suggested_params()

application_client = ApplicationClient(
    algod_client=algod_client,
    app_spec=app_spec,
    app_id=app_id,
    signer=signer,
    sender=address,
    suggested_params=params
)

atc = AtomicTransactionComposer()

decoded_address = decode_address(address)
pokemon_selected_box_name = decoded_address

uint64_coder = ABIType.from_string('(uint64)')
user_pokemon_selected = algod_client.application_box_by_name(app_id, pokemon_selected_box_name)['value']
b64_decoded_value = b64decode(user_pokemon_selected)

global_states = algod_client.application_info(app_id)['params']['global-state']
for key in global_states:
    if b64decode(key['key']) == b'pokemonCreated':
        current_uid = b64decode(key['value']['bytes'])

total_bytes = (7 * 8) + (32 * 2) + 1
box_cost = (total_bytes * 400) + 2500 + 102_000

mbr_fee_payment_tx_1 = PaymentTxn(
    sender=address,
    sp=params,
    receiver=application_client.app_address,
    amt=box_cost,
    note='#1'
)
wrapped_payment_tx_1 = TransactionWithSigner(mbr_fee_payment_tx_1, signer)

box_ref_1 = b64_decoded_value              # The pokemon selected #1 , #2, or #3
box_ref_2 = decoded_address                # The users address decoded
box_ref_3 = current_uid + decoded_address  # The next unique ID counter + the users address

application_client.compose_call(
    atc, 
    call_abi_method='mintUserPokemon', 
    payment_txn=wrapped_payment_tx_1,
    transaction_parameters={
        'boxes':[[app_id, box_ref_1], [app_id, box_ref_2], [app_id, box_ref_3]]
    }
)

results = atc.execute(algod_client, 2)

tx_ids = [results.tx_ids[i] for i in range(len(results.tx_ids))]
abi_results = [results.abi_results[i].return_value for i in range(len(results.abi_results))]
print(tx_ids)
print(abi_results)

Confirming User Claim

from algosdk.v2client.algod import AlgodClient
from algokit_utils import ApplicationClient
from algosdk.atomic_transaction_composer import AtomicTransactionComposer, AccountTransactionSigner, TransactionWithSigner
from algosdk.transaction import PaymentTxn, AssetTransferTxn
from algosdk.account import address_from_private_key
from pathlib import Path
from algosdk.abi import ABIType
from algosdk.encoding import decode_address
from dotenv import load_dotenv
from base64 import b64decode
import os

load_dotenv()

node_token = os.getenv('algod_token')
node_server = os.getenv('algod_server')

private_key = os.getenv('private_key')

algod_client = AlgodClient(node_token, node_server)
app_spec = Path(__file__).parent / './arc69NFTmodifier.arc32.json'
app_id = int(os.getenv('app_id'))
signer = AccountTransactionSigner(private_key=private_key)
address = address_from_private_key(private_key=private_key)
params = algod_client.suggested_params()

application_client = ApplicationClient(
    algod_client=algod_client,
    app_spec=app_spec,
    app_id=app_id,
    signer=signer,
    sender=address,
    suggested_params=params
)

atc = AtomicTransactionComposer()

decoded_address = decode_address(address)

mbr_fee_payment_tx_1 = PaymentTxn(
    sender=address,
    sp=params,
    receiver=application_client.app_address,
    amt=2000,
    note='#1'
)
wrapped_payment_tx_1 = TransactionWithSigner(mbr_fee_payment_tx_1, signer)
atc.add_transaction(wrapped_payment_tx_1)

users_pokemon = 728462064

user_optin_tx = AssetTransferTxn(
    sender=address,
    receiver=address,
    sp=params,
    amt=0,
    index=users_pokemon
)

wrapped_user_optin_tx = TransactionWithSigner(user_optin_tx, signer)

current_uid = (0).to_bytes(8, 'big')
box_ref_1 = current_uid + decoded_address

application_client.compose_call(
    atc, 
    call_abi_method='claimPokemon', 
    opt_in_txn=wrapped_user_optin_tx,
    uid=0,
    transaction_parameters={
        'boxes':[[app_id, box_ref_1]],
        'foreign_assets': [users_pokemon]
    }
)

results = atc.execute(algod_client, 2)

tx_ids = [results.tx_ids[i] for i in range(len(results.tx_ids))]
abi_results = [results.abi_results[i].return_value for i in range(len(results.abi_results))]
print(tx_ids)
print(abi_results)

Code Editor