diff --git a/_examples/__init__.py b/_examples/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/_examples/app_walkthrough.py b/_examples/app_walkthrough.py new file mode 100644 index 000000000..e221e4410 --- /dev/null +++ b/_examples/app_walkthrough.py @@ -0,0 +1,65 @@ +import json +from pyteal import ( + App, + Approve, + Assert, + BareCallActions, + Bytes, + Cond, + Global, + If, + Int, + Mode, + OnComplete, + OnCompleteAction, + Reject, + Return, + Router, + ScratchVar, + Seq, + TealType, + Txn, + compileTeal, +) + + +def step_1(): + # example: APP_EMPTY_LOGIC + def approval_program(): + program = Return(Int(1)) + # Mode.Application specifies that this is a smart contract + return compileTeal(program, Mode.Application, version=5) + + def clear_state_program(): + program = Return(Int(1)) + # Mode.Application specifies that this is a smart contract + return compileTeal(program, Mode.Application, version=5) + + print(approval_program()) + print(clear_state_program()) + # example: APP_EMPTY_LOGIC + + +def app_manual_router(): + # example: APP_MANUAL_ROUTER + def approval_program(): + + handle_creation = Approve() + handle_optin = Reject() + handle_closeout = Reject() + handle_update = Reject() + handle_delete = Reject() + handle_noop = Reject() + + program = Cond( + [Txn.application_id() == Int(0), handle_creation], + [Txn.on_completion() == OnComplete.OptIn, handle_optin], + [Txn.on_completion() == OnComplete.CloseOut, handle_closeout], + [Txn.on_completion() == OnComplete.UpdateApplication, handle_update], + [Txn.on_completion() == OnComplete.DeleteApplication, handle_delete], + [Txn.on_completion() == OnComplete.NoOp, handle_noop], + ) + return program + + return compileTeal(approval_program(), Mode.Application, version=5) + # example: APP_MANUAL_ROUTER diff --git a/_examples/application.py b/_examples/application.py new file mode 100644 index 000000000..90c237058 --- /dev/null +++ b/_examples/application.py @@ -0,0 +1,59 @@ +from pyteal import ( + App, + Bytes, + Cond, + Err, + Global, + Int, + Mode, + OnComplete, + Return, + Seq, + Txn, + compileTeal, +) + + +def opted_in(): + # example: APPL_CHECK_OPTEDIN + program = App.optedIn(Int(0), Txn.application_id()) + print(compileTeal(program, Mode.Application)) + # example: APPL_CHECK_OPTEDIN + + +def global_ts(): + # example: GLOBAL_LATEST_TIMESTAMP + program = Global.latest_timestamp() >= App.globalGet(Bytes("StartDate")) + print(compileTeal(program, Mode.Application)) + # example: GLOBAL_LATEST_TIMESTAMP + + +def boilerplate(): + # example: BOILERPLATE + # Handle each possible OnCompletion type. We don't have to worry about + # handling ClearState, because the ClearStateProgram will execute in that + # case, not the ApprovalProgram. + def approval_program(): + handle_noop = Seq([Return(Int(1))]) + + handle_optin = Seq([Return(Int(1))]) + + handle_closeout = Seq([Return(Int(1))]) + + handle_updateapp = Err() + + handle_deleteapp = Err() + + program = Cond( + [Txn.on_completion() == OnComplete.NoOp, handle_noop], + [Txn.on_completion() == OnComplete.OptIn, handle_optin], + [Txn.on_completion() == OnComplete.CloseOut, handle_closeout], + [Txn.on_completion() == OnComplete.UpdateApplication, handle_updateapp], + [Txn.on_completion() == OnComplete.DeleteApplication, handle_deleteapp], + ) + return program + + with open("boilerplate_approval_pyteal.teal", "w") as f: + compiled = compileTeal(approval_program(), Mode.Application, version=5) + f.write(compiled) + # example: BOILERPLATE diff --git a/_examples/assets.py b/_examples/assets.py new file mode 100644 index 000000000..aa35ea7de --- /dev/null +++ b/_examples/assets.py @@ -0,0 +1,18 @@ +from pyteal import AssetHolding, AssetParam, If, Int, Mode, Return, Seq, compileTeal + + +def asset_balance(): + # example: APPL_ASSET_BALANCE + asset_balance = AssetHolding.balance(Int(0), Int(2)) + program = Seq( + asset_balance, If(asset_balance.hasValue(), Return(Int(1)), Return(Int(0))) + ) + print(compileTeal(program, Mode.Application)) + # example: APPL_ASSET_BALANCE + + +def asset_param(): + # example: APPL_ASSET_PARAM + program = AssetParam.total(Int(0)) + print(compileTeal(program, Mode.Application)) + # example: APPL_ASSET_PARAM diff --git a/_examples/box.py b/_examples/box.py new file mode 100644 index 000000000..89ae1c06e --- /dev/null +++ b/_examples/box.py @@ -0,0 +1,71 @@ +from pyteal import App, Assert, Bytes, Int, Itob, ScratchVar, Seq, TealType + + +def box_create(): + return Seq( + # example: BOX_CREATE + # ... + # box created with box_create, size 100 bytes + App.box_create(Bytes("MyKey"), Int(100)), + # OR box created with box_put, size is implicitly the + # length of bytes written + App.box_put(Bytes("MyKey"), Bytes("My data values")) + # ... + # example: BOX_CREATE + ) + + +def box_get(): + return Seq( + App.box_put(Bytes("MyKey"), Itob(Int(123))), + # example: BOX_GET + boxval := App.box_get(Bytes("MyKey")), + Assert(boxval.hasValue()), + # do something with boxval.value() + # ... + # example: BOX_GET + ) + + +def box_extract(): + return Seq( + (scratchVar := ScratchVar(TealType.bytes)).store(Bytes("")), + # example: BOX_EXTRACT + # ... + App.box_put( + Bytes("BoxA"), Bytes("this is a test of a very very very very long string") + ), + scratchVar.store(App.box_extract(Bytes("BoxA"), Int(5), Int(9))), + Assert(scratchVar.load() == Bytes("is a test")) + # ... + # example: BOX_EXTRACT + ) + + +def box_len(): + return Seq( + # example: BOX_LEN + App.box_put( + Bytes("BoxA"), Bytes("this is a test of a very very very very long string") + ), + # box length is equal to the size of the box created + # not a measure of how many bytes have been _written_ + # by the smart contract + bt := App.box_length(Bytes("BoxA")), + Assert(bt.hasValue()), + Assert(bt.value() == 51), + # example: BOX_LEN + ) + + +def box_delete(): + return Seq( + # example: BOX_DELETE + App.box_put( + Bytes("BoxA"), Bytes("this is a test of a very very very very long string") + ), + # Box delete returns a 1/0 on the stack + # depending on if it was successful + Assert(App.box_delete(Bytes("BoxA"))), + # example: BOX_DELETE + ) diff --git a/_examples/itxn.py b/_examples/itxn.py new file mode 100644 index 000000000..4aa5f012d --- /dev/null +++ b/_examples/itxn.py @@ -0,0 +1,218 @@ +from pyteal import ( + AppParam, + Assert, + Bytes, + Global, + InnerTxnBuilder, + Int, + OnComplete, + Seq, + Subroutine, + TealType, + Txn, + TxnField, + TxnType, +) + + +@Subroutine(TealType.none) +def send_payment(): + + return Seq( + # example: ITXN_PAYMENT + # ... + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields( + { + TxnField.type_enum: TxnType.Payment, + TxnField.amount: Int(5000), + TxnField.receiver: Txn.sender(), + } + ), + InnerTxnBuilder.Submit(), + # ... + # The `Sender` for the above is implied to be Global.current_application_address(). + # If a different sender is needed, it'd have to be an account that has been rekeyed to + # the application address. + # example: ITXN_PAYMENT + ) + + +@Subroutine(TealType.none) +def send_asset(): + return Seq( + # example: ITXN_ASSET_TRANSFER + # ... + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields( + { + TxnField.type_enum: TxnType.AssetTransfer, + TxnField.asset_amount: Int(5000), + TxnField.asset_receiver: Txn.sender(), + TxnField.xfer_asset: Txn.assets[0], + } + ), + # ... + InnerTxnBuilder.Submit(), + # example: ITXN_ASSET_TRANSFER + ) + + +@Subroutine(TealType.none) +def asset_freeze(): + return Seq( + # example: ITXN_ASSET_FREEZE + # ... + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields( + { + TxnField.type_enum: TxnType.AssetFreeze, + TxnField.freeze_asset: Txn.assets[0], + TxnField.freeze_asset_account: Txn.accounts[1], + TxnField.freeze_asset_frozen: Int(1), + } + ), + InnerTxnBuilder.Submit(), + # ... + # example: ITXN_ASSET_FREEZE + ) + + +@Subroutine(TealType.none) +def asset_revoke(): + return Seq( + # example: ITXN_ASSET_REVOKE + # ... + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields( + { + TxnField.type_enum: TxnType.AssetTransfer, + TxnField.asset_receiver: Global.current_application_address(), + # AssetSender is _only_ used in the case of clawback + # Sender is implied to be current_application_address + TxnField.asset_sender: Txn.accounts[1], + TxnField.asset_amount: Int(1000), + } + ), + InnerTxnBuilder.Submit(), + # ... + # example: ITXN_ASSET_REVOKE + ) + + +@Subroutine(TealType.none) +def asset_create(): + return Seq( + # example: ITXN_ASSET_CREATE + # ... + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields( + { + TxnField.type_enum: TxnType.AssetConfig, + TxnField.config_asset_total: Int(1000000), + TxnField.config_asset_decimals: Int(3), + TxnField.config_asset_unit_name: Bytes("oz"), + TxnField.config_asset_name: Bytes("Gold"), + TxnField.config_asset_url: Bytes("https://gold.rush"), + TxnField.config_asset_manager: Global.current_application_address(), + TxnField.config_asset_reserve: Global.current_application_address(), + TxnField.config_asset_freeze: Global.current_application_address(), + TxnField.config_asset_clawback: Global.current_application_address(), + } + ), + InnerTxnBuilder.Submit(), + # ... + # example: ITXN_ASSET_CREATE + ) + + +@Subroutine(TealType.none) +def asset_config(): + return Seq( + # example: ITXN_ASSET_CONFIG + # ... + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields( + { + TxnField.type_enum: TxnType.AssetConfig, + TxnField.config_asset: Txn.assets[0], + TxnField.config_asset_manager: Txn.sender(), + TxnField.config_asset_reserve: Txn.sender(), + TxnField.config_asset_freeze: Txn.sender(), + TxnField.config_asset_clawback: Txn.sender(), + } + ), + InnerTxnBuilder.Submit(), + # ... + # example: ITXN_ASSET_CONFIG + ) + + +@Subroutine(TealType.none) +def asset_destroy(): + return Seq( + # example: ITXN_ASSET_DESTROY + # ... + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields( + { + TxnField.type_enum: TxnType.AssetConfig, + TxnField.config_asset: Txn.assets[0], + } + ), + InnerTxnBuilder.Submit(), + # ... + # example: ITXN_ASSET_DESTROY + ) + + +@Subroutine(TealType.none) +def grouped_itxn(): + return Seq( + # example: GROUPED_ITXN + # This returns a `MaybeValue`, see pyteal docs + addr := AppParam.address(Int(1234)), + Assert(addr.hasValue()), + # ... + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields( + { + TxnField.type_enum: TxnType.Payment, + TxnField.receiver: addr.value(), + TxnField.amount: Int(1000000), + } + ), + InnerTxnBuilder.Next(), # This indicates we're moving to constructing the next txn in the group + InnerTxnBuilder.SetFields( + { + TxnField.type_enum: TxnType.ApplicationCall, + TxnField.application_id: Int(1234), + TxnField.on_completion: OnComplete.NoOp, + # Note this is _not_ using the ABI to call the + # method in the other app + TxnField.application_args: [Bytes("buy")], + } + ), + InnerTxnBuilder.Submit(), + # ... + # example: GROUPED_ITXN + ) + + +@Subroutine(TealType.none) +def inner_c2c(): + return Seq( + # example: ITXN_C2C + # ... + InnerTxnBuilder.Begin(), + InnerTxnBuilder.SetFields( + { + TxnField.type_enum: TxnType.ApplicationCall, + TxnField.application_id: Int(1234), + TxnField.on_completion: OnComplete.NoOp, + } + ), + InnerTxnBuilder.Submit(), + # ... + # example: ITXN_C2C + ) diff --git a/_examples/kv_state.py b/_examples/kv_state.py new file mode 100644 index 000000000..31008e63b --- /dev/null +++ b/_examples/kv_state.py @@ -0,0 +1,79 @@ +from pyteal import App, Bytes, If, Int, Mode, Return, Seq, Txn, compileTeal + + +def write_to_global_state(): + # example: WRITE_GLOBAL_STATE + program = App.globalPut(Bytes("Mykey"), Int(50)) + print(compileTeal(program, Mode.Application)) + # example: WRITE_GLOBAL_STATE + + +def write_sender_local_state(): + # example: WRITE_SENDER_LOCAL_STATE + program = App.localPut(Int(0), Bytes("MyLocalKey"), Int(50)) + print(compileTeal(program, Mode.Application)) + # example: WRITE_SENDER_LOCAL_STATE + + +def write_other_local_state(): + # example: WRITE_OTHER_LOCAL_STATE + program = App.localPut(Int(2), Bytes("MyLocalKey"), Int(50)) + print(compileTeal(program, Mode.Application)) + # example: WRITE_OTHER_LOCAL_STATE + + +def read_global_state(): + # example: READ_GLOBAL_STATE + program = App.globalGet(Bytes("MyGlobalKey")) + print(compileTeal(program, Mode.Application)) + # example: READ_GLOBAL_STATE + + +def read_local_state(): + # example: READ_LOCAL_STATE + program = App.localGet(Int(0), Bytes("MyLocalKey")) + print(compileTeal(program, Mode.Application)) + # example: READ_LOCAL_STATE + + +def read_sender_local_state_external(): + # example: READ_SENDER_LOCAL_STATE_EX + program = App.localGetEx(Int(0), Txn.application_id(), Bytes("MyAmountGiven")) + print(compileTeal(program, Mode.Application)) + # example: READ_SENDER_LOCAL_STATE_EX + + +def read_local_state_external(): + # example: READ_LOCAL_STATE_EX + get_amount_given = App.localGetEx( + Int(0), Txn.application_id(), Bytes("MyAmountGiven") + ) + + # Change these to appropriate logic for new and previous givers. + new_giver_logic = Seq(Return(Int(1))) + + previous_giver_logic = Seq(Return(Int(1))) + + program = Seq( + get_amount_given, + If(get_amount_given.hasValue(), previous_giver_logic, new_giver_logic), + ) + + print(compileTeal(program, Mode.Application)) + # example: READ_LOCAL_STATE_EX + + +def read_global_state_external(): + # example: READ_GLOBAL_STATE_EX + get_global_key = App.globalGetEx(Int(0), Bytes("MyGlobalKey")) + + # Update with appropriate logic for use case + increment_existing = Seq(Return(Int(1))) + + program = Seq( + get_global_key, + If(get_global_key.hasValue(), increment_existing, Return(Int(1))), + ) + + print(compileTeal(program, Mode.Application)) + # example: READ_GLOBAL_STATE_EX diff --git a/_examples/lsig.py b/_examples/lsig.py new file mode 100644 index 000000000..f30031749 --- /dev/null +++ b/_examples/lsig.py @@ -0,0 +1,132 @@ +import base64 +from typing import Tuple +from algosdk import mnemonic, transaction, account +from algosdk.v2client import algod +from pyteal import * + +# example: LSIG_SIMPLE_ESCROW +def donation_escrow(benefactor): + Fee = Int(1000) + + # Only the benefactor account can withdraw from this escrow + program = And( + Txn.type_enum() == TxnType.Payment, + Txn.fee() <= Fee, + Txn.receiver() == Addr(benefactor), + Global.group_size() == Int(1), + Txn.rekey_to() == Global.zero_address(), + ) + + # Mode.Signature specifies that this is a smart signature + return compileTeal(program, Mode.Signature, version=5) + + +# example: LSIG_SIMPLE_ESCROW + +# example: LSIG_SIMPLE_ESCROW_INIT +rando_addr, _ = account.generate_account() +teal_program = donation_escrow(rando_addr) +# example: LSIG_SIMPLE_ESCROW_INIT + + +# example: LSIG_SIMPLE_SETUP +# user declared account mnemonics +benefactor_mnemonic = "REPLACE WITH YOUR OWN MNEMONIC" +sender_mnemonic = "REPLACE WITH YOUR OWN MNEMONIC" + + +# user declared algod connection parameters. Node must have EnableDeveloperAPI set to true in its config +algod_address = "http://localhost:4001" +algod_token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +# example: LSIG_SIMPLE_SETUP + +# example: LSIG_SIMPLE_HELPERS +# helper function to compile program source +def compile_smart_signature( + client: algod.AlgodClient, source_code: str +) -> Tuple[str, str]: + compile_response = client.compile(source_code) + return compile_response["result"], compile_response["hash"] + + +# example: LSIG_SIMPLE_HELPERS + + +# example: LSIG_SIMPLE_SEED_PAYMENT +def payment_transaction( + creator_mnemonic: str, amt: int, rcv: str, algod_client: algod.AlgodClient +) -> dict: + creator_pk = mnemonic.to_private_key(creator_mnemonic) + creator_address = account.address_from_private_key(creator_pk) + + params = algod_client.suggested_params() + unsigned_txn = transaction.PaymentTxn(creator_address, params, rcv, amt) + signed = unsigned_txn.sign(creator_pk) + + txid = algod_client.send_transaction(signed) + pmtx = transaction.wait_for_confirmation(algod_client, txid, 5) + return pmtx + + +# example: LSIG_SIMPLE_SEED_PAYMENT + + +# example: LSIG_SIMPLE_WITHDRAW +def lsig_payment_txn( + encoded_program: str, amt: int, rcv: str, algod_client: algod.AlgodClient +): + # Create an lsig object using the compiled, b64 encoded program + program = base64.b64decode(encoded_program) + lsig = transaction.LogicSigAccount(program) + + # Create transaction with the lsig address as the sender + params = algod_client.suggested_params() + unsigned_txn = transaction.PaymentTxn(lsig.address(), params, rcv, amt) + + # sign the transaction using the logic + stxn = transaction.LogicSigTransaction(unsigned_txn, lsig) + tx_id = algod_client.send_transaction(stxn) + pmtx = transaction.wait_for_confirmation(algod_client, tx_id, 10) + return pmtx + + +# example: LSIG_SIMPLE_WITHDRAW + + +# example: LSIG_SIMPLE_USAGE +def main(): + # initialize an algodClient + algod_client = algod.AlgodClient(algod_token, algod_address) + + # define private keys + private_key = mnemonic.to_private_key(benefactor_mnemonic) + receiver_public_key = account.address_from_private_key(private_key) + + print("Compiling Donation Smart Signature......") + + stateless_program_teal = donation_escrow(receiver_public_key) + escrow_result, escrow_address = compile_smart_signature( + algod_client, stateless_program_teal + ) + + print("Program:", escrow_result) + print("LSig Address: ", escrow_address) + + print("Activating Donation Smart Signature......") + + # Activate escrow contract by sending 2 algo and 1000 microalgo for transaction fee from creator + amt = 2001000 + payment_transaction(sender_mnemonic, amt, escrow_address, algod_client) + + print("Withdraw from Donation Smart Signature......") + + # Withdraws 1 ALGO from smart signature using logic signature. + withdrawal_amt = 1000000 + lsig_payment_txn(escrow_result, withdrawal_amt, receiver_public_key, algod_client) + + +# example: LSIG_SIMPLE_USAGE + + +if __name__ == "__main__": + main() diff --git a/_examples/simple_router.py b/_examples/simple_router.py new file mode 100644 index 000000000..1cccd4108 --- /dev/null +++ b/_examples/simple_router.py @@ -0,0 +1,311 @@ +import base64 +import json +from typing import List, Dict, Any +from algosdk import transaction +from algosdk import account, mnemonic +from algosdk.atomic_transaction_composer import ( + AccountTransactionSigner, + AtomicTransactionComposer, +) +from algosdk.v2client import algod +from pyteal import ( + App, + Approve, + Assert, + BareCallActions, + Bytes, + Global, + If, + Int, + OnCompleteAction, + Reject, + Router, + ScratchVar, + Seq, + TealType, +) + +# example: APP_ROUTER_SETUP +# user declared account mnemonics +creator_mnemonic = "TODO: Add your mnemonic" +# user declared algod connection parameters. Node must have EnableDeveloperAPI set to true in its config +algod_address = "http://localhost:4001" +algod_token = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +# example: APP_ROUTER_SETUP + +# example: APP_ROUTER_HELPERS +# helper function to compile program source +def compile_program(client: algod.AlgodClient, source_code: str): + compile_response = client.compile(source_code) + return base64.b64decode(compile_response["result"]) + + +# helper function that converts a mnemonic passphrase into a private signing key +def get_private_key_from_mnemonic(mn: str): + private_key = mnemonic.to_private_key(mn) + return private_key + + +# helper function that formats global state for printing +def format_state(state: List[Dict[str, Any]]): + formatted = {} + for item in state: + key = item["key"] + value = item["value"] + formatted_key = base64.b64decode(key).decode("utf-8") + if value["type"] == 1: + # byte string + if formatted_key == "voted": + formatted_value = base64.b64decode(value["bytes"]).decode("utf-8") + else: + formatted_value = value["bytes"] + formatted[formatted_key] = formatted_value + else: + # integer + formatted[formatted_key] = value["uint"] + return formatted + + +# helper function to read app global state +def read_global_state(client: algod.AlgodClient, app_id: int): + app = client.application_info(app_id) + global_state = ( + app["params"]["global-state"] if "global-state" in app["params"] else [] + ) + return format_state(global_state) + + +# example: APP_ROUTER_HELPERS + + +# example: APP_ROUTER_BASIC +## Contract logic +count_key = Bytes("Count") + +# Create an expression to store 0 in the `Count` global variable and return 1 +handle_creation = Seq(App.globalPut(count_key, Int(0)), Approve()) + +# Main router class +router = Router( + # Name of the contract + "my-first-router", + # What to do for each on-complete type when no arguments are passed (bare call) + BareCallActions( + # On create only, just approve + no_op=OnCompleteAction.create_only(handle_creation), + # Always let creator update/delete but only by the creator of this contract + update_application=OnCompleteAction.always(Reject()), + delete_application=OnCompleteAction.always(Reject()), + # No local state, don't bother handling it. + close_out=OnCompleteAction.never(), + opt_in=OnCompleteAction.never(), + ), +) +# example: APP_ROUTER_BASIC + +# example: APP_ROUTER_COMPILE +# Compile the program +approval_program, clear_program, contract = router.compile_program(version=6) + +# print out the results +print(approval_program) +print(clear_program) + +print(json.dumps(contract.dictify())) +# example: APP_ROUTER_COMPILE + +# example: APP_ROUTER_METHODS +@router.method +def increment(): + # Declare the ScratchVar as a Python variable _outside_ the expression tree + scratchCount = ScratchVar(TealType.uint64) + return Seq( + Assert(Global.group_size() == Int(1)), + # The initial `store` for the scratch var sets the value to + # whatever is in the `Count` global state variable + scratchCount.store(App.globalGet(count_key)), + # Increment the value stored in the scratch var + # and update the global state variable + App.globalPut(count_key, scratchCount.load() + Int(1)), + ) + + +@router.method +def decrement(): + # Declare the ScratchVar as a Python variable _outside_ the expression tree + scratchCount = ScratchVar(TealType.uint64) + return Seq( + Assert(Global.group_size() == Int(1)), + # The initial `store` for the scratch var sets the value to + # whatever is in the `Count` global state variable + scratchCount.store(App.globalGet(count_key)), + # Check if the value would be negative by decrementing + If( + scratchCount.load() > Int(0), + # If the value is > 0, decrement the value stored + # in the scratch var and update the global state variable + App.globalPut(count_key, scratchCount.load() - Int(1)), + ), + ) + + +# example: APP_ROUTER_METHODS + + +# example: APP_ROUTER_CREATOR +# create new application +def create_app( + client: algod.AlgodClient, + private_key: str, + approval_program: bytes, + clear_program: bytes, + global_schema: transaction.StateSchema, + local_schema: transaction.StateSchema, +) -> int: + # define sender as creator + sender = account.address_from_private_key(private_key) + + # declare on_complete as NoOp + on_complete = transaction.OnComplete.NoOpOC.real + + # get node suggested parameters + params = client.suggested_params() + + # create unsigned transaction + txn = transaction.ApplicationCreateTxn( + sender, + params, + on_complete, + approval_program, + clear_program, + global_schema, + local_schema, + ) + + # sign transaction + signed_txn = txn.sign(private_key) + tx_id = signed_txn.transaction.get_txid() + + # send transaction + client.send_transactions([signed_txn]) + + # wait for confirmation + try: + transaction_response = transaction.wait_for_confirmation(client, tx_id, 5) + print("TXID: ", tx_id) + print( + "Result confirmed in round: {}".format( + transaction_response["confirmed-round"] + ) + ) + except Exception as err: + print(err) + return 0 + + # display results + transaction_response = client.pending_transaction_info(tx_id) + app_id = transaction_response["application-index"] + print("Created new app-id:", app_id) + + return app_id + + +# example: APP_ROUTER_CREATOR + + +# example: APP_ROUTER_CALLER +# call application +def call_app(client, private_key, index, contract): + # get sender address + sender = account.address_from_private_key(private_key) + # create a Signer object + signer = AccountTransactionSigner(private_key) + + # get node suggested parameters + sp = client.suggested_params() + + # Create an instance of AtomicTransactionComposer + atc = AtomicTransactionComposer() + atc.add_method_call( + app_id=index, + method=contract.get_method_by_name("increment"), + sender=sender, + sp=sp, + signer=signer, + method_args=[], # No method args needed here + ) + + # send transaction + results = atc.execute(client, 2) + + # wait for confirmation + print("TXID: ", results.tx_ids[0]) + print("Result confirmed in round: {}".format(results.confirmed_round)) + + +# example: APP_ROUTER_CALLER + + +def main(): + # example: APP_ROUTER_DEPLOY + # initialize an algodClient + algod_client = algod.AlgodClient(algod_token, algod_address) + + # define private keys + creator_private_key = get_private_key_from_mnemonic(creator_mnemonic) + + # declare application state storage (immutable) + local_ints = 0 + local_bytes = 0 + global_ints = 1 + global_bytes = 0 + global_schema = transaction.StateSchema(global_ints, global_bytes) + local_schema = transaction.StateSchema(local_ints, local_bytes) + + # Compile the program + approval_program, clear_program, contract = router.compile_program(version=6) + + with open("./approval.teal", "w") as f: + f.write(approval_program) + + with open("./clear.teal", "w") as f: + f.write(clear_program) + + with open("./contract.json", "w") as f: + import json + + f.write(json.dumps(contract.dictify())) + + # compile program to binary + approval_program_compiled = compile_program(algod_client, approval_program) + + # compile program to binary + clear_state_program_compiled = compile_program(algod_client, clear_program) + + print("Deploying Counter application......") + + # create new application + app_id = create_app( + algod_client, + creator_private_key, + approval_program_compiled, + clear_state_program_compiled, + global_schema, + local_schema, + ) + + # read global state of application + print("Global state:", read_global_state(algod_client, app_id)) + # example: APP_ROUTER_DEPLOY + + # example: APP_ROUTER_CALL + print("Calling Counter application......") + call_app(algod_client, creator_private_key, app_id, contract) + + # read global state of application + print("Global state:", read_global_state(algod_client, app_id)) + # example: APP_ROUTER_CALL + + +if __name__ == "__main__": + main() diff --git a/_examples/txn.py b/_examples/txn.py new file mode 100644 index 000000000..b29ba63bc --- /dev/null +++ b/_examples/txn.py @@ -0,0 +1,113 @@ +from pyteal import ( + Approve, + Assert, + Btoi, + Bytes, + Global, + Gtxn, + If, + Int, + Mode, + OnComplete, + Reject, + Txn, + TxnType, + compileTeal, +) + + +def on_complete(): + # example: TXN_ONCOMPLETE + program = OnComplete.NoOp == Txn.on_completion() + print(compileTeal(program, Mode.Application)) + # example: TXN_ONCOMPLETE + + +def app_args(): + # example: TXN_APP_ARGS + program = Txn.application_args[1] == Bytes("claim") + print(compileTeal(program, Mode.Application)) + # example: TXN_APP_ARGS + + +def num_app_args(): + # example: TXN_NUM_APP_ARGS + program = Txn.application_args.length() == Int(4) + print(compileTeal(program, Mode.Application)) + # example: TXN_NUM_APP_ARGS + + +def arg_to_int(): + # example: TXN_APP_ARG_TO_INT + program = Btoi(Txn.application_args[0]) + print(compileTeal(program, Mode.Application)) + # example: TXN_APP_ARG_TO_INT + + +def txn_amt(): + # example: TXN_AMOUNT + program = Txn.amount() + print(compileTeal(program, Mode.Application)) + # example: TXN_AMOUNT + + +def group_size(): + # example: TXN_GROUP_SIZE + program = Global.group_size() == Int(2) + print(compileTeal(program, Mode.Application)) + # example: TXN_GROUP_SIZE + + +def gtxn_type_enum(): + # example: GTXN_TYPE_ENUM + program = Gtxn[1].type_enum() == TxnType.Payment + print(compileTeal(program, Mode.Application)) + # example: GTXN_TYPE_ENUM + + +def gtxn_app_args(): + # example: GTXN_APP_ARGS + program = Gtxn[Txn.group_index() - Int(1)].application_args[0] + print(compileTeal(program, Mode.Application)) + # example: GTXN_APP_ARGS + + +def app_call(): + # example: APPL_CALL + # this would approve _ANY_ transaction that has its + # first app arg set to the byte string "myparam" + # and reject all others + program = If(Bytes("myparm") == Txn.application_args[0], Approve(), Reject()) + print(compileTeal(program, Mode.Application)) + # example: APPL_CALL + + +def app_update(): + # example: APPL_UPDATE + program = Assert( + Txn.on_completion() == OnComplete.UpdateApplication, + Global.creator_address() == Txn.sender(), + ) + print(compileTeal(program, Mode.Application)) + # example: APPL_UPDATE + + +def app_update_reject(): + # example: APPL_UPDATE_REJECT + program = If( + OnComplete.UpdateApplication == Txn.on_completion(), + Reject(), + # placeholder, update with actual logic + Approve(), + ) + print(compileTeal(program, Mode.Application)) + # example: APPL_UPDATE_REJECT + + +def is_opt_in(): + # example: APPL_OPTIN + # this would reject _ANY_ transaction that isn't an opt-in + # and approve _ANY_ transaction that is an opt-in + program = If(OnComplete.OptIn == Txn.on_completion(), Approve(), Reject()) + print(compileTeal(program, Mode.Application)) + # example: APPL_OPTIN