PyTeal Examples

Here are some additional PyTeal example programs:

Signature Mode

Atomic Swap

Atomic Swap allows the transfer of Algos from a buyer to a seller in exchange for a good or service. This is done using a Hashed Time Locked Contract. In this scheme, the buyer and funds a TEAL account with the sale price. The buyer also picks a secret value and encodes a secure hash of this value in the TEAL program. The TEAL program will transfer its balance to the seller if the seller is able to provide the secret value that corresponds to the hash in the program. When the seller renders the good or service to the buyer, the buyer discloses the secret from the program. The seller can immediately verify the secret and withdraw the payment.

# This example is provided for informational purposes only and has not been audited for security.

from pyteal import *

"""Atomic Swap"""

alice = Addr("6ZHGHH5Z5CTPCF5WCESXMGRSVK7QJETR63M3NY5FJCUYDHO57VTCMJOBGY")
bob = Addr("7Z5PWO2C6LFNQFGHWKSK5H47IQP5OJW2M3HA2QPXTY3WTNP5NU2MHBW27M")
secret = Bytes("base32", "23232323232323")
timeout = 3000

def htlc(tmpl_seller=alice,
         tmpl_buyer=bob,
         tmpl_fee=1000,
         tmpl_secret=secret,
         tmpl_hash_fn=Sha256,
         tmpl_timeout=timeout):
    
    fee_cond = Txn.fee() < Int(tmpl_fee)
    type_cond = Txn.type_enum() == TxnType.Payment

    recv_cond = And(
        Txn.close_remainder_to() == Global.zero_address(),
        Txn.receiver() == tmpl_seller,
        tmpl_hash_fn(Arg(0)) == tmpl_secret
    )
    
    esc_cond = And(
        Txn.close_remainder_to() == Global.zero_address(),
        Txn.receiver() == tmpl_buyer,
        Txn.first_valid() > Int(tmpl_timeout)
    )

    return And(
        fee_cond,
        type_cond,
        Or(recv_cond, esc_cond)
    )

if __name__ == "__main__":
    print(compileTeal(htlc(), Mode.Signature))

Split Payment

Split Payment splits payment between tmpl_rcv1 and tmpl_rcv2 on the ratio of tmpl_ratn / tmpl_ratd.

# This example is provided for informational purposes only and has not been audited for security.

from pyteal import *

"""Split Payment"""

tmpl_fee = Int(1000)
tmpl_rcv1 = Addr("6ZHGHH5Z5CTPCF5WCESXMGRSVK7QJETR63M3NY5FJCUYDHO57VTCMJOBGY")
tmpl_rcv2 = Addr("7Z5PWO2C6LFNQFGHWKSK5H47IQP5OJW2M3HA2QPXTY3WTNP5NU2MHBW27M")
tmpl_own = Addr("5MK5NGBRT5RL6IGUSYDIX5P7TNNZKRVXKT6FGVI6UVK6IZAWTYQGE4RZIQ")
tmpl_ratn  = Int(1)
tmpl_ratd = Int(3)
tmpl_min_pay = Int(1000)
tmpl_timeout = Int(3000)

def split(tmpl_fee=tmpl_fee,
             tmpl_rcv1=tmpl_rcv1,
             tmpl_rcv2=tmpl_rcv2,
             tmpl_own=tmpl_own,
             tmpl_ratn=tmpl_ratn,
             tmpl_ratd=tmpl_ratd,
             tmpl_min_pay=tmpl_min_pay,
             tmpl_timeout=tmpl_timeout):
    
    split_core = (Txn.type_enum() == TxnType.Payment).And(Txn.fee() < tmpl_fee)

    split_transfer = And(
        Gtxn[0].sender() == Gtxn[1].sender(),
        Txn.close_remainder_to() == Global.zero_address(),
        Gtxn[0].receiver() == tmpl_rcv1,
        Gtxn[1].receiver() == tmpl_rcv2,
        Gtxn[0].amount() == ((Gtxn[0].amount() + Gtxn[1].amount()) * tmpl_ratn) / tmpl_ratd,
        Gtxn[0].amount() == tmpl_min_pay
    )

    split_close = And(
        Txn.close_remainder_to() == tmpl_own,
        Txn.receiver() == Global.zero_address(),
        Txn.amount() == Int(0),
        Txn.first_valid() > tmpl_timeout
    )

    split = And(
        split_core,
        If(Global.group_size() == Int(2),
            split_transfer,
            split_close
        )
    )
    
    return split

if __name__ == "__main__":
    print(compileTeal(split, Mode.Signature))

Periodic Payment

Periodic Payment allows some account to execute periodic withdrawal of funds. This PyTeal program creates an contract account that allows tmpl_rcv to withdraw tmpl_amt every tmpl_period rounds for tmpl_dur after every multiple of tmpl_period.

After tmpl_timeout, all remaining funds in the escrow are available to tmpl_rcv.

# This example is provided for informational purposes only and has not been audited for security.

from pyteal import *

"""Periodic Payment"""

tmpl_fee = Int(1000)
tmpl_period = Int(50)
tmpl_dur = Int(5000)
tmpl_lease = Bytes("base64", "023sdDE2")
tmpl_amt = Int(2000)
tmpl_rcv = Addr("6ZHGHH5Z5CTPCF5WCESXMGRSVK7QJETR63M3NY5FJCUYDHO57VTCMJOBGY")
tmpl_timeout = Int(30000)

def periodic_payment(tmpl_fee=tmpl_fee,
                     tmpl_period=tmpl_period,
                     tmpl_dur=tmpl_dur,
                     tmpl_lease=tmpl_lease,
                     tmpl_amt=tmpl_amt,
                     tmpl_rcv=tmpl_rcv,
                     tmpl_timeout=tmpl_timeout):

    periodic_pay_core = And(
        Txn.type_enum() == TxnType.Payment,
        Txn.fee() < tmpl_fee,
        Txn.first_valid() % tmpl_period == Int(0),
        Txn.last_valid() == tmpl_dur + Txn.first_valid(),
        Txn.lease() == tmpl_lease
    )

    periodic_pay_transfer = And(
        Txn.close_remainder_to() == Global.zero_address(),
        Txn.receiver() == tmpl_rcv,
        Txn.amount() == tmpl_amt
    )

    periodic_pay_close = And(
        Txn.close_remainder_to() == tmpl_rcv,
        Txn.receiver() == Global.zero_address(),
        Txn.first_valid() == tmpl_timeout,
        Txn.amount() == Int(0)
    )

    periodic_pay_escrow = periodic_pay_core.And(periodic_pay_transfer.Or(periodic_pay_close))

    return periodic_pay_escrow

if __name__ == "__main__":
    print(compileTeal(periodic_payment(), Mode.Signature))

Application Mode

Voting

Voting allows accounts to register and vote for arbitrary candiates. Here a candiate is any byte slice and anyone is allowed to register to vote.

This example has a configurable registration period defined by the global state RegBegin and RegEnd which restrict when accounts can register to vote. There is also a separate configurable voting period defined by the global state VotingBegin and VotingEnd which restrict when voting can take place.

An account must register in order to vote. Accounts cannot vote more than once, and if an account opts out of the application before the voting period has concluded, their vote is discarded. The results are visible in the global state of the application, and the winner is the candidate with the highest number of votes.

# This example is provided for informational purposes only and has not been audited for security.

from pyteal import *

def approval_program():
    on_creation = Seq([
        App.globalPut(Bytes("Creator"), Txn.sender()),
        Assert(Txn.application_args.length() == Int(4)),
        App.globalPut(Bytes("RegBegin"), Btoi(Txn.application_args[0])),
        App.globalPut(Bytes("RegEnd"), Btoi(Txn.application_args[1])),
        App.globalPut(Bytes("VoteBegin"), Btoi(Txn.application_args[2])),
        App.globalPut(Bytes("VoteEnd"), Btoi(Txn.application_args[3])),
        Return(Int(1))
    ])

    is_creator = Txn.sender() == App.globalGet(Bytes("Creator"))

    get_vote = App.localGetEx(Int(0), Txn.application_id(), Bytes("voted"))
    get_count = App.globalGetEx(Int(0), get_vote.value())

    decrement_existing = Seq([
        App.globalPut(get_vote.value(), get_count.value() - Int(1)),
        Return(Int(1))
    ])

    if_voted = Seq([
        get_count,
        If(get_count.hasValue(), decrement_existing, Return(Int(1)))
    ])

    on_closeout = If(
        Global.round() > App.globalGet(Bytes("VoteEnd")),
        Return(Int(1)),
        Seq([
            get_vote,
            If(get_vote.hasValue(), if_voted, Return(Int(1)))
        ])
    )

    on_register = Return(And(
        Global.round() >= App.globalGet(Bytes("RegBegin")),
        Global.round() <= App.globalGet(Bytes("RegEnd"))
    ))

    get_candidate = App.globalGetEx(Int(0), Txn.application_args[1])

    not_voted = Seq([
        get_candidate,
        App.globalPut(Txn.application_args[1], If(get_candidate.hasValue(), get_candidate.value() + Int(1), Int(1))),
        App.localPut(Int(0), Bytes("voted"), Txn.application_args[1]),
        Return(Int(1))
    ])

    on_vote = Seq([
        Assert(And(Global.round() >= App.globalGet(Bytes("VoteBegin")), Global.round() <= App.globalGet(Bytes("VoteEnd")))),
        get_vote,
        If(get_vote.hasValue(), Return(Int(1)), not_voted)
    ])

    program = Cond(
        [Txn.application_id() == Int(0), on_creation],
        [Txn.on_completion() == OnComplete.DeleteApplication, Return(is_creator)],
        [Txn.on_completion() == OnComplete.UpdateApplication, Return(is_creator)],
        [Txn.on_completion() == OnComplete.CloseOut, on_closeout],
        [Txn.on_completion() == OnComplete.OptIn, on_register],
        [Bytes("vote") == Txn.application_args[0], on_vote]
    )

    return program

def close_out_program():
    get_voted = App.localGetEx(Int(0), Txn.application_id(), Bytes("voted"))
    get_candidate = App.globalGetEx(Int(0), get_voted.value())

    decrement_existing = Seq([
        App.globalPut(get_voted.value(), get_candidate.value() - Int(1)),
        Return(Int(1))
    ])

    voted = Seq([
        get_candidate,
        If(get_candidate.hasValue(), decrement_existing, Return(Int(1)))
    ])

    program = If(
        Global.round() > App.globalGet(Bytes("VoteEnd")),
        Return(Int(1)),
        Seq([
            get_voted,
            If(get_voted.hasValue(), voted, Return(Int(1)))
        ])
    )

    return program

with open('vote_approval.teal', 'w') as f:
    compiled = compileTeal(approval_program(), Mode.Application)
    f.write(compiled)

with open('vote_close_out.teal', 'w') as f:
    compiled = compileTeal(close_out_program(), Mode.Application)
    f.write(compiled)

Asset

Asset is an implementation of a custom asset type using smart contracts. While Algorand has ASAs, in some blockchains the only way to create a custom asset is through smart contracts.

At creation, the creator specifies the total supply of the asset. Initially this supply is placed in a reserve and the creator is made an admin. Any admin can move funds from the reserve into the balance of any account that has opted into the application using the mint argument. Additionally, any admin can move funds from any account’s balance into the reserve using the burn argument.

Accounts are free to transfer funds in their balance to any other account that has opted into the application. When an account opts out of the application, their balance is added to the reserve.

# This example is provided for informational purposes only and has not been audited for security.

from pyteal import *

def approval_program():
    on_creation = Seq([
        Assert(Txn.application_args.length() == Int(1)),
        App.globalPut(Bytes("total supply"), Btoi(Txn.application_args[0])),
        App.globalPut(Bytes("reserve"), Btoi(Txn.application_args[0])),
        App.localPut(Int(0), Bytes("admin"), Int(1)),
        App.localPut(Int(0), Bytes("balance"), Int(0)),
        Return(Int(1))
    ])

    is_admin = App.localGet(Int(0), Bytes("admin"))

    on_closeout = Seq([
        App.globalPut(
            Bytes("reserve"),
            App.globalGet(Bytes("reserve")) + App.localGet(Int(0), Bytes("balance"))
        ),
        Return(Int(1))
    ])

    register = Seq([
        App.localPut(Int(0), Bytes("balance"), Int(0)),
        Return(Int(1))
    ])

    # configure the admin status of the account Txn.accounts[0]
    # sender must be admin
    new_admin_status = Btoi(Txn.application_args[1])
    set_admin = Seq([
        Assert(And(is_admin, Txn.application_args.length() == Int(2))),
        App.localPut(Int(1), Bytes("admin"), new_admin_status),
        Return(Int(1))
    ])
    # NOTE: The above set_admin code is carefully constructed. If instead we used the following code:
    # Seq([
    #     Assert(Txn.application_args.length() == Int(2)),
    #     App.localPut(Int(1), Bytes("admin"), new_admin_status),
    #     Return(is_admin)
    # ])
    # It would be vulnerable to the following attack: a sender passes in their own address as
    # Txn.accounts[0], so then the line App.localPut(Int(1), Bytes("admin"), new_admin_status)
    # changes the sender's admin status, meaning the final Return(is_admin) can return anything the
    # sender wants. This allows anyone to become an admin!

    # move assets from the reserve to Txn.accounts[0]
    # sender must be admin
    mint_amount = Btoi(Txn.application_args[1])
    mint = Seq([
        Assert(Txn.application_args.length() == Int(2)),
        Assert(mint_amount <= App.globalGet(Bytes("reserve"))),
        App.globalPut(Bytes("reserve"), App.globalGet(Bytes("reserve")) - mint_amount),
        App.localPut(Int(1), Bytes("balance"), App.localGet(Int(1), Bytes("balance")) + mint_amount),
        Return(is_admin)
    ])

    # transfer assets from the sender to Txn.accounts[0]
    transfer_amount = Btoi(Txn.application_args[1])
    transfer = Seq([
        Assert(Txn.application_args.length() == Int(2)),
        Assert(transfer_amount <= App.localGet(Int(0), Bytes("balance"))),
        App.localPut(Int(0), Bytes("balance"), App.localGet(Int(0), Bytes("balance")) - transfer_amount),
        App.localPut(Int(1), Bytes("balance"), App.localGet(Int(1), Bytes("balance")) + transfer_amount),
        Return(Int(1))
    ])

    program = Cond(
        [Txn.application_id() == Int(0), on_creation],
        [Txn.on_completion() == OnComplete.DeleteApplication, Return(is_admin)],
        [Txn.on_completion() == OnComplete.UpdateApplication, Return(is_admin)],
        [Txn.on_completion() == OnComplete.CloseOut, on_closeout],
        [Txn.on_completion() == OnComplete.OptIn, register],
        [Txn.application_args[0] == Bytes("set admin"), set_admin],
        [Txn.application_args[0] == Bytes("mint"), mint],
        [Txn.application_args[0] == Bytes("transfer"), transfer]
    )

    return program

def close_out_program():
    program = Seq([
        App.globalPut(
            Bytes("reserve"),
            App.globalGet(Bytes("reserve")) + App.localGet(Int(0), Bytes("balance"))
        ),
        Return(Int(1))
    ])

    return program

with open('asset_approval.teal', 'w') as f:
    compiled = compileTeal(approval_program(), Mode.Application)
    f.write(compiled)

with open('asset_close_out.teal', 'w') as f:
    compiled = compileTeal(close_out_program(), Mode.Application)
    f.write(compiled)

Security Token

Security Token is an extension of the Asset example with more features and restrictions. There are two types of admins, contract admins and transfer admins.

Contract admins can delete the smart contract if the entire supply is in the reserve. They can promote accounts to transfer or contract admins. They can also mint and burn funds.

Transfer admins can impose maximum balance limitations on accounts, temporarily lock accounts, assign accounts to transfer groups, and impose transaction restrictions between transaction groups.

Both contract and transfer admins can pause trading of funds and freeze individual accounts.

Accounts can only transfer funds if trading is not paused, both the sender and receive accounts are not frozen or temporarily locked, transfer group restrictions are not in place between them, and the receiver’s account does not have a maximum balance restriction that would be invalidated.

# This example is provided for informational purposes only and has not been audited for security.

from pyteal import *

def approval_program():
    on_creation = Seq([
        Assert(Txn.application_args.length() == Int(1)),
        App.globalPut(Bytes("total supply"), Btoi(Txn.application_args[0])),
        App.globalPut(Bytes("reserve"), Btoi(Txn.application_args[0])),
        App.globalPut(Bytes("paused"), Int(0)),
        App.localPut(Int(0), Bytes("contract admin"), Int(1)),
        App.localPut(Int(0), Bytes("transfer admin"), Int(1)),
        App.localPut(Int(0), Bytes("balance"), Int(0)),
        Return(Int(1))
    ])

    is_contract_admin = App.localGet(Int(0), Bytes("contract admin"))
    is_transfer_admin = App.localGet(Int(0), Bytes("transfer admin"))
    is_any_admin = is_contract_admin.Or(is_transfer_admin)

    can_delete = And(
        is_contract_admin,
        App.globalGet(Bytes("total supply")) == App.globalGet(Bytes("reserve"))
    )

    on_closeout = Seq([
        App.globalPut(
            Bytes("reserve"),
            App.globalGet(Bytes("reserve")) + App.localGet(Int(0), Bytes("balance"))
        ),
        Return(Int(1))
    ])

    register = Seq([
        App.localPut(Int(0), Bytes("balance"), Int(0)),
        Return(Int(1))
    ])

    # pause all transfers
    # sender must be any admin
    new_pause_value = Btoi(Txn.application_args[1])
    pause = Seq([
        Assert(Txn.application_args.length() == Int(2)),
        App.globalPut(Bytes("paused"), new_pause_value),
        Return(is_any_admin)
    ])

    # configure the admin status of the account Txn.accounts[0]
    # sender must be contract admin
    new_admin_type = Txn.application_args[1]
    new_admin_status = Btoi(Txn.application_args[2])
    set_admin = Seq([
        Assert(And(
            is_contract_admin,
            Txn.application_args.length() == Int(3),
            Or(new_admin_type == Bytes("contract admin"), new_admin_type == Bytes("transfer admin")),
            Txn.accounts.length() == Int(1)
        )),
        App.localPut(Int(1), new_admin_type, new_admin_status),
        Return(Int(1))
    ])

    # freeze Txn.accounts[0]
    # sender must be any admin
    new_freeze_value = Btoi(Txn.application_args[1])
    freeze = Seq([
        Assert(And(
            Txn.application_args.length() == Int(2),
            Txn.accounts.length() == Int(1)
        )),
        App.localPut(Int(1), Bytes("frozen"), new_freeze_value),
        Return(is_any_admin)
    ])

    # modify the max balance of Txn.accounts[0]
    # if max_balance_value is 0, will delete the exisint max balance limitation on the account
    # sender must be transfer admin
    max_balance_value = Btoi(Txn.application_args[1])
    max_balance = Seq([
        Assert(And(
            Txn.application_args.length() == Int(2),
            Txn.accounts.length() == Int(1)
        )),
        If(max_balance_value == Int(0),
            App.localDel(Int(1), Bytes("max balance")),
            App.localPut(Int(1), Bytes("max balance"), max_balance_value)
        ),
        Return(is_transfer_admin)
    ])

    # lock Txn.accounts[0] until a UNIX timestamp
    # sender must be transfer admin
    lock_until_value = Btoi(Txn.application_args[1])
    lock_until = Seq([
        Assert(And(
            Txn.application_args.length() == Int(2),
            Txn.accounts.length() == Int(1)
        )),
        If(lock_until_value == Int(0),
            App.localDel(Int(1), Bytes("lock until")),
            App.localPut(Int(1), Bytes("lock until"), lock_until_value)
        ),
        Return(is_transfer_admin)
    ])

    set_transfer_group = Seq([
        Assert(And(
            Txn.application_args.length() == Int(3),
            Txn.accounts.length() == Int(1)
        )),
        App.localPut(Int(1), Bytes("transfer group"), Btoi(Txn.application_args[2]))
    ])

    def getRuleKey(sendGroup, receiveGroup):
        return Concat(Bytes("rule"), Itob(sendGroup), Itob(receiveGroup))

    lock_transfer_key = getRuleKey(Btoi(Txn.application_args[2]), Btoi(Txn.application_args[3]))
    lock_transfer_until = Btoi(Txn.application_args[4])
    lock_transfer_group = Seq([
        Assert(Txn.application_args.length() == Int(5)),
        If(lock_transfer_until == Int(0),
            App.globalDel(lock_transfer_key),
            App.globalPut(lock_transfer_key, lock_transfer_until)
        )
    ])

    # sender must be transfer admin
    transfer_group = Seq([
        Assert(Txn.application_args.length() > Int(2)),
        Cond(
            [Txn.application_args[1] == Bytes("set"), set_transfer_group],
            [Txn.application_args[1] == Bytes("lock"), lock_transfer_group]
        ),
        Return(is_transfer_admin)
    ])

    # move assets from the reserve to Txn.accounts[0]
    # sender must be contract admin
    mint_amount = Btoi(Txn.application_args[1])
    mint = Seq([
        Assert(And(
            Txn.application_args.length() == Int(2),
            Txn.accounts.length() == Int(1),
            mint_amount <= App.globalGet(Bytes("reserve"))
        )),
        App.globalPut(Bytes("reserve"), App.globalGet(Bytes("reserve")) - mint_amount),
        App.localPut(Int(1), Bytes("balance"), App.localGet(Int(1), Bytes("balance")) + mint_amount),
        Return(is_contract_admin)
    ])

    # move assets from Txn.accounts[0] to the reserve
    # sender must be contract admin
    burn_amount = Btoi(Txn.application_args[1])
    burn = Seq([
        Assert(And(
            Txn.application_args.length() == Int(2),
            Txn.accounts.length() == Int(1),
            burn_amount <= App.localGet(Int(1), Bytes("balance"))
        )),
        App.globalPut(Bytes("reserve"), App.globalGet(Bytes("reserve")) + burn_amount),
        App.localPut(Int(1), Bytes("balance"), App.localGet(Int(1), Bytes("balance")) - burn_amount),
        Return(is_contract_admin)
    ])

    # transfer assets from the sender to Txn.accounts[0]
    transfer_amount = Btoi(Txn.application_args[1])
    receiver_max_balance = App.localGetEx(Int(1), App.id(), Bytes("max balance"))
    transfer = Seq([
        Assert(And(
            Txn.application_args.length() == Int(2),
            Txn.accounts.length() == Int(1),
            transfer_amount <= App.localGet(Int(0), Bytes("balance"))
        )),
        receiver_max_balance,
        If(
            Or(
                App.globalGet(Bytes("paused")),
                App.localGet(Int(0), Bytes("frozen")),
                App.localGet(Int(1), Bytes("frozen")),
                App.localGet(Int(0), Bytes("lock until")) >= Global.latest_timestamp(),
                App.localGet(Int(1), Bytes("lock until")) >= Global.latest_timestamp(),
                App.globalGet(getRuleKey(App.localGet(Int(0), Bytes("transfer group")), App.localGet(Int(1), Bytes("transfer group")))) >= Global.latest_timestamp(),
                And(
                    receiver_max_balance.hasValue(),
                    receiver_max_balance.value() < App.localGet(Int(1), Bytes("balance")) + transfer_amount
                )
            ),
            Return(Int(0))
        ),
        App.localPut(Int(0), Bytes("balance"), App.localGet(Int(0), Bytes("balance")) - transfer_amount),
        App.localPut(Int(1), Bytes("balance"), App.localGet(Int(1), Bytes("balance")) + transfer_amount),
        Return(Int(1))
    ])

    program = Cond(
        [Txn.application_id() == Int(0), on_creation],
        [Txn.on_completion() == OnComplete.DeleteApplication, Return(can_delete)],
        [Txn.on_completion() == OnComplete.UpdateApplication, Return(is_contract_admin)],
        [Txn.on_completion() == OnComplete.CloseOut, on_closeout],
        [Txn.on_completion() == OnComplete.OptIn, register],
        [Txn.application_args[0] == Bytes("pause"), pause],
        [Txn.application_args[0] == Bytes("set admin"), set_admin],
        [Txn.application_args[0] == Bytes("freeze"), freeze],
        [Txn.application_args[0] == Bytes("max balance"), max_balance],
        [Txn.application_args[0] == Bytes("lock until"), lock_until],
        [Txn.application_args[0] == Bytes("transfer group"), transfer_group],
        [Txn.application_args[0] == Bytes("mint"), mint],
        [Txn.application_args[0] == Bytes("burn"), burn],
        [Txn.application_args[0] == Bytes("transfer"), transfer],
    )

    return program

def close_out_program():
    program = Seq([
        App.globalPut(
            Bytes("reserve"),
            App.globalGet(Bytes("reserve")) + App.localGet(Int(0), Bytes("balance"))
        ),
        Return(Int(1))
    ])

    return program

with open('security_token_approval.teal', 'w') as f:
    compiled = compileTeal(approval_program(), Mode.Application)
    f.write(compiled)

with open('security_token_close_out.teal', 'w') as f:
    compiled = compileTeal(close_out_program(), Mode.Application)
    f.write(compiled)