Skip to content

Commit

Permalink
fix: fixed error when assigning result of an abi_call with inner tran…
Browse files Browse the repository at this point in the history
…sactions that return an ABI value
  • Loading branch information
daniel-makerx committed Oct 3, 2024
1 parent ebc55de commit 2a893d3
Show file tree
Hide file tree
Showing 16 changed files with 326 additions and 143 deletions.
2 changes: 1 addition & 1 deletion examples/sizes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
tuple_support/TupleSupport 696 409 - | 381 180 -
typed_abi_call/Greeter 3147 2685 - | 1488 1171 -
typed_abi_call/Logger 1014 838 - | 549 435 -
typed_abi_call_txn/Caller 326 283 - | 160 138 -
typed_abi_call_txn/Caller 336 290 - | 167 143 -
typed_abi_call_txn/Txn 241 188 - | 128 100 -
unary/Unary 130 67 - | 62 27 -
unassigned_expression/Unassigned 158 119 - | 80 58 -
Expand Down
4 changes: 4 additions & 0 deletions src/puya/awst/validation/inner_transactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,10 @@ def _is_assignable_itxn_expr(expr: awst_nodes.Expression) -> bool:
items=items
): # tuple expressions composed of assignable expressions are assignable
return all(map(_is_assignable_itxn_expr, items))
case awst_nodes.TupleItemExpression(
base=base
): # tuple items are assignable if their base is assignable
return _is_assignable_itxn_expr(base)
case awst_nodes.SingleEvaluation(source=source):
return _is_assignable_itxn_expr(source)
# anything else is not considered assignable
Expand Down
3 changes: 2 additions & 1 deletion test_cases/typed_abi_call_txn/caller.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,14 @@ def test_call_with_txn(self, a: Bytes, b: Bytes, app: Application) -> None:
asset_name="TEST",
total=1,
)
arc4.abi_call(
asset_id, _txn = arc4.abi_call(
TxnContract.call_with_txn,
a,
txn,
b,
app_id=app,
)
assert asset_id, "expected asset id"

@arc4.abimethod
def test_call_with_acfg(self, a: Bytes, b: Bytes, app: Application) -> None:
Expand Down
158 changes: 83 additions & 75 deletions test_cases/typed_abi_call_txn/out/Caller.approval.mir

Large diffs are not rendered by default.

37 changes: 22 additions & 15 deletions test_cases/typed_abi_call_txn/out/Caller.approval.teal
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ __puya_arc4_router___test_call_with_txn_route@2:
retsub

__puya_arc4_router___test_call_with_acfg_route@3:
// typed_abi_call_txn/caller.py:28
// typed_abi_call_txn/caller.py:29
// @arc4.abimethod
txn OnCompletion
!
Expand All @@ -59,7 +59,7 @@ __puya_arc4_router___test_call_with_acfg_route@3:
txna ApplicationArgs 3
btoi
txnas Applications
// typed_abi_call_txn/caller.py:28
// typed_abi_call_txn/caller.py:29
// @arc4.abimethod
callsub test_call_with_acfg
int 1
Expand Down Expand Up @@ -90,7 +90,7 @@ test_call_with_txn:
// def test_call_with_txn(self, a: Bytes, b: Bytes, app: Application) -> None:
proto 3 0
// typed_abi_call_txn/caller.py:20-26
// arc4.abi_call(
// asset_id, _txn = arc4.abi_call(
// TxnContract.call_with_txn,
// a,
// txn,
Expand All @@ -117,7 +117,7 @@ test_call_with_txn:
int 0
itxn_field Fee
// typed_abi_call_txn/caller.py:20-26
// arc4.abi_call(
// asset_id, _txn = arc4.abi_call(
// TxnContract.call_with_txn,
// a,
// txn,
Expand All @@ -144,7 +144,7 @@ test_call_with_txn:
frame_dig -1
itxn_field ApplicationID
// typed_abi_call_txn/caller.py:20-26
// arc4.abi_call(
// asset_id, _txn = arc4.abi_call(
// TxnContract.call_with_txn,
// a,
// txn,
Expand All @@ -162,20 +162,27 @@ test_call_with_txn:
itxn_field Fee
itxn_submit
gitxn 1 LastLog
dup
extract 4 0
swap
extract 0 4
byte 0x151f7c75
==
assert // ARC4 prefix is valid
btoi
// typed_abi_call_txn/caller.py:27
// assert asset_id, "expected asset id"
assert // expected asset id
retsub


// test_cases.typed_abi_call_txn.caller.Caller.test_call_with_acfg(a: bytes, b: bytes, app: uint64) -> void:
test_call_with_acfg:
// typed_abi_call_txn/caller.py:28-29
// typed_abi_call_txn/caller.py:29-30
// @arc4.abimethod
// def test_call_with_acfg(self, a: Bytes, b: Bytes, app: Application) -> None:
proto 3 0
// typed_abi_call_txn/caller.py:35-41
// typed_abi_call_txn/caller.py:36-42
// arc4.abi_call(
// TxnContract.call_with_acfg,
// a,
Expand All @@ -184,25 +191,25 @@ test_call_with_acfg:
// app_id=app,
// )
itxn_begin
// typed_abi_call_txn/caller.py:33
// typed_abi_call_txn/caller.py:34
// total=1,
int 1
itxn_field ConfigAssetTotal
// typed_abi_call_txn/caller.py:32
// typed_abi_call_txn/caller.py:33
// asset_name="TEST",
byte "TEST"
itxn_field ConfigAssetName
// typed_abi_call_txn/caller.py:31
// typed_abi_call_txn/caller.py:32
// unit_name="TST",
byte "TST"
itxn_field ConfigAssetUnitName
// typed_abi_call_txn/caller.py:30
// typed_abi_call_txn/caller.py:31
// txn = itxn.AssetConfig(
int acfg
itxn_field TypeEnum
int 0
itxn_field Fee
// typed_abi_call_txn/caller.py:35-41
// typed_abi_call_txn/caller.py:36-42
// arc4.abi_call(
// TxnContract.call_with_acfg,
// a,
Expand All @@ -211,15 +218,15 @@ test_call_with_acfg:
// app_id=app,
// )
itxn_next
// typed_abi_call_txn/caller.py:37
// typed_abi_call_txn/caller.py:38
// a,
frame_dig -3
len
itob
extract 6 2
frame_dig -3
concat
// typed_abi_call_txn/caller.py:39
// typed_abi_call_txn/caller.py:40
// b,
frame_dig -2
len
Expand All @@ -229,7 +236,7 @@ test_call_with_acfg:
concat
frame_dig -1
itxn_field ApplicationID
// typed_abi_call_txn/caller.py:35-41
// typed_abi_call_txn/caller.py:36-42
// arc4.abi_call(
// TxnContract.call_with_acfg,
// a,
Expand Down
2 changes: 1 addition & 1 deletion test_cases/typed_abi_call_txn/out/Caller.arc32.json

Large diffs are not rendered by default.

7 changes: 5 additions & 2 deletions test_cases/typed_abi_call_txn/out/Caller.destructured.ir
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ contract test_cases.typed_abi_call_txn.caller.Caller:
let tmp%13#0: uint64 = ((txnas Applications) tmp%12#0)
test_cases.typed_abi_call_txn.caller.Caller.test_call_with_txn(tmp%8#0, tmp%10#0, tmp%13#0)
return 1u
block@3: // test_call_with_acfg_route_L28
block@3: // test_call_with_acfg_route_L29
let tmp%14#0: uint64 = (txn OnCompletion)
let tmp%15#0: bool = (! tmp%14#0)
(assert tmp%15#0) // OnCompletion is NoOp
Expand Down Expand Up @@ -78,13 +78,16 @@ contract test_cases.typed_abi_call_txn.caller.Caller:
((itxn_field Fee) 0u)
itxn_submit
let awst_tmp%0#0: bytes = (gitxn 1 LastLog)
let tmp%0#0: bytes = ((extract 4 0) awst_tmp%0#0)
let tmp%1#0: bytes = ((extract 0 4) awst_tmp%0#0)
let tmp%2#0: bool = (== tmp%1#0 0x151f7c75)
(assert tmp%2#0) // ARC4 prefix is valid
let asset_id#0: uint64 = (btoi tmp%0#0)
(assert asset_id#0) // expected asset id
return

subroutine test_cases.typed_abi_call_txn.caller.Caller.test_call_with_acfg(a: bytes, b: bytes, app: uint64) -> void:
block@0: // L28
block@0: // L29
itxn_begin
((itxn_field ConfigAssetTotal) 1u)
((itxn_field ConfigAssetName) "TEST")
Expand Down
75 changes: 70 additions & 5 deletions test_cases/typed_abi_call_txn/out/Caller.ssa.ir
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ contract test_cases.typed_abi_call_txn.caller.Caller:
let tmp%13#0: uint64 = ((txnas Applications) tmp%12#0)
test_cases.typed_abi_call_txn.caller.Caller.test_call_with_txn(tmp%8#0, tmp%10#0, tmp%13#0)
return 1u
block@3: // test_call_with_acfg_route_L28
block@3: // test_call_with_acfg_route_L29
let tmp%14#0: uint64 = (txn OnCompletion)
let tmp%15#0: bool = (== tmp%14#0 NoOp)
(assert tmp%15#0) // OnCompletion is NoOp
Expand Down Expand Up @@ -213,11 +213,76 @@ contract test_cases.typed_abi_call_txn.caller.Caller:
let tmp%1#0: bytes = ((extract 0 4) awst_tmp%0#0)
let tmp%2#0: bool = (== tmp%1#0 0x151f7c75)
(assert tmp%2#0) // ARC4 prefix is valid
let tmp%3#0: uint64 = (btoi tmp%0#0)
let asset_id#0: uint64 = (btoi tmp%0#0)
let _txn#0: itxn_group_idx = itxn_group_idx(1)
let _txn._is_last#0: bool = 1u
let _txn.Sender#0: bytes = itxn[_txn#0].Sender
let _txn.Fee#0: uint64 = itxn[_txn#0].Fee
let _txn.FirstValid#0: uint64 = itxn[_txn#0].FirstValid
let _txn.FirstValidTime#0: uint64 = itxn[_txn#0].FirstValidTime
let _txn.LastValid#0: uint64 = itxn[_txn#0].LastValid
let _txn.Note#0: bytes = itxn[_txn#0].Note
let _txn.Lease#0: bytes = itxn[_txn#0].Lease
let _txn.Receiver#0: bytes = itxn[_txn#0].Receiver
let _txn.Amount#0: uint64 = itxn[_txn#0].Amount
let _txn.CloseRemainderTo#0: bytes = itxn[_txn#0].CloseRemainderTo
let _txn.VotePK#0: bytes = itxn[_txn#0].VotePK
let _txn.SelectionPK#0: bytes = itxn[_txn#0].SelectionPK
let _txn.VoteFirst#0: uint64 = itxn[_txn#0].VoteFirst
let _txn.VoteLast#0: uint64 = itxn[_txn#0].VoteLast
let _txn.VoteKeyDilution#0: uint64 = itxn[_txn#0].VoteKeyDilution
let _txn.Type#0: bytes = itxn[_txn#0].Type
let _txn.TypeEnum#0: uint64 = itxn[_txn#0].TypeEnum
let _txn.XferAsset#0: uint64 = itxn[_txn#0].XferAsset
let _txn.AssetAmount#0: uint64 = itxn[_txn#0].AssetAmount
let _txn.AssetSender#0: bytes = itxn[_txn#0].AssetSender
let _txn.AssetReceiver#0: bytes = itxn[_txn#0].AssetReceiver
let _txn.AssetCloseTo#0: bytes = itxn[_txn#0].AssetCloseTo
let _txn.GroupIndex#0: uint64 = itxn[_txn#0].GroupIndex
let _txn.TxID#0: bytes = itxn[_txn#0].TxID
let _txn.ApplicationID#0: uint64 = itxn[_txn#0].ApplicationID
let _txn.OnCompletion#0: uint64 = itxn[_txn#0].OnCompletion
let _txn.NumAppArgs#0: uint64 = itxn[_txn#0].NumAppArgs
let _txn.NumAccounts#0: uint64 = itxn[_txn#0].NumAccounts
let _txn.ApprovalProgram#0: bytes = itxn[_txn#0].ApprovalProgram
let _txn.ClearStateProgram#0: bytes = itxn[_txn#0].ClearStateProgram
let _txn.RekeyTo#0: bytes = itxn[_txn#0].RekeyTo
let _txn.ConfigAsset#0: uint64 = itxn[_txn#0].ConfigAsset
let _txn.ConfigAssetTotal#0: uint64 = itxn[_txn#0].ConfigAssetTotal
let _txn.ConfigAssetDecimals#0: uint64 = itxn[_txn#0].ConfigAssetDecimals
let _txn.ConfigAssetDefaultFrozen#0: bool = itxn[_txn#0].ConfigAssetDefaultFrozen
let _txn.ConfigAssetUnitName#0: bytes = itxn[_txn#0].ConfigAssetUnitName
let _txn.ConfigAssetName#0: bytes = itxn[_txn#0].ConfigAssetName
let _txn.ConfigAssetURL#0: bytes = itxn[_txn#0].ConfigAssetURL
let _txn.ConfigAssetMetadataHash#0: bytes = itxn[_txn#0].ConfigAssetMetadataHash
let _txn.ConfigAssetManager#0: bytes = itxn[_txn#0].ConfigAssetManager
let _txn.ConfigAssetReserve#0: bytes = itxn[_txn#0].ConfigAssetReserve
let _txn.ConfigAssetFreeze#0: bytes = itxn[_txn#0].ConfigAssetFreeze
let _txn.ConfigAssetClawback#0: bytes = itxn[_txn#0].ConfigAssetClawback
let _txn.FreezeAsset#0: uint64 = itxn[_txn#0].FreezeAsset
let _txn.FreezeAssetAccount#0: bytes = itxn[_txn#0].FreezeAssetAccount
let _txn.FreezeAssetFrozen#0: bool = itxn[_txn#0].FreezeAssetFrozen
let _txn.NumAssets#0: uint64 = itxn[_txn#0].NumAssets
let _txn.NumApplications#0: uint64 = itxn[_txn#0].NumApplications
let _txn.GlobalNumUint#0: uint64 = itxn[_txn#0].GlobalNumUint
let _txn.GlobalNumByteSlice#0: uint64 = itxn[_txn#0].GlobalNumByteSlice
let _txn.LocalNumUint#0: uint64 = itxn[_txn#0].LocalNumUint
let _txn.LocalNumByteSlice#0: uint64 = itxn[_txn#0].LocalNumByteSlice
let _txn.ExtraProgramPages#0: uint64 = itxn[_txn#0].ExtraProgramPages
let _txn.Nonparticipation#0: bool = itxn[_txn#0].Nonparticipation
let _txn.NumLogs#0: uint64 = itxn[_txn#0].NumLogs
let _txn.CreatedAssetID#0: uint64 = itxn[_txn#0].CreatedAssetID
let _txn.CreatedApplicationID#0: uint64 = itxn[_txn#0].CreatedApplicationID
let _txn.LastLog#0: bytes = itxn[_txn#0].LastLog
let _txn.StateProofPK#0: bytes = itxn[_txn#0].StateProofPK
let _txn.NumApprovalProgramPages#0: uint64 = itxn[_txn#0].NumApprovalProgramPages
let _txn.NumClearStateProgramPages#0: uint64 = itxn[_txn#0].NumClearStateProgramPages
let tmp%3#0: bool = (!= asset_id#0 0u)
(assert tmp%3#0) // expected asset id
return

subroutine test_cases.typed_abi_call_txn.caller.Caller.test_call_with_acfg(a: bytes, b: bytes, app: uint64) -> void:
block@0: // L28
block@0: // L29
let txn#0: itxn_field_set = itxn_field_set(0)
let txn%%param_Fee_idx_0#0: uint64 = 0u
let txn%%Fee_length#0: uint64 = 1u
Expand Down Expand Up @@ -282,7 +347,7 @@ contract test_cases.typed_abi_call_txn.caller.Caller:
((itxn_field TypeEnum) txn%%param_TypeEnum_idx_0#0)
((itxn_field Fee) txn%%param_Fee_idx_0#0)
goto block@1
block@1: // next_txn_L38
block@1: // next_txn_L39
itxn_next
let inner_txn_params%0#0: itxn_field_set = itxn_field_set(1)
let inner_txn_params%0%%param_Fee_idx_0#0: uint64 = 0u
Expand Down Expand Up @@ -357,7 +422,7 @@ contract test_cases.typed_abi_call_txn.caller.Caller:
((itxn_field TypeEnum) inner_txn_params%0%%param_TypeEnum_idx_0#0)
((itxn_field Fee) inner_txn_params%0%%param_Fee_idx_0#0)
goto block@2
block@2: // next_txn_L35
block@2: // next_txn_L36
itxn_submit
let awst_tmp%0#0: bytes = itxn[itxn_group_idx(1)].LastLog
let tmp%0#0: bytes = ((extract 4 0) awst_tmp%0#0)
Expand Down
6 changes: 4 additions & 2 deletions test_cases/typed_abi_call_txn/out/Caller.ssa.opt_pass_1.ir
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ contract test_cases.typed_abi_call_txn.caller.Caller:
let tmp%13#0: uint64 = ((txnas Applications) tmp%12#0)
test_cases.typed_abi_call_txn.caller.Caller.test_call_with_txn(tmp%8#0, tmp%10#0, tmp%13#0)
return 1u
block@3: // test_call_with_acfg_route_L28
block@3: // test_call_with_acfg_route_L29
let tmp%14#0: uint64 = (txn OnCompletion)
let tmp%15#0: bool = (! tmp%14#0)
(assert tmp%15#0) // OnCompletion is NoOp
Expand Down Expand Up @@ -82,10 +82,12 @@ contract test_cases.typed_abi_call_txn.caller.Caller:
let tmp%1#0: bytes = ((extract 0 4) awst_tmp%0#0)
let tmp%2#0: bool = (== tmp%1#0 0x151f7c75)
(assert tmp%2#0) // ARC4 prefix is valid
let asset_id#0: uint64 = (btoi tmp%0#0)
(assert asset_id#0) // expected asset id
return

subroutine test_cases.typed_abi_call_txn.caller.Caller.test_call_with_acfg(a: bytes, b: bytes, app: uint64) -> void:
block@0: // L28
block@0: // L29
itxn_begin
((itxn_field ConfigAssetTotal) 1u)
((itxn_field ConfigAssetName) "TEST")
Expand Down
7 changes: 5 additions & 2 deletions test_cases/typed_abi_call_txn/out/Caller.ssa.opt_pass_2.ir
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ contract test_cases.typed_abi_call_txn.caller.Caller:
let tmp%13#0: uint64 = ((txnas Applications) tmp%12#0)
test_cases.typed_abi_call_txn.caller.Caller.test_call_with_txn(tmp%8#0, tmp%10#0, tmp%13#0)
return 1u
block@3: // test_call_with_acfg_route_L28
block@3: // test_call_with_acfg_route_L29
let tmp%14#0: uint64 = (txn OnCompletion)
let tmp%15#0: bool = (! tmp%14#0)
(assert tmp%15#0) // OnCompletion is NoOp
Expand Down Expand Up @@ -78,13 +78,16 @@ contract test_cases.typed_abi_call_txn.caller.Caller:
((itxn_field Fee) 0u)
itxn_submit
let awst_tmp%0#0: bytes = (gitxn 1 LastLog)
let tmp%0#0: bytes = ((extract 4 0) awst_tmp%0#0)
let tmp%1#0: bytes = ((extract 0 4) awst_tmp%0#0)
let tmp%2#0: bool = (== tmp%1#0 0x151f7c75)
(assert tmp%2#0) // ARC4 prefix is valid
let asset_id#0: uint64 = (btoi tmp%0#0)
(assert asset_id#0) // expected asset id
return

subroutine test_cases.typed_abi_call_txn.caller.Caller.test_call_with_acfg(a: bytes, b: bytes, app: uint64) -> void:
block@0: // L28
block@0: // L29
itxn_begin
((itxn_field ConfigAssetTotal) 1u)
((itxn_field ConfigAssetName) "TEST")
Expand Down
3 changes: 2 additions & 1 deletion test_cases/typed_abi_call_txn/out/module.awst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ contract Caller extends (algopy.arc4.ARC4Contract)
abimethod test_call_with_txn(a: bytes, b: bytes, app: application): void
{
txn: inner_transaction_fields_acfg = create_inner_transaction(Fee=0u, TypeEnum=acfg, ConfigAssetUnitName='TST', ConfigAssetName='TEST', ConfigAssetTotal=1u)
(arc4_decode(checked_maybe((extract<4, 0>(SINGLE_EVAL(id=2, source=SINGLE_EVAL(id=1, source=SINGLE_EVAL(id=0, source=submit_txn(txn, create_inner_transaction(Fee=0u, TypeEnum=appl, ApplicationArgs=(Method("call_with_txn(byte[],txn,byte[])uint64"), arc4_encode(a, arc4.dynamic_array<arc4.uint8>), arc4_encode(b, arc4.dynamic_array<arc4.uint8>)), ApplicationID=app)))[-1]).LastLog)), extract<0, 4>(SINGLE_EVAL(id=2)) == hex<"151F7C75">)), uint64), SINGLE_EVAL(id=1))
(asset_id, _txn): tuple<uint64,inner_transaction_appl> = (arc4_decode(checked_maybe((extract<4, 0>(SINGLE_EVAL(id=2, source=SINGLE_EVAL(id=1, source=SINGLE_EVAL(id=0, source=submit_txn(txn, create_inner_transaction(Fee=0u, TypeEnum=appl, ApplicationArgs=(Method("call_with_txn(byte[],txn,byte[])uint64"), arc4_encode(a, arc4.dynamic_array<arc4.uint8>), arc4_encode(b, arc4.dynamic_array<arc4.uint8>)), ApplicationID=app)))[-1]).LastLog)), extract<0, 4>(SINGLE_EVAL(id=2)) == hex<"151F7C75">)), uint64), SINGLE_EVAL(id=1))
assert(asset_id != 0u, comment="expected asset id")
}

abimethod test_call_with_acfg(a: bytes, b: bytes, app: application): void
Expand Down
5 changes: 5 additions & 0 deletions test_cases/typed_abi_call_txn/out_O2/Caller.approval.teal
Original file line number Diff line number Diff line change
Expand Up @@ -105,10 +105,15 @@ test_call_with_txn:
itxn_field Fee
itxn_submit
gitxn 1 LastLog
dup
extract 4 0
swap
extract 0 4
byte 0x151f7c75
==
assert // ARC4 prefix is valid
btoi
assert // expected asset id
retsub


Expand Down
Loading

0 comments on commit 2a893d3

Please sign in to comment.