Skip to content

Commit

Permalink
feat: add is_opted_in method to Account type (#126)
Browse files Browse the repository at this point in the history
  • Loading branch information
daniel-makerx authored Mar 13, 2024
1 parent fccb36e commit e21dc55
Show file tree
Hide file tree
Showing 30 changed files with 630 additions and 460 deletions.
4 changes: 2 additions & 2 deletions examples/sizes.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Name O0 size O1 size O2 size
abi_routing/Reference 1179 1019 1019
amm/ConstantProductAMM 1213 1114 1114
application/Reference 168 161 161
application/Reference 175 168 168
arc4_numeric_comparisons/UIntNOrdering 1220 908 908
arc4_types/Arc4Arrays 588 376 376
arc4_types/Arc4BoolEval 569 20 20
Expand All @@ -15,7 +15,7 @@ arc4_types/Arc4StringTypes 328 8 8
arc4_types/Arc4StructsFromAnotherModule 67 12 12
arc4_types/Arc4StructsType 296 237 237
arc4_types/Arc4TuplesType 799 146 146
asset/Reference 259 252 252
asset/Reference 268 261 261
auction/Auction 564 523 523
augmented_assignment/Augmented 157 156 156
avm_types_in_abi/Test 226 173 173
Expand Down
56 changes: 55 additions & 1 deletion src/puya/awst_build/eb/reference_types/account.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

import typing
from typing import Sequence

from immutabledict import immutabledict

Expand All @@ -10,10 +11,16 @@
AddressConstant,
BytesComparisonExpression,
EqualityComparison,
Expression,
IntrinsicCall,
Literal,
TupleItemExpression,
)
from puya.awst_build.eb.base import (
BuilderComparisonOp,
ExpressionBuilder,
IntermediateExpressionBuilder,
)
from puya.awst_build.eb.base import BuilderComparisonOp, ExpressionBuilder
from puya.awst_build.eb.bytes_backed import BytesBackedClassExpressionBuilder
from puya.awst_build.eb.reference_types.base import ReferenceValueExpressionBuilder
from puya.awst_build.eb.var_factory import var_expression
Expand Down Expand Up @@ -57,6 +64,48 @@ def call(
raise CodeError("Invalid/unhandled arguments", location)


class AccountOptedInExpressionBuilder(IntermediateExpressionBuilder):
def __init__(self, expr: Expression, source_location: SourceLocation):
super().__init__(source_location)
self.expr = expr

def call(
self,
args: Sequence[ExpressionBuilder | Literal],
arg_kinds: list[mypy.nodes.ArgKind],
arg_names: list[str | None],
location: SourceLocation,
original_expr: mypy.nodes.CallExpr,
) -> ExpressionBuilder:
match args:
case [ExpressionBuilder(value_type=wtypes.asset_wtype) as asset]:
return var_expression(
TupleItemExpression(
base=IntrinsicCall(
op_code="asset_holding_get",
immediates=["AssetBalance"],
stack_args=[self.expr, asset.rvalue()],
wtype=wtypes.WTuple.from_types(
(wtypes.uint64_wtype, wtypes.bool_wtype)
),
source_location=location,
),
index=1,
source_location=location,
)
)
case [ExpressionBuilder(value_type=wtypes.application_wtype) as app]:
return var_expression(
IntrinsicCall(
op_code="app_opted_in",
stack_args=[self.expr, app.rvalue()],
source_location=location,
wtype=wtypes.bool_wtype,
)
)
raise CodeError("Unexpected argument", location)


class AccountExpressionBuilder(ReferenceValueExpressionBuilder):
wtype = wtypes.account_wtype
native_wtype = wtypes.bytes_wtype
Expand All @@ -80,6 +129,11 @@ class AccountExpressionBuilder(ReferenceValueExpressionBuilder):
field_op_code = "acct_params_get"
field_bool_comment = "account funded"

def member_access(self, name: str, location: SourceLocation) -> ExpressionBuilder | Literal:
if name == "is_opted_in":
return AccountOptedInExpressionBuilder(self.expr, location)
return super().member_access(name, location)

def bool_eval(self, location: SourceLocation, *, negate: bool = False) -> ExpressionBuilder:
cmp_with_zero_expr = BytesComparisonExpression(
source_location=location,
Expand Down
7 changes: 7 additions & 0 deletions src/puyapy-stubs/_reference.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,13 @@ class Account(BytesBacked):
Account must be an available resource
```
"""
def is_opted_in(self, aseet_or_app: Asset | Application) -> bool:
"""Returns true if this account is opted in to the specified Asset or Application.
```{note}
Account and Asset/Application must be an available resource
```
"""

class Asset:
"""An Asset on the Algorand network."""
Expand Down
2 changes: 2 additions & 0 deletions test_cases/application/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
Bytes,
Contract,
LocalState,
Txn,
UInt64,
op,
subroutine,
Expand Down Expand Up @@ -35,6 +36,7 @@ def clear_state_program(self) -> bool:

@subroutine
def validate_asset(self, app: Application) -> None:
assert not Txn.sender.is_opted_in(app), "app opted in"
assert app.creator == op.Global.creator_address, "expected creator"
assert app.global_num_uint == 1, "expected global_num_uint"
assert app.global_num_byte_slice == 2, "expected global_num_byte_slice"
Expand Down
Loading

0 comments on commit e21dc55

Please sign in to comment.