Skip to content

Commit

Permalink
TEAL v2 support (#12)
Browse files Browse the repository at this point in the history
* Add new opcodes & expressions

* Add escapeStr documentation
  • Loading branch information
jasonpaulos committed Aug 19, 2020
1 parent a67fd6e commit 13aab7d
Show file tree
Hide file tree
Showing 63 changed files with 3,204 additions and 527 deletions.
2 changes: 1 addition & 1 deletion examples/atomic_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,4 @@ def htlc(tmpl_seller=alice,
type_cond,
Or(recv_cond, esc_cond))

print(compileTeal(htlc()))
print(compileTeal(htlc(), Mode.Signature))
2 changes: 1 addition & 1 deletion examples/dutch_auction.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,4 +89,4 @@
[Global.group_size() == Int(4), redeem],
[Global.group_size() == Int(1), wrapup])

print(compileTeal(dutch))
print(compileTeal(dutch, Mode.Signature))
2 changes: 1 addition & 1 deletion examples/periodic_payment.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ def periodic_payment(tmpl_fee=tmpl_fee,

return And(periodic_pay_core, periodic_pay_transfer)

# print(compileTeal(periodic_payment()))
# print(compileTeal(periodic_payment(), Mode.Signature))
2 changes: 1 addition & 1 deletion examples/recurring_swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,6 @@ def recurring_swap(tmpl_buyer=tmpl_buyer,
Txn.amount() == Int(0),
Txn.first_valid() >= tmpl_timeout)

return compileTeal(And(fee_cond, type_cond, Or(recv_cond, close_cond)))
return compileTeal(And(fee_cond, type_cond, Or(recv_cond, close_cond)), Mode.Signature)

# print(recurring_swap())
2 changes: 1 addition & 1 deletion examples/split.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@
split_transfer,
split_close))

print(split.teal())
print(compileTeal(split, Mode.Signature))
2 changes: 2 additions & 0 deletions pyteal/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from .ast import *
from .ir import *
from .compiler import compileTeal
from .types import TealType
from .errors import TealInternalError, TealTypeError, TealInputError
from .util import execute
from .config import MAX_GROUP_SIZE
21 changes: 15 additions & 6 deletions pyteal/ast/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,37 @@
from .addr import Addr
from .bytes import Bytes
from .err import Err
from .int import Int
from .int import Int, EnumInt

# properties
from .arg import Arg
from .txn import Txn, TxnField
from .txn import TxnType, TxnField, Txn, Txna
from .gtxn import Gtxn
from .global_ import Global
from .global_ import Global, GlobalField
from .app import App, AppField, OnComplete
from .asset import AssetHolding, AssetParam

# meta
from .tmpl import Tmpl
from .nonce import Nonce

# unary ops
from .unaryexpr import UnaryExpr, Btoi, Itob, Len, Sha256, Sha512_256, Keccak256, Pop
from .unaryexpr import UnaryExpr, Btoi, Itob, Len, Sha256, Sha512_256, Keccak256, Not, BitwiseNot, Pop, Return, Balance

# binary ops
from .binaryexpr import BinaryExpr, Add, Minus, Mul, Div, Mod, Eq, Lt, Le, Gt, Ge
from .binaryexpr import BinaryExpr, Add, Minus, Mul, Div, BitwiseAnd, BitwiseOr, BitwiseXor, Mod, Eq, Neq, Lt, Le, Gt, Ge

# more ops
from .ed25519verify import Ed25519Verify
from .naryexpr import NaryExpr, And, Or
from .substring import Substring
from .naryexpr import NaryExpr, And, Or, Concat

# control flow
from .if_ import If
from .cond import Cond
from .seq import Seq
from .assert_ import Assert

# misc
from .scratch import ScratchSlot, ScratchLoad, ScratchStore
from .maybe import MaybeValue
7 changes: 4 additions & 3 deletions pyteal/ast/addr.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
from ..types import TealType, valid_address
from ..ir import TealOp, Op
from .leafexpr import LeafExpr

class Addr(LeafExpr):
"""An expression that represents an Algorand address."""

def __init__(self, address: str) -> None:
"""Create a new Addr expression.
Args:
address: A string containing a valid base32 Algorand address
"""
valid_address(address)
self.address = address

def __teal__(self):
return [["addr", self.address]]
return [TealOp(Op.addr, self.address)]

def __str__(self):
return "(address: {})".format(self.address)
Expand Down
3 changes: 2 additions & 1 deletion pyteal/ast/addr_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

def test_addr():
expr = Addr("NJUWK3DJNZTWU2LFNRUW4Z3KNFSWY2LOM5VGSZLMNFXGO2TJMVWGS3THMF")
assert expr.type_of() == TealType.bytes
assert expr.__teal__() == [
["addr", "NJUWK3DJNZTWU2LFNRUW4Z3KNFSWY2LOM5VGSZLMNFXGO2TJMVWGS3THMF"]
TealOp(Op.addr, "NJUWK3DJNZTWU2LFNRUW4Z3KNFSWY2LOM5VGSZLMNFXGO2TJMVWGS3THMF")
]

def test_addr_invalid():
Expand Down
185 changes: 185 additions & 0 deletions pyteal/ast/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
from enum import Enum

from ..types import TealType, require_type
from ..ir import TealOp, Op
from .leafexpr import LeafExpr
from .expr import Expr
from .maybe import MaybeValue
from .int import EnumInt
from .global_ import Global

class OnComplete:
"""An enum of values that Txn.on_completion() may return."""
NoOp = EnumInt("NoOp")
OptIn = EnumInt("OptIn")
CloseOut = EnumInt("CloseOut")
ClearState = EnumInt("ClearState")
UpdateApplication = EnumInt("UpdateApplication")
DeleteApplication = EnumInt("DeleteApplication")

class AppField(Enum):
optedIn = (Op.app_opted_in, TealType.uint64)
localGet = (Op.app_local_get, TealType.anytype)
localGetEx = (Op.app_local_get_ex, TealType.none)
globalGet = (Op.app_global_get, TealType.anytype)
globalGetEx = (Op.app_global_get_ex, TealType.none)
localPut = (Op.app_local_put, TealType.none)
globalPut = (Op.app_global_put, TealType.none)
localDel = (Op.app_local_del, TealType.none)
globalDel = (Op.app_global_del, TealType.none)

def __init__(self, op: Op, type: TealType) -> None:
self.op = op
self.ret_type = type

def get_op(self) -> Op:
return self.op

def type_of(self) -> TealType:
return self.ret_type

class App(LeafExpr):
"""An expression related to applications."""

def __init__(self, field:AppField, args) -> None:
self.field = field
self.args = args

def __str__(self):
ret_str = "({}".format(self.field.get_op())
for a in self.args:
ret_str += " " + a.__str__()
ret_str += ")"
return ret_str

def __teal__(self):
teal = []
for arg in self.args:
teal += arg.__teal__()
teal += [TealOp(self.field.get_op())]
return teal

def type_of(self):
return self.field.type_of()

@classmethod
def id(cls):
"""Get the ID of the current running application.
This is the same as Global.current_application_id().
"""
return Global.current_application_id()

@classmethod
def optedIn(cls, account: Expr, app: Expr):
"""Check if an account has opted in for an application.
Args:
account: An index into Txn.Accounts that corresponds to the account to check. Must
evaluate to uint64.
app: The ID of the application being checked. Must evaluate to uint64.
"""
require_type(account.type_of(), TealType.uint64)
require_type(app.type_of(), TealType.uint64)
return cls(AppField.optedIn, [account, app])

@classmethod
def localGet(cls, account: Expr, key: Expr):
"""Read from an account's local state for the current application.
Args:
account: An index into Txn.Accounts that corresponds to the account to read from. Must
evaluate to uint64.
key: The key to read from the account's local state. Must evaluate to bytes.
"""
require_type(account.type_of(), TealType.uint64)
require_type(key.type_of(), TealType.bytes)
return cls(AppField.localGet, [account, key])

@classmethod
def localGetEx(cls, account: Expr, app: Expr, key: Expr):
"""Read from an account's local state for an application.
Args:
account: An index into Txn.Accounts that corresponds to the account to read from. Must
evaluate to uint64.
app: The ID of the application being checked. Must evaluate to uint64.
key: The key to read from the account's local state. Must evaluate to bytes.
"""
require_type(account.type_of(), TealType.uint64)
require_type(app.type_of(), TealType.uint64)
require_type(key.type_of(), TealType.bytes)
return MaybeValue(AppField.localGetEx.get_op(), TealType.anytype, args=[account, app, key])

@classmethod
def globalGet(cls, key: Expr):
"""Read from the global state of the current application.
Args:
key: The key to read from the global application state. Must evaluate to bytes.
"""
require_type(key.type_of(), TealType.bytes)
return cls(AppField.globalGet, [key])

@classmethod
def globalGetEx(cls, app: Expr, key: Expr):
"""Read from the global state of an application.
Args:
app: An index into Txn.ForeignApps that corresponds to the application to read from.
Must evaluate to uint64.
key: The key to read from the global application state. Must evaluate to bytes.
"""
require_type(app.type_of(), TealType.uint64)
require_type(key.type_of(), TealType.bytes)
return MaybeValue(AppField.globalGetEx.get_op(), TealType.anytype, args=[app, key])

@classmethod
def localPut(cls, account: Expr, key: Expr, value: Expr):
"""Write to an account's local state for the current application.
Args:
account: An index into Txn.Accounts that corresponds to the account to write to. Must
evaluate to uint64.
key: The key to write in the account's local state. Must evaluate to bytes.
value: The value to write in the account's local state. Can evaluate to any type.
"""
require_type(account.type_of(), TealType.uint64)
require_type(key.type_of(), TealType.bytes)
require_type(value.type_of(), TealType.anytype)
return cls(AppField.localPut, [account, key, value])

@classmethod
def globalPut(cls, key: Expr, value: Expr):
"""Write to the global state of the current application.
Args:
key: The key to write in the global application state. Must evaluate to bytes.
value: THe value to write in the global application state. Can evaluate to any type.
"""
require_type(key.type_of(), TealType.bytes)
require_type(value.type_of(), TealType.anytype)
return cls(AppField.globalPut, [key, value])

@classmethod
def localDel(cls, account: Expr, key: Expr):
"""Delete a key from an account's local state for the current application.
Args:
account: An index into Txn.Accounts that corresponds to the account from which the key
should be deleted. Must evaluate to uint64.
key: The key to delete from the account's local state. Must evaluate to bytes.
"""
require_type(account.type_of(), TealType.uint64)
require_type(key.type_of(), TealType.bytes)
return cls(AppField.localDel, [account, key])

@classmethod
def globalDel(cls, key: Expr):
"""Delete a key from the global state of the current application.
Args:
key: The key to delete from the global application state. Must evaluate to bytes.
"""
require_type(key.type_of(), TealType.bytes)
return cls(AppField.globalDel, [key])

0 comments on commit 13aab7d

Please sign in to comment.