#Smart Contract Fees

1 messages · Page 1 of 1 (latest)

cold vigil
#

I have this function in a contract it works find but it is costing 1.116 Algo to run this function can anyone explain how the fees are calculated?

cold vigil
#

This is the set level code

#

i have an atomic transaction which opts in, sets level and does clawback, the outer transaction shows as 1.116 Algo and the inner transaction is 0

cold vigil
#

ok so apparently I needed to add params.flat_fee = True now it is calculating correctly

mortal solar
#

@cold vigil

Here's the an untested equivalent transfer sequence in algopy (PUYA) as an abimethod, which I think you'll love:

from algopy import ARC4Contract, itxn, LocalState, Txn, UInt64
from algopy.arc4 import abimethod


class contractInAlgopy(ARC4Contract):
    def __init__(self) -> None:
        self.user_level = LocalState(UInt64) 
        self.minimum_level = UInt64(3)

    @abimethod
    def transfer(
        self,
        amount: UInt64
    ) -> None:
        
        sender = Txn.sender
        receiver = Txn.accounts(1)

        assert Txn.num_app_args == 2
        assert Txn.num_accounts == 1
        assert self.user_level[sender] >= self.minimum_level
        assert self.user_level[receiver] >= self.minimum_level

        asset = Txn.assets(0)

        itxn.AssetTransfer(
            asset_receiver=receiver,
            asset_sender=sender,
            asset_amount=amount,
            xfer_asset=asset,
        ).submit()
       
#

Deploying in Python:

from algosdk.v2client.indexer import IndexerClient
from algokit_utils import ApplicationClient, OnUpdate
from algosdk.atomic_transaction_composer import AccountTransactionSigner
from pathlib import Path
from dotenv import load_dotenv, set_key
from algosdk.account import address_from_private_key
import os

load_dotenv()

algod_token = ''
algod_address = 'https://testnet-api.4160.nodely.dev'
algod_client = AlgodClient(algod_token, algod_address)

indexer_token = ''
indexer_address = 'https://testnet-idx.4160.nodely.dev'
indexer_client = IndexerClient(indexer_token, indexer_address)

app_spec = Path(__file__).parent / 'contractInAlgopy.arc32.json'

private_key = os.getenv('pk')
address = address_from_private_key(private_key)
signer = AccountTransactionSigner(private_key)

params = algod_client.suggested_params()

app_client = ApplicationClient(
    algod_client=algod_client,
    indexer_client=indexer_client,
    app_spec=app_spec,
    signer=signer,
    sender=address,
    suggested_params=params,
    creator=address,
)

app_id = app_client.deploy(on_update=OnUpdate.AppendApp).app.app_id

set_key('.env', 'app_id', str(app_id))```
#

Calling in Python:

from algokit_utils import ApplicationClient
from algosdk.atomic_transaction_composer import AccountTransactionSigner, AtomicTransactionComposer
from pathlib import Path
from dotenv import load_dotenv
from algosdk.account import address_from_private_key
from algosdk.encoding import encode_address
import os

load_dotenv()

algod_token = ''
algod_address = 'https://testnet-api.4160.nodely.dev'
algod_client = AlgodClient(algod_token, algod_address)

app_spec = Path(__file__).parent / 'contractInAlgopy.arc32.json'

private_key = os.getenv('pk')
address = address_from_private_key(private_key)
signer = AccountTransactionSigner(private_key)

params = algod_client.suggested_params()

app_id = int(os.getenv('app_id'))

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

atc = AtomicTransactionComposer()

user_address = encode_address(os.urandom(32))

params.flat_fee = True
params.fee = 2000

app_client.compose_call(
    atc,
    call_abi_method='transfer',
    amount=10,
    transaction_parameters={
        'suggested_params': params,
        'accounts': [user_address]
    }

)

results = atc.execute(algod_client, 2)

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

print(abi_results)
print(tx_ids)
#

I choose the hard way for deploying and calling without using algokit for testing, but I have heard algokit is easier

#

Pyteal will be unmaintained soon, so when cool new opcodes come out they may not be supported— I also took a while to crossover but trust me when I say you will not regret it

#

🫂

cold vigil
#

Thanks will be moving in this direction. Just a quick question i am using this as a custom logic for my ASA, I saw the example in which they used a stateful contract for verification and stateless contract to do the transfer. I am using a single stateful contract to do the verification and transfer, is there a reason or any benefit in using 2 contracts ?

mortal solar
#

Hmm I mean stateless contracts get a larger opcode reserve (20,000 vs 700)

#

But I don't see a reason why you would need to divide that logic otherwise unless you are creating some behemoth of a smart contract and it feels necessary

#

But stateless contracts are not my forte tbh

cold vigil
#

Hey @mortal solar i am trying to use your deployment code
app_client = ApplicationClient(
algod_client=algod_client,
app_spec=app_spec,
signer=signer,
sender=address,
suggested_params=params,
app_id=app_id
)

#

seems that ApplicationClient is not working

#

or depricated

#

keep getting a strange error file not found but the file is in the location

mortal solar
#

Do you mean something like this?

FileNotFoundError: [Errno 2] No such file or directory:

#

For deployment the app client requires creator and indexer parameters

#

Thats a native python issue if so, it has to do with the way with how you've structured your project directory. If you have your arc32.json in a different folder then you would use

Also apparently there is a create method that's a bit easier to work with IMHO

    algod_client=algod_client,
    app_spec=app_spec,
    signer=signer,
    sender=address,
    suggested_params=params,
)

tx_id = app_client.create().tx_id
tx_info = algod_client.pending_transaction_info(tx_id)
created_app_id = tx_info['application-index']

set_key('.env', 'app_id', str(created_app_id))```
#

Note that there's a new ApplicationClient though, I'm going to be testing it out today

mortal solar
#

pip install --upgrade algokit_utils

#

Much easier now:

#
from algosdk.atomic_transaction_composer import AccountTransactionSigner
from pathlib import Path
from dotenv import load_dotenv, set_key
from algosdk.account import address_from_private_key
import os

load_dotenv()

algod_client = AlgorandClient.testnet()

app_spec = (Path(__file__).parent / 'test.arc32.json').read_text()

private_key = os.getenv('pk')
address = address_from_private_key(private_key)
signer = AccountTransactionSigner(private_key)

app_factory_params = AppFactoryParams(
    algorand=algod_client,
    app_spec=app_spec,
    default_sender=address,
    default_signer=signer,
)

app_factory = AppFactory(app_factory_params)

app_client, deploy_result = app_factory.send.bare.create(
)

created_app_id = deploy_result.app_id

set_key('.env', 'app_id', str(created_app_id))```
#

And then this is calling a method:

from algosdk.atomic_transaction_composer import AccountTransactionSigner
from pathlib import Path
from dotenv import load_dotenv, set_key
from algosdk.account import address_from_private_key
import os

load_dotenv()

algod_client = AlgorandClient.testnet()

app_spec = (Path(__file__).parent / 'test.arc32.json').read_text()

app_id = int(os.getenv('app_id'))

private_key = os.getenv('pk')
address = address_from_private_key(private_key)
signer = AccountTransactionSigner(private_key)


app_client_params = AppClientParams(
    algorand=algod_client,
    app_spec=app_spec,
    app_id=app_id,
    default_sender=address,
    default_signer=signer
)

app_client = AppClient(app_client_params)

method_call_params = AppCallMethodCallParams(
    sender=address,
    app_id=app_client.app_id,
    method='test_1',
    args=[100_000]
)

result = app_client.send.call(method_call_params)
tx_ids = result.tx_ids
abi_results = result.abi_return

print(tx_ids)
print(abi_results)```
cold vigil
#

wow thanks bro !

mortal solar
#

There’s also a typed factory class to make sending transactions easier @cold vigil

Check this thread, it’s generated in the artifacts folder via algokit:

cold vigil
#

how do you automatically opt in using factory

#

I a, creating the app but when i try to opt in it say action rejected by approval program

mortal solar
#

There’s an allow actions parameter you can pass into the abimethod decorator within your contract

You will also need an on complete action in your transaction but I haven’t dived into this for the new sdk. Should be similar

It should be there somewhere though

cold vigil
#

The factory params doesn't have on complete

mortal solar
#

I don’t believe you can create and opt in simultaneously if that’s what you’re trying to do

cold vigil
#

I will do the abi action allow them try send opt in

cold vigil
mortal solar
#

Nice!

cold vigil
#

hey any idea why i can't change AssetOptInParams getting this error "dataclasses.FrozenInstanceError: cannot assign to field 'sender'", I was thinking i can just do this opt_in_params.sender = sender_addr or are certain fields immutable

mortal solar
#
            sender=address,
            signer=signer,
            asset_id=LP_POOL_ASSET
        )```
#
    LP_opt_in = algorand.create_transaction.asset_opt_in(
        AssetOptInParams(
            sender=address,
            signer=signer,
            asset_id=LP_POOL_ASSET
        )
    )
    print("Trying to reassign sender")
    LP_opt_in.sender = encode_address(os.urandom(32))
    print("Reassigned sender")
    app_group.add_transaction(LP_opt_in, signer)

Trying to reassign sender
Reassigned sender```
#

In this context it seems to be mutable.