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

Add ABI support #264

Merged
merged 76 commits into from
Jul 22, 2022
Merged
Show file tree
Hide file tree
Changes from 75 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
3b386b0
ABI Types (#154)
barnjamin Mar 1, 2022
7a66db7
Merge branch 'master' into feature/abi
ahangsu Mar 1, 2022
404ef2f
Merge with master and regenerate `__index__.pyi` (#224)
jasonpaulos Mar 3, 2022
0ff98a8
Merge #227 into feature/abi (#231)
michaeldiamant Mar 4, 2022
bfaf1c1
Make ABI types fully specified (#222)
jasonpaulos Mar 18, 2022
ee2ca94
Merge branch 'master' into feature/abi
jasonpaulos Mar 18, 2022
63b4858
Passing ABI type arguments to Subroutine (#263)
ahangsu Mar 30, 2022
217b678
Merge branch 'master' into feature/abi
ahangsu Mar 30, 2022
a37be3f
Merge branch 'master' into feature/abi
ahangsu Mar 31, 2022
9a6a854
ABI Method Return (#175)
ahangsu Apr 1, 2022
b8ba82f
Merge branch 'feature/abi' of github.com:algorand/pyteal into feature…
ahangsu Apr 1, 2022
25fc7ac
Merge branch 'master' into feature/abi
ahangsu Apr 4, 2022
bac312d
Merge branch 'master' into feature/abi
ahangsu Apr 12, 2022
3ad5eac
Allow `ComputedType` to be passed to the `set` method on ABI types (#…
ahangsu Apr 12, 2022
81edc2c
Merge branch 'master' into feature/abi
jasonpaulos Apr 13, 2022
5c43e12
Merge branch 'master' into feature/abi (#284)
michaeldiamant Apr 20, 2022
7a63907
Move to pyteal as pt in ABI tests with concise prefix (#286)
michaeldiamant Apr 20, 2022
719a3a7
ABI Strings (#278)
barnjamin Apr 21, 2022
c2ae178
Move to pyteal as pt in #278 (#287)
michaeldiamant Apr 21, 2022
82ab41f
Merge absolute imports into feature/abi (#288)
michaeldiamant Apr 21, 2022
3265a07
Remove temporary I252 ignore on pyteal.ast.abi (#290)
michaeldiamant Apr 21, 2022
11f970a
Fix abi import (#303)
jasonpaulos Apr 27, 2022
33c565d
Feature/abi merge master again (#310)
tzaffi May 2, 2022
dd4bf95
merge conflicts
May 2, 2022
b500499
merge again
May 2, 2022
57a9e36
Merge branch 'master' into feature/abi
tzaffi May 2, 2022
11ff5c3
wip
May 2, 2022
36657c9
pass all tests after resolving conflicts
May 2, 2022
6d8f447
Merge branch 'master' into feature/abi
ahangsu May 4, 2022
a873cba
Add `set` on address and string, change array init to accept typespec…
barnjamin May 4, 2022
ef61b2c
Add convenience `make` method for ABI types (#326)
jasonpaulos May 4, 2022
00f02a1
Merge branch 'master' into feature/abi
ahangsu May 9, 2022
0bd9c01
Merge branch 'master' into feature/abi
ahangsu May 10, 2022
c72a7ef
ABI Type subroutine return (#256)
ahangsu May 11, 2022
ed6f0e2
adding byte type hint to DyanamicArray parent class (#339)
barnjamin May 13, 2022
8558f07
Define level of support provided for ABIReturnSubroutine (#341)
michaeldiamant May 17, 2022
ef7fe0e
Testing ABI Functionality via Graviton / Dry-runs + Txn Friendly Grav…
tzaffi May 19, 2022
fe5bc50
abi_roundtrip_test::test_abi_types_comprehensive() (#355)
tzaffi May 20, 2022
4f092a4
Merge branch 'master' into feature/abi
ahangsu May 20, 2022
f70e8c2
adding sizeof util (#358)
barnjamin May 23, 2022
18d67f4
Reference Types (#361)
barnjamin May 25, 2022
dd0eaa3
Clearer Comment (#364)
tzaffi May 25, 2022
57cf6e4
Bug: Graviton's report no longer has last_rows (#365)
tzaffi May 26, 2022
54eb02e
adding deref type to get the underlying value of the reference type (…
barnjamin May 31, 2022
c2bbd39
Merge branch 'master' into feature/abi
ahangsu May 31, 2022
7304e8f
`ABI Router` implementation and test (#170)
ahangsu Jun 1, 2022
3eecaaf
Adding method_spec to ABIReturnSubroutine (#380)
barnjamin Jun 3, 2022
c9fe579
Txn types (#376)
barnjamin Jun 6, 2022
ba31c12
Blackbox testing unifying abi-return-subroutine and subroutine input …
ahangsu Jun 7, 2022
6c1f995
Changes to router clear state (#386)
jasonpaulos Jun 7, 2022
135f215
define reference type spec (#383)
barnjamin Jun 8, 2022
36f3655
Rename decode parameters to abide by PEP8 conventions (#389)
michaeldiamant Jun 14, 2022
9edc6f5
More abi graviton tests (#391)
tzaffi Jun 15, 2022
908303f
Router return subroutine (#422)
barnjamin Jun 27, 2022
9847b29
Method call itxn (#387)
barnjamin Jun 28, 2022
cb0a5c5
__str__ formatting (#411)
barnjamin Jun 29, 2022
8de49ab
Check transaction type arguments (#427)
jasonpaulos Jun 29, 2022
1870759
Check `Expr` length when converting to `abi.Address` (#432)
ahangsu Jul 1, 2022
6d30147
Fix on previous address length check (#433)
ahangsu Jul 1, 2022
a29e222
Merge branch 'master' into feature/abi
ahangsu Jul 1, 2022
7f78f9b
unit -> uint (#434)
ahangsu Jul 1, 2022
cf56084
Support declaring compatible PyTeal version in source code (#429)
jdtzmn Jul 6, 2022
bd7de04
Consistently use `start_index` and `end_index` (#436)
jasonpaulos Jul 11, 2022
281f2d3
allow passing router description to Contract constructor (#448)
barnjamin Jul 18, 2022
657064f
Document new ABI features (#400)
jasonpaulos Jul 20, 2022
71d1bd0
Feature/abi merge master (#457)
jasonpaulos Jul 21, 2022
53b82d8
Merge branch 'master' into feature/abi
jasonpaulos Jul 21, 2022
7f5a493
`abi.Bool` improvements (#453)
jasonpaulos Jul 21, 2022
51aa407
Rename pyteal/ast/abi/bool.py functions to follow PEP 8 conventions (…
michaeldiamant Jul 21, 2022
f13677b
Specify dependency versions (#464)
jasonpaulos Jul 21, 2022
5cc6541
Rename pyteal/ast/abi/util.py functions to follow PEP 8 conventions (…
michaeldiamant Jul 21, 2022
06a3e94
Rename pyteal/ast/abi/tuple.py functions to follow PEP 8 conventions …
michaeldiamant Jul 21, 2022
a27b31b
Fix typos in pyteal/ast/abi/util.py (#467)
michaeldiamant Jul 22, 2022
f2f1563
Rename encoded_string to demarcate internal usage (#466)
michaeldiamant Jul 22, 2022
9550fe3
Rename pyteal/ast/abi method parameters to follow PEP 8 conventions (…
michaeldiamant Jul 22, 2022
e0fe580
Merge branch 'master' into feature/abi
jasonpaulos Jul 22, 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
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
- name: Install python dependencies
run: make setup-development
- name: Build and Test
run: make build-and-test
run: make lint-and-test

run-integration-tests:
runs-on: ubuntu-20.04
Expand Down Expand Up @@ -63,8 +63,8 @@ jobs:
run: make sandbox-dev-up
- name: Install python dependencies
run: make setup-development
- name: Build, Unit Tests and Integration Tests
run: make all-tests
- name: Integration Tests Only
run: make test-integration
- name: Stop running images
run: make sandbox-dev-stop

Expand Down
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ celerybeat-schedule
*.sage.py

# Environments
_env
.env
.venv
env/
Expand Down Expand Up @@ -138,3 +139,6 @@ dmypy.json

# mac OS
.DS_Store

# asdf
.tool-versions
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ black:
flake8:
flake8 $(ALLPY)

MYPY = pyteal scripts
MYPY = pyteal scripts tests
mypy:
mypy --show-error-codes $(MYPY)

Expand All @@ -54,9 +54,9 @@ test-unit:
pytest -n $(NUM_PROCS) --durations=10 -sv pyteal tests/unit --ignore tests/unit/blackbox_test.py --ignore tests/unit/user_guide_test.py
pytest -n 1 -sv tests/unit/blackbox_test.py tests/unit/user_guide_test.py

build-and-test: check-generate-init lint test-unit
lint-and-test: check-generate-init lint test-unit

# ---- Integration Test (algod required) ---- #
# ---- Integration Tests (algod required) ---- #

sandbox-dev-up:
docker-compose up -d algod
Expand All @@ -69,7 +69,7 @@ integration-run:

test-integration: integration-run

all-tests: build-and-test test-integration
all-tests: lint-and-test test-integration

# ---- Local Github Actions Simulation via `act` ---- #
# assumes act is installed, e.g. via `brew install act`
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 *
import json


@Subroutine(TealType.none)
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
),
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