"AttributeError: 'list' object has no attribute 'dictify'" with app create txn

from algosdk.future import transaction
from algosdk import v2client
import algosdk

algod_client = v2client.algod.AlgodClient("", "https://node.testnet.algoexplorerapi.io", {"User-Agent": "algosdk"})

params = algod_client.suggested_params()
params.fee = 1000
params.flat_fee = True

f1 = open("testnet-contract.teal").read()
approval_program = algod_client.compile(f1)
f2 = open("clear-state.teal").read()
clear_program = algod_client.compile(f2)

createAppTxn = transaction.ApplicationCreateTxn("RABQ4PRRWJDCMHCCCZ24YTLTT44MI4KFZEMRWGFEYESDRBIKD6QFPNNGBQ", params, 1, approval_program, clear_program, 0, 2, foreign_assets=[75312966, 75313288])

stxn = createAppTxn.sign(algosdk.mnemonic.to_private_key(input("mnemonic:")))

print(f"{stxn.get_txid()} is the transaction ID. Will it work?")

algod_client.send_transaction(stxn)

I get the error AttributeError: ‘list’ object has no attribute ‘dictify’

If you look at the error, you see:

  File "/usr/local/lib/python3.9/site-packages/algosdk/future/transaction.py", line 1768, in __init__
    ApplicationCallTxn.__init__(
  File "/usr/local/lib/python3.9/site-packages/algosdk/future/transaction.py", line 1589, in __init__
    self.local_schema = self.state_schema(local_schema)
  File "/usr/local/lib/python3.9/site-packages/algosdk/future/transaction.py", line 1606, in state_schema
    if not schema or not schema.dictify():
AttributeError: 'int' object has no attribute 'dictify'

whereby you can see that the issue is with the local_schema that should not be an integer.

One thing I strongly recommend is to always use named parameters when you have so many parameters, as it makes it then easier to debug. I’ve also made the following changes to your program: * use with open() as f: to ensure proper closing of files,

  • change 1 (in application creation, which corresponds to OptIn) into NoOpC, which is more usual.
  • use the constant for the min fee
import base64

from algosdk.future import transaction
from algosdk import v2client
from algosdk.constants import min_txn_fee
import algosdk

algod_client = v2client.algod.AlgodClient("", "https://node.testnet.algoexplorerapi.io", {"User-Agent": "algosdk"})

params = algod_client.suggested_params()
params.fee = min_txn_fee
params.flat_fee = True

with open("testnet-contract.teal") as f:
    app_result = algod_client.compile(f.read())
    approval_program = base64.b64decode(app_result['result'])

with open("clear-state.teal") as f:
    app_result = algod_client.compile(f.read())
    clear_program = base64.b64decode(app_result['result'])

globalSchema = transaction.StateSchema(num_uints=0, num_byte_slices=0)
localSchema = transaction.StateSchema(num_uints=0, num_byte_slices=2)

createAppTxn = transaction.ApplicationCreateTxn(
    sender="RABQ4PRRWJDCMHCCCZ24YTLTT44MI4KFZEMRWGFEYESDRBIKD6QFPNNGBQ",
    sp=params,
    on_complete=transaction.OnComplete.NoOpOC,
    approval_program=approval_program,
    clear_program=clear_program,
    global_schema=globalSchema,
    local_schema=localSchema,
    foreign_assets=[75312966, 75313288]
)

stxn = createAppTxn.sign(algosdk.mnemonic.to_private_key(input("mnemonic:")))

print(f"{stxn.get_txid()} is the transaction ID. Will it work?")

algod_client.send_transaction(stxn)
1 Like

Thank you so much for your help, Fabrice!

Yeah, the app create txn works, however, my contract doesn’t. I’m not really sure why:

from pyteal import *

def teal():    
    
    # deposit 
    deposit = Seq(
        App.localPut(Txn.sender(), Bytes("AmountDeposited"), Gtxn[0].asset_amount()),
        App.localPut(Txn.sender(), Bytes("TimeDepositedUnix"), Global.latest_timestamp()),
        Return(Int(1))
    )

    def claim():
        return Seq([
                # then
                Assert((App.localGet(Txn.sender(), Bytes("TimeDepositedUnix")) - Global.latest_timestamp()) % Int(86400) > Int(0)),
                InnerTxnBuilder.Begin(),
                InnerTxnBuilder.SetFields(
                    {
                        TxnField.type_enum: TxnType.AssetTransfer,
                        TxnField.xfer_asset: Int(75312966),
                        TxnField.asset_amount: (App.localGet(Txn.sender(), Bytes("AmountDeposited")) * ((App.localGet(Txn.sender(), Bytes("TimeDepositedUnix")) - Global.latest_timestamp()) % Int(86400)) * Int(10)) / Int(10),
                        TxnField.asset_sender: Global.current_application_address(),
                        TxnField.asset_receiver: Txn.sender(),
                        TxnField.fee: Global.min_txn_fee()
                    }
                ),
                InnerTxnBuilder.Submit(),
                Return(Int(1))
            ])
    
    def withdraw():
        return Seq([
            InnerTxnBuilder.Begin(),
            InnerTxnBuilder.SetFields(
                {
                    TxnField.type_enum: TxnType.AssetTransfer,
                    TxnField.xfer_asset: Int(75313288),
                    TxnField.asset_amount: App.localGet(Txn.sender(), Bytes("AmountDeposited")),
                    TxnField.asset_sender: Global.current_application_address(),
                    TxnField.asset_receiver: Txn.sender(),
                    TxnField.fee: Global.min_txn_fee()
                }
            ),
            InnerTxnBuilder.Submit(),
            Return(Int(1))
        ])

    def optin():
        return Seq([
            Seq([
                InnerTxnBuilder.Begin(),
                InnerTxnBuilder.SetFields(
                    {
                        TxnField.type_enum: TxnType.AssetTransfer,
                        TxnField.xfer_asset: Int(75313288),
                        TxnField.asset_amount: Int(0),
                        TxnField.asset_sender: Global.current_application_address(),
                        TxnField.asset_receiver: Global.current_application_address(),
                        TxnField.fee: Global.min_txn_fee()
                    }
                ),
                InnerTxnBuilder.Submit(),
                Return(Int(1))
            ]),

            Seq([
                InnerTxnBuilder.Begin(),
                InnerTxnBuilder.SetFields(
                    {
                        TxnField.type_enum: TxnType.AssetTransfer,
                        TxnField.xfer_asset: Int(75312966),
                        TxnField.asset_amount: Int(0),
                        TxnField.asset_sender: Global.current_application_address(),
                        TxnField.asset_receiver: Global.current_application_address(),
                        TxnField.fee: Global.min_txn_fee()
                    }
                ),
                InnerTxnBuilder.Submit(),
                Return(Int(1))
            ])
        ])

    
    # Check what action is being completed
    conditions = Cond(
        # check if create txn
        [Global.current_application_id() == Int(0), Return(Int(1))],
        # is deposit transaction
        [Global.group_size() == Int(3), If(And(Gtxn[0].asset_amount() >= Int(1000000), Gtxn[0].xfer_asset() == Int(75313288), Gtxn[0].asset_receiver() == Global.current_application_address(), Gtxn[1].amount() == Global.min_txn_fee(), Gtxn[1].receiver() == Global.current_application_address()), deposit, Return(Int(0)))],
        # is claim transaction
        [Global.group_size() == Int(2), If(And(Gtxn[0].amount() == Global.min_txn_fee(), Gtxn[0].receiver() == Global.current_application_address(), Gtxn[1].type_enum() == TxnType.ApplicationCall), claim(), Return(Int(0)))],
        # is withdraw transaction
        [Txn.type_enum() == TxnType.ApplicationCall, withdraw()],
        # is contract asset opt-in transaction
        [And(Txn.sender() == Addr("RABQ4PRRWJDCMHCCCZ24YTLTT44MI4KFZEMRWGFEYESDRBIKD6QFPNNGBQ"), Txn.note() == Bytes("OptIn")), optin()],
        # check if update txn
        [Txn.on_completion() == OnComplete().UpdateApplication, Return(Int(0))],
        # check if optin
        [Txn.on_completion() == OnComplete().OptIn, Return(Int(1))],
        # check if optout
        [Txn.on_completion() == OnComplete().CloseOut, Return(Int(1))],
        # check if delete
        [Txn.on_completion() == OnComplete().DeleteApplication, Return(Int(1))],
        
    )
    

    return conditions


# compile
print(compileTeal(teal(), mode=Mode.Application, version=5))
save_to_file_bool = True

def save_to_file():
    with open('testnet-contract.teal', 'w') as f:
        f.write(compileTeal(teal(), mode=Mode.Application, version=5))
        f.close()

if save_to_file_bool == True:
    save_to_file()

I get the error logic eval error: cannot fetch key, XXXXXXXXXXXXXXXXXXXXXXXXXXXXX has not opted in to app 77002123. Details: pc=238, opcodes=txn Sender bytec_0 // "AmountDeposited" app_local_get even though my code has a condition to check if it’s an app create txn [Global.current_application_id() == Int(0), Return(Int(1))],

It’s because Global.current_application_id() always returns the application ID. It is never 0, even on application creation.

You want to check:

Txn.application_id() == Int(0)

See Build with Python - Algorand Developer Portal