Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document new ABI features #400

Merged
merged 32 commits into from
Jul 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e5c3101
Initial commit
jasonpaulos Jun 16, 2022
d6e8501
Fix abi-docs tests (#402)
michaeldiamant Jun 17, 2022
9e01761
Fix abi-docs Sphinx warnings (#401)
michaeldiamant Jun 17, 2022
a581965
Extend abi-docs with experimental design language (#403)
michaeldiamant Jun 21, 2022
a5af855
Add type fundamentals section
jasonpaulos Jun 21, 2022
4f1ec48
Add basic type usage and some docstrings for referenced methods
jasonpaulos Jun 23, 2022
9a04c10
Finish documenting set and fix overloaded method docs
jasonpaulos Jun 23, 2022
7ef9537
Add docstrings for get and __getitem__
jasonpaulos Jun 23, 2022
0c7fe6f
Add reference type docs
jasonpaulos Jun 24, 2022
33da26c
Add txn type examples
jasonpaulos Jun 27, 2022
6a77a65
Fix errors
jasonpaulos Jun 28, 2022
5e61e18
Make ComputedValue parameter type covariant
jasonpaulos Jun 29, 2022
41d8e85
ComputedValue and subroutine sections
jasonpaulos Jun 29, 2022
b5d00a0
...wasn't included in previous commit
jasonpaulos Jun 29, 2022
35b60fe
Add bare app call and method registration examples
jasonpaulos Jul 5, 2022
67edead
Add router e2e example and compilation explanation
jasonpaulos Jul 6, 2022
6982f77
Merge branch 'feature/abi' into abi-docs
jasonpaulos Jul 6, 2022
cd5d212
Fix post-merge linter/test failures
jasonpaulos Jul 6, 2022
96b2cbd
Add calling documentation
jasonpaulos Jul 6, 2022
d37cc0f
Merge branch 'feature/abi' into abi-docs
jasonpaulos Jul 6, 2022
e3c7312
Partially address feedback
jasonpaulos Jul 7, 2022
3b42c1c
Merge branch 'feature/abi' into abi-docs
jasonpaulos Jul 18, 2022
fe5d68b
Respond to feedback
jasonpaulos Jul 18, 2022
7aabc2e
Resolve TODOs
jasonpaulos Jul 18, 2022
13a81ab
Add pragma references
jasonpaulos Jul 18, 2022
bf2c233
didn't make it into the previous commit
jasonpaulos Jul 18, 2022
7eae32c
Fix Bool.__module__
jasonpaulos Jul 18, 2022
e10f84f
Mention stack size limit
jasonpaulos Jul 19, 2022
fdbbc96
More pragma documentation
jasonpaulos Jul 19, 2022
f49ec24
Address other feedback
jasonpaulos Jul 19, 2022
bf85959
Warn about reference type limits
jasonpaulos Jul 20, 2022
44ea401
**cannot**
jasonpaulos Jul 20, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ ignore =

per-file-ignores =
pyteal/compiler/optimizer/__init__.py: F401
examples/application/abi/algobank.py: F403, F405
examples/application/asset.py: F403, F405
examples/application/opup.py: F403, F405
examples/application/security_token.py: F403, F405
Expand Down
917 changes: 917 additions & 0 deletions docs/abi.rst

Large diffs are not rendered by default.

16 changes: 16 additions & 0 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,19 @@ PyTeal Package
:annotation: = <pyteal.TxnObject object>

The most recently submitted inner transaction. This is an instance of :any:`TxnObject`.

If a transaction group was submitted most recently, then this will be the last transaction in that group.

.. data:: Gitxn
:annotation: = <pyteal.InnerTxnGroup object>

The most recently submitted inner transaction group. This is an instance of :any:`InnerTxnGroup`.

If a single transaction was submitted most recently, then this will be a group of size 1.

.. automodule:: pyteal.abi
:members:
:undoc-members:
:imported-members:
:special-members: __getitem__
:show-inheritance:
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ PyTeal **hasn't been security audited**. Use it at your own risk.
versions
compiler_optimization
opup
abi

.. toctree::
:maxdepth: 3
Expand Down
36 changes: 28 additions & 8 deletions docs/versions.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.. _versions:

TEAL Versions
Versions
=============

Each version of PyTeal compiles contracts for a specific version of TEAL. Newer versions of TEAL
Expand All @@ -18,14 +18,34 @@ TEAL Version PyTeal Version
6 >= 0.10.0
============ ==============

.. _version pragmas:

Version Pragmas
----------------

When writing a PyTeal smart contract, it's important to target a specific AVM version and to compile
with a single PyTeal version. This will ensure your compiled program remains consistent and has the
exact same behavior no matter when you compile it.

The :any:`pragma` function can be used to assert that the current PyTeal version matches a constraint
of your choosing. This can help strengthen the dependency your source code has on the PyTeal package
version you used when writing it.

If you are writing code for others to consume, or if your codebase has different PyTeal version
dependencies in different places, the :any:`Pragma` expression can be used to apply a pragma
constraint to only a section of the AST.

PyTeal v0.5.4 and Below
-----------------------

In order to support TEAL v2, PyTeal v0.6.0 breaks backward compatibility with v0.5.4. PyTeal
programs written for PyTeal version 0.5.4 and below will not compile properly and most likely will
display an error of the form :code:`AttributeError: * object has no attribute 'teal'`.

**WARNING:** before updating PyTeal to a version with generates TEAL v2 contracts and fixing the
programs to use the global function :any:`compileTeal` rather the class method :code:`.teal()`, make
sure your program abides by the TEAL safety guidelines `<https://developer.algorand.org/docs/reference/teal/guidelines/>`_.
Changing a v1 TEAL program to a v2 TEAL program without any code changes is insecure because v2
TEAL programs allow rekeying. Specifically, you must add a check that the :code:`RekeyTo` property
of any transaction is set to the zero address when updating an older PyTeal program from v0.5.4 and
below.
.. warning::
If you are updating from a v1 TEAL program, make
sure your program abides by the `TEAL safety guidelines <https://developer.algorand.org/docs/reference/teal/guidelines/>`_.
Changing a v1 TEAL program to a v2 TEAL program without any code changes is insecure because v2
TEAL programs allow rekeying. Specifically, you must add a check that the :code:`RekeyTo` property
of any transaction is set to the zero address when updating an older PyTeal program from v0.5.4 and
below.
Empty file.
54 changes: 54 additions & 0 deletions examples/application/abi/algobank.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"name": "AlgoBank",
"methods": [
{
"name": "deposit",
"args": [
{
"type": "pay",
"name": "payment"
},
{
"type": "account",
"name": "sender"
}
],
"returns": {
"type": "void"
},
"desc": "This method receives a payment from an account opted into this app and records it in their local state. The caller may opt into this app during this call."
},
{
"name": "getBalance",
"args": [
{
"type": "account",
"name": "user"
}
],
"returns": {
"type": "uint64"
},
"desc": "Lookup the balance of a user held by this app."
},
{
"name": "withdraw",
"args": [
{
"type": "uint64",
"name": "amount"
},
{
"type": "account",
"name": "recipient"
}
],
"returns": {
"type": "void"
},
"desc": "Withdraw an amount of Algos held by this app. The sender of this method call will be the source of the Algos, and the destination will be the `recipient` argument. This may or may not be the same as the sender's address. This method will fail if the amount of Algos requested to be withdrawn exceeds the amount of Algos held by this app for the sender. The Algos will be transferred to the recipient using an inner transaction whose fee is set to 0, meaning the caller's transaction must include a surplus fee to cover the inner transaction."
}
],
"desc": null,
"networks": {}
}
113 changes: 113 additions & 0 deletions examples/application/abi/algobank.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# This example is provided for informational purposes only and has not been audited for security.
from pyteal import *
jasonpaulos marked this conversation as resolved.
Show resolved Hide resolved
import json


@Subroutine(TealType.none)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

really nice example!!!

def assert_sender_is_creator() -> Expr:
return Assert(Txn.sender() == Global.creator_address())


# move any balance that the user has into the "lost" amount when they close out or clear state
transfer_balance_to_lost = App.globalPut(
Bytes("lost"),
App.globalGet(Bytes("lost")) + App.localGet(Txn.sender(), Bytes("balance")),
)

router = Router(
name="AlgoBank",
bare_calls=BareCallActions(
# approve a creation no-op call
no_op=OnCompleteAction(action=Approve(), call_config=CallConfig.CREATE),
# approve opt-in calls during normal usage, and during creation as a convenience for the creator
opt_in=OnCompleteAction(action=Approve(), call_config=CallConfig.ALL),
# move any balance that the user has into the "lost" amount when they close out or clear state
close_out=OnCompleteAction(
action=transfer_balance_to_lost, call_config=CallConfig.CALL
),
clear_state=OnCompleteAction(
action=transfer_balance_to_lost, call_config=CallConfig.CALL
),
# only the creator can update or delete the app
update_application=OnCompleteAction(
action=assert_sender_is_creator, call_config=CallConfig.CALL
),
tzaffi marked this conversation as resolved.
Show resolved Hide resolved
delete_application=OnCompleteAction(
action=assert_sender_is_creator, call_config=CallConfig.CALL
),
),
)


@router.method(no_op=CallConfig.CALL, opt_in=CallConfig.CALL)
def deposit(payment: abi.PaymentTransaction, sender: abi.Account) -> Expr:
"""This method receives a payment from an account opted into this app and records it in
their local state.

The caller may opt into this app during this call.
"""
return Seq(
Assert(payment.get().sender() == sender.address()),
Assert(payment.get().receiver() == Global.current_application_address()),
App.localPut(
sender.address(),
Bytes("balance"),
App.localGet(sender.address(), Bytes("balance")) + payment.get().amount(),
),
)


@router.method
def getBalance(user: abi.Account, *, output: abi.Uint64) -> Expr:
"""Lookup the balance of a user held by this app."""
return output.set(App.localGet(user.address(), Bytes("balance")))


@router.method
def withdraw(amount: abi.Uint64, recipient: abi.Account) -> Expr:
"""Withdraw an amount of Algos held by this app.

The sender of this method call will be the source of the Algos, and the destination will be
the `recipient` argument. This may or may not be the same as the sender's address.

This method will fail if the amount of Algos requested to be withdrawn exceeds the amount of
Algos held by this app for the sender.

The Algos will be transferred to the recipient using an inner transaction whose fee is set
to 0, meaning the caller's transaction must include a surplus fee to cover the inner
transaction.
"""
return Seq(
# if amount is larger than App.localGet(Txn.sender(), Bytes("balance")), the subtraction
# will underflow and fail this method call
App.localPut(
Txn.sender(),
Bytes("balance"),
App.localGet(Txn.sender(), Bytes("balance")) - amount.get(),
),
InnerTxnBuilder.Begin(),
InnerTxnBuilder.SetFields(
{
TxnField.type_enum: TxnType.Payment,
TxnField.receiver: recipient.address(),
TxnField.amount: amount.get(),
TxnField.fee: Int(0),
}
),
InnerTxnBuilder.Submit(),
)


approval_program, clear_state_program, contract = router.compile_program(
version=6, optimize=OptimizeOptions(scratch_slots=True)
)

if __name__ == "__main__":
with open("algobank_approval.teal", "w") as f:
f.write(approval_program)

with open("algobank_clear_state.teal", "w") as f:
f.write(clear_state_program)

with open("algobank.json", "w") as f:
f.write(json.dumps(contract.dictify(), indent=4))
Loading