#Smart Contract Fees
1 messages · Page 1 of 1 (latest)
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
ok so apparently I needed to add params.flat_fee = True now it is calculating correctly
@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
🫂
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 ?
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
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
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
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)```
wow thanks bro !
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:
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
Do you have your allow actions set?
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
The factory params doesn't have on complete
I don’t believe you can create and opt in simultaneously if that’s what you’re trying to do
I will do the abi action allow them try send opt in
my isssue was the directory path was too deep i put it in a shorter path and it get the file strange !
Nice!
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
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.