diff --git a/cmd/goal/application.go b/cmd/goal/application.go index 2758a27125..49a2ce9c58 100644 --- a/cmd/goal/application.go +++ b/cmd/goal/application.go @@ -1047,6 +1047,43 @@ var infoAppCmd = &cobra.Command{ }, } +// populateMethodCallTxnArgs parses and loads transactions from the files indicated by the values +// slice. An error will occur if the transaction does not matched the expected type, it has a nonzero +// group ID, or if it is signed by a normal signature or Msig signature (but not Lsig signature) +func populateMethodCallTxnArgs(types []string, values []string) ([]transactions.SignedTxn, error) { + loadedTxns := make([]transactions.SignedTxn, len(values)) + + for i, txFilename := range values { + data, err := readFile(txFilename) + if err != nil { + return nil, fmt.Errorf(fileReadError, txFilename, err) + } + + var txn transactions.SignedTxn + err = protocol.Decode(data, &txn) + if err != nil { + return nil, fmt.Errorf(txDecodeError, txFilename, err) + } + + if !txn.Sig.Blank() || !txn.Msig.Blank() { + return nil, fmt.Errorf("Transaction from %s has already been signed", txFilename) + } + + if !txn.Txn.Group.IsZero() { + return nil, fmt.Errorf("Transaction from %s already has a group ID: %s", txFilename, txn.Txn.Group) + } + + expectedType := types[i] + if expectedType != "txn" && txn.Txn.Type != protocol.TxType(expectedType) { + return nil, fmt.Errorf("Transaction from %s does not match method argument type. Expected %s, got %s", txFilename, expectedType, txn.Txn.Type) + } + + loadedTxns[i] = txn + } + + return loadedTxns, nil +} + var methodAppCmd = &cobra.Command{ Use: "method", Short: "Invoke a method", @@ -1079,16 +1116,50 @@ var methodAppCmd = &cobra.Command{ applicationArgs = append(applicationArgs, hash[0:4]) // parse down the ABI type from method signature - argTupleTypeStr, retTypeStr, err := abi.ParseMethodSignature(method) + _, argTypes, retTypeStr, err := abi.ParseMethodSignature(method) if err != nil { reportErrorf("cannot parse method signature: %v", err) } - err = abi.ParseArgJSONtoByteSlice(argTupleTypeStr, methodArgs, &applicationArgs) + + var retType *abi.Type + if retTypeStr != "void" { + theRetType, err := abi.TypeOf(retTypeStr) + if err != nil { + reportErrorf("cannot cast %s to abi type: %v", retTypeStr, err) + } + retType = &theRetType + } + + if len(methodArgs) != len(argTypes) { + reportErrorf("incorrect number of arguments, method expected %d but got %d", len(argTypes), len(methodArgs)) + } + + var txnArgTypes []string + var txnArgValues []string + var basicArgTypes []string + var basicArgValues []string + for i, argType := range argTypes { + argValue := methodArgs[i] + if abi.IsTransactionType(argType) { + txnArgTypes = append(txnArgTypes, argType) + txnArgValues = append(txnArgValues, argValue) + } else { + basicArgTypes = append(basicArgTypes, argType) + basicArgValues = append(basicArgValues, argValue) + } + } + + err = abi.ParseArgJSONtoByteSlice(basicArgTypes, basicArgValues, &applicationArgs) if err != nil { reportErrorf("cannot parse arguments to ABI encoding: %v", err) } - tx, err := client.MakeUnsignedApplicationCallTx( + txnArgs, err := populateMethodCallTxnArgs(txnArgTypes, txnArgValues) + if err != nil { + reportErrorf("error populating transaction arguments: %v", err) + } + + appCallTxn, err := client.MakeUnsignedApplicationCallTx( appIdx, applicationArgs, appAccounts, foreignApps, foreignAssets, onCompletionEnum, approvalProg, clearProg, basics.StateSchema{}, basics.StateSchema{}, 0) @@ -1097,8 +1168,8 @@ var methodAppCmd = &cobra.Command{ } // Fill in note and lease - tx.Note = parseNoteField(cmd) - tx.Lease = parseLease(cmd) + appCallTxn.Note = parseNoteField(cmd) + appCallTxn.Lease = parseLease(cmd) // Fill in rounds, fee, etc. fv, lv, err := client.ComputeValidityRounds(firstValid, lastValid, numValidRounds) @@ -1106,23 +1177,65 @@ var methodAppCmd = &cobra.Command{ reportErrorf("Cannot determine last valid round: %s", err) } - tx, err = client.FillUnsignedTxTemplate(account, fv, lv, fee, tx) + appCallTxn, err = client.FillUnsignedTxTemplate(account, fv, lv, fee, appCallTxn) if err != nil { reportErrorf("Cannot construct transaction: %s", err) } explicitFee := cmd.Flags().Changed("fee") if explicitFee { - tx.Fee = basics.MicroAlgos{Raw: fee} + appCallTxn.Fee = basics.MicroAlgos{Raw: fee} + } + + // Compile group + var txnGroup []transactions.Transaction + for i := range txnArgs { + txnGroup = append(txnGroup, txnArgs[i].Txn) + } + txnGroup = append(txnGroup, appCallTxn) + if len(txnGroup) > 1 { + // Only if transaction arguments are present, assign group ID + groupID, err := client.GroupID(txnGroup) + if err != nil { + reportErrorf("Cannot assign transaction group ID: %s", err) + } + for i := range txnGroup { + txnGroup[i].Group = groupID + } } + // Sign transactions + var signedTxnGroup []transactions.SignedTxn + shouldSign := sign || outFilename == "" + for i, unsignedTxn := range txnGroup { + txnFromArgs := transactions.SignedTxn{} + if i < len(txnArgs) { + txnFromArgs = txnArgs[i] + } + + if !txnFromArgs.Lsig.Blank() { + signedTxnGroup = append(signedTxnGroup, transactions.SignedTxn{ + Lsig: txnFromArgs.Lsig, + AuthAddr: txnFromArgs.AuthAddr, + Txn: unsignedTxn, + }) + continue + } + + signedTxn, err := createSignedTransaction(client, shouldSign, dataDir, walletName, unsignedTxn, txnFromArgs.AuthAddr) + if err != nil { + reportErrorf(errorSigningTX, err) + } + + signedTxnGroup = append(signedTxnGroup, signedTxn) + } + + // Output to file if outFilename != "" { if dumpForDryrun { - err = writeDryrunReqToFile(client, tx, outFilename) + err = writeDryrunReqToFile(client, signedTxnGroup, outFilename) } else { - // Write transaction to file - err = writeTxnToFile(client, sign, dataDir, walletName, tx, outFilename) + err = writeSignedTxnsToFile(signedTxnGroup, outFilename) } - if err != nil { reportErrorf(err.Error()) } @@ -1130,19 +1243,19 @@ var methodAppCmd = &cobra.Command{ } // Broadcast - wh, pw := ensureWalletHandleMaybePassword(dataDir, walletName, true) - signedTxn, err := client.SignTransactionWithWallet(wh, pw, tx) - if err != nil { - reportErrorf(errorSigningTX, err) - } - - txid, err := client.BroadcastTransaction(signedTxn) + err = client.BroadcastTransactionGroup(signedTxnGroup) if err != nil { reportErrorf(errorBroadcastingTX, err) } // Report tx details to user - reportInfof("Issued transaction from account %s, txid %s (fee %d)", tx.Sender, txid, tx.Fee.Raw) + reportInfof("Issued %d transaction(s):", len(signedTxnGroup)) + // remember the final txid in this variable + var txid string + for _, stxn := range signedTxnGroup { + txid = stxn.Txn.ID().String() + reportInfof("\tIssued transaction from account %s, txid %s (fee %d)", stxn.Txn.Sender, txid, stxn.Txn.Fee.Raw) + } if !noWaitAfterSend { _, err := waitForCommit(client, txid, lv) @@ -1155,8 +1268,8 @@ var methodAppCmd = &cobra.Command{ reportErrorf(err.Error()) } - if retTypeStr == "void" { - fmt.Printf("method %s succeeded", method) + if retType == nil { + fmt.Printf("method %s succeeded\n", method) return } @@ -1181,10 +1294,6 @@ var methodAppCmd = &cobra.Command{ reportErrorf("cannot find return log for abi type %s", retTypeStr) } - retType, err := abi.TypeOf(retTypeStr) - if err != nil { - reportErrorf("cannot cast %s to abi type: %v", retTypeStr, err) - } decoded, err := retType.Decode(abiEncodedRet) if err != nil { reportErrorf("cannot decode return value %v: %v", abiEncodedRet, err) @@ -1194,7 +1303,7 @@ var methodAppCmd = &cobra.Command{ if err != nil { reportErrorf("cannot marshal returned bytes %v to JSON: %v", decoded, err) } - fmt.Printf("method %s succeeded with output: %s", method, string(decodedJSON)) + fmt.Printf("method %s succeeded with output: %s\n", method, string(decodedJSON)) } }, } diff --git a/cmd/goal/clerk.go b/cmd/goal/clerk.go index ed5392f5a1..c5d69854a5 100644 --- a/cmd/goal/clerk.go +++ b/cmd/goal/clerk.go @@ -194,34 +194,45 @@ func waitForCommit(client libgoal.Client, txid string, transactionLastValidRound return } -func createSignedTransaction(client libgoal.Client, signTx bool, dataDir string, walletName string, tx transactions.Transaction) (stxn transactions.SignedTxn, err error) { +func createSignedTransaction(client libgoal.Client, signTx bool, dataDir string, walletName string, tx transactions.Transaction, signer basics.Address) (stxn transactions.SignedTxn, err error) { if signTx { // Sign the transaction wh, pw := ensureWalletHandleMaybePassword(dataDir, walletName, true) - stxn, err = client.SignTransactionWithWallet(wh, pw, tx) - if err != nil { - return - } - } else { - // Wrap in a transactions.SignedTxn with an empty sig. - // This way protocol.Encode will encode the transaction type - stxn, err = transactions.AssembleSignedTxn(tx, crypto.Signature{}, crypto.MultisigSig{}) - if err != nil { - return + if signer.IsZero() { + stxn, err = client.SignTransactionWithWallet(wh, pw, tx) + } else { + stxn, err = client.SignTransactionWithWalletAndSigner(wh, pw, signer.String(), tx) } + return + } - stxn = populateBlankMultisig(client, dataDir, walletName, stxn) + // Wrap in a transactions.SignedTxn with an empty sig. + // This way protocol.Encode will encode the transaction type + stxn, err = transactions.AssembleSignedTxn(tx, crypto.Signature{}, crypto.MultisigSig{}) + if err != nil { + return } + + stxn = populateBlankMultisig(client, dataDir, walletName, stxn) return } +func writeSignedTxnsToFile(stxns []transactions.SignedTxn, filename string) error { + var outData []byte + for _, stxn := range stxns { + outData = append(outData, protocol.Encode(&stxn)...) + } + + return writeFile(filename, outData, 0600) +} + func writeTxnToFile(client libgoal.Client, signTx bool, dataDir string, walletName string, tx transactions.Transaction, filename string) error { - stxn, err := createSignedTransaction(client, signTx, dataDir, walletName, tx) + stxn, err := createSignedTransaction(client, signTx, dataDir, walletName, tx, basics.Address{}) if err != nil { return err } // Write the SignedTxn to the output file - return writeFile(filename, protocol.Encode(&stxn), 0600) + return writeSignedTxnsToFile([]transactions.SignedTxn{stxn}, filename) } func getB64Args(args []string) [][]byte { @@ -419,7 +430,7 @@ var sendCmd = &cobra.Command{ } } else { signTx := sign || (outFilename == "") - stx, err = createSignedTransaction(client, signTx, dataDir, walletName, payment) + stx, err = createSignedTransaction(client, signTx, dataDir, walletName, payment, basics.Address{}) if err != nil { reportErrorf(errorSigningTX, err) } @@ -854,13 +865,12 @@ var groupCmd = &cobra.Command{ transactionIdx++ } - var outData []byte - for _, stxn := range stxns { - stxn.Txn.Group = crypto.HashObj(group) - outData = append(outData, protocol.Encode(&stxn)...) + groupHash := crypto.HashObj(group) + for i := range stxns { + stxns[i].Txn.Group = groupHash } - err = writeFile(outFilename, outData, 0600) + err = writeSignedTxnsToFile(stxns, outFilename) if err != nil { reportErrorf(fileWriteError, outFilename, err) } diff --git a/data/abi/abi_encode.go b/data/abi/abi_encode.go index fa5dbd57c8..397e66856f 100644 --- a/data/abi/abi_encode.go +++ b/data/abi/abi_encode.go @@ -481,37 +481,39 @@ func decodeTuple(encoded []byte, childT []Type) ([]interface{}, error) { // ParseArgJSONtoByteSlice convert input method arguments to ABI encoded bytes // it converts funcArgTypes into a tuple type and apply changes over input argument string (in JSON format) // if there are greater or equal to 15 inputs, then we compact the tailing inputs into one tuple -func ParseArgJSONtoByteSlice(funcArgTypes string, jsonArgs []string, applicationArgs *[][]byte) error { - abiTupleT, err := TypeOf(funcArgTypes) - if err != nil { - return err +func ParseArgJSONtoByteSlice(argTypes []string, jsonArgs []string, applicationArgs *[][]byte) error { + abiTypes := make([]Type, len(argTypes)) + for i, typeString := range argTypes { + abiType, err := TypeOf(typeString) + if err != nil { + return err + } + abiTypes[i] = abiType } - if len(abiTupleT.childTypes) != len(jsonArgs) { - return fmt.Errorf("input argument number %d != method argument number %d", len(jsonArgs), len(abiTupleT.childTypes)) + + if len(abiTypes) != len(jsonArgs) { + return fmt.Errorf("input argument number %d != method argument number %d", len(jsonArgs), len(abiTypes)) } // change the input args to be 1 - 14 + 15 (compacting everything together) if len(jsonArgs) > 14 { - compactedType, err := MakeTupleType(abiTupleT.childTypes[14:]) + compactedType, err := MakeTupleType(abiTypes[14:]) if err != nil { return err } - abiTupleT.childTypes = abiTupleT.childTypes[:14] - abiTupleT.childTypes = append(abiTupleT.childTypes, compactedType) - abiTupleT.staticLength = 15 + abiTypes = append(abiTypes[:14], compactedType) remainingJSON := "[" + strings.Join(jsonArgs[14:], ",") + "]" - jsonArgs = jsonArgs[:14] - jsonArgs = append(jsonArgs, remainingJSON) + jsonArgs = append(jsonArgs[:14], remainingJSON) } // parse JSON value to ABI encoded bytes for i := 0; i < len(jsonArgs); i++ { - interfaceVal, err := abiTupleT.childTypes[i].UnmarshalFromJSON([]byte(jsonArgs[i])) + interfaceVal, err := abiTypes[i].UnmarshalFromJSON([]byte(jsonArgs[i])) if err != nil { return err } - abiEncoded, err := abiTupleT.childTypes[i].Encode(interfaceVal) + abiEncoded, err := abiTypes[i].Encode(interfaceVal) if err != nil { return err } @@ -520,30 +522,41 @@ func ParseArgJSONtoByteSlice(funcArgTypes string, jsonArgs []string, application return nil } -// ParseMethodSignature parses a method of format `method(...argTypes...)retType` -// into `(...argTypes)` and `retType` -func ParseMethodSignature(methodSig string) (string, string, error) { - var stack []int - - for index, chr := range methodSig { - if chr == '(' { - stack = append(stack, index) - } else if chr == ')' { - if len(stack) == 0 { - break +// ParseMethodSignature parses a method of format `method(argType1,argType2,...)retType` +// into `method` {`argType1`,`argType2`,..} and `retType` +func ParseMethodSignature(methodSig string) (name string, argTypes []string, returnType string, err error) { + argsStart := strings.Index(methodSig, "(") + if argsStart == -1 { + err = fmt.Errorf("Invalid method signature: %s", methodSig) + return + } + + argsEnd := -1 + depth := 0 + for index, char := range methodSig { + switch char { + case '(': + depth++ + case ')': + if depth == 0 { + err = fmt.Errorf("Unpaired parenthesis in method signature: %s", methodSig) + return } - leftParenIndex := stack[len(stack)-1] - stack = stack[:len(stack)-1] - if len(stack) == 0 { - returnType := methodSig[index+1:] - if _, err := TypeOf(returnType); err != nil { - if returnType != "void" { - return "", "", fmt.Errorf("cannot infer return type: %s", returnType) - } - } - return methodSig[leftParenIndex : index+1], methodSig[index+1:], nil + depth-- + if depth == 0 { + argsEnd = index + break } } } - return "", "", fmt.Errorf("unpaired parentheses: %s", methodSig) + + if argsEnd == -1 { + err = fmt.Errorf("Invalid method signature: %s", methodSig) + return + } + + name = methodSig[:argsStart] + argTypes, err = parseTupleContent(methodSig[argsStart+1 : argsEnd]) + returnType = methodSig[argsEnd+1:] + return } diff --git a/data/abi/abi_type.go b/data/abi/abi_type.go index eb93f9eea1..3535170277 100644 --- a/data/abi/abi_type.go +++ b/data/abi/abi_type.go @@ -457,3 +457,14 @@ func (t Type) ByteLen() (int, error) { return -1, fmt.Errorf("%s is a dynamic type", t.String()) } } + +// IsTransactionType checks if a type string represents a transaction type +// argument, such as "txn", "pay", "keyreg", etc. +func IsTransactionType(s string) bool { + switch s { + case "txn", "pay", "keyreg", "acfg", "axfer", "afrz", "appl": + return true + default: + return false + } +} diff --git a/libgoal/libgoal.go b/libgoal/libgoal.go index b7f8cf519c..2563adf6ee 100644 --- a/libgoal/libgoal.go +++ b/libgoal/libgoal.go @@ -1037,17 +1037,24 @@ func MakeDryrunState(client Client, txnOrStxn interface{}, otherTxns []transacti } // MakeDryrunStateGenerated function creates generatedV2.DryrunRequest data structure -func MakeDryrunStateGenerated(client Client, txnOrStxn interface{}, otherTxns []transactions.SignedTxn, otherAccts []basics.Address, proto string) (dr generatedV2.DryrunRequest, err error) { +func MakeDryrunStateGenerated(client Client, txnOrStxnOrSlice interface{}, otherTxns []transactions.SignedTxn, otherAccts []basics.Address, proto string) (dr generatedV2.DryrunRequest, err error) { var txns []transactions.SignedTxn - if txnOrStxn == nil { - // empty input do nothing - } else if txn, ok := txnOrStxn.(transactions.Transaction); ok { - txns = append(txns, transactions.SignedTxn{Txn: txn}) - } else if stxn, ok := txnOrStxn.(transactions.SignedTxn); ok { - txns = append(txns, stxn) - } else { - err = fmt.Errorf("unsupported txn type") - return + if txnOrStxnOrSlice != nil { + switch txnType := txnOrStxnOrSlice.(type) { + case transactions.Transaction: + txns = append(txns, transactions.SignedTxn{Txn: txnType}) + case []transactions.Transaction: + for _, t := range txnType { + txns = append(txns, transactions.SignedTxn{Txn: t}) + } + case transactions.SignedTxn: + txns = append(txns, txnType) + case []transactions.SignedTxn: + txns = append(txns, txnType...) + default: + err = fmt.Errorf("unsupported txn type") + return + } } txns = append(txns, otherTxns...) diff --git a/test/scripts/e2e_subs/e2e-app-abi-add.sh b/test/scripts/e2e_subs/e2e-app-abi-method.sh similarity index 53% rename from test/scripts/e2e_subs/e2e-app-abi-add.sh rename to test/scripts/e2e_subs/e2e-app-abi-method.sh index 4ee0494b63..ec3a0d71e7 100755 --- a/test/scripts/e2e_subs/e2e-app-abi-add.sh +++ b/test/scripts/e2e_subs/e2e-app-abi-method.sh @@ -1,6 +1,6 @@ #!/bin/bash -date '+app-abi-add-test start %Y%m%d_%H%M%S' +date '+app-abi-method-test start %Y%m%d_%H%M%S' set -e set -x @@ -18,13 +18,13 @@ ACCOUNT=$(${gcmd} account list|awk '{ print $3 }') printf '#pragma version 2\nint 1' > "${TEMPDIR}/simple.teal" PROGRAM=($(${gcmd} clerk compile "${TEMPDIR}/simple.teal")) -APPID=$(${gcmd} app create --creator ${ACCOUNT} --approval-prog ${DIR}/tealprogs/app-abi-add-example.teal --clear-prog ${TEMPDIR}/simple.teal --global-byteslices 0 --global-ints 0 --local-byteslices 1 --local-ints 0 | grep Created | awk '{ print $6 }') +APPID=$(${gcmd} app create --creator ${ACCOUNT} --approval-prog ${DIR}/tealprogs/app-abi-method-example.teal --clear-prog ${TEMPDIR}/simple.teal --global-byteslices 0 --global-ints 0 --local-byteslices 1 --local-ints 0 | grep Created | awk '{ print $6 }') # Opt in RES=$(${gcmd} app method --method "optIn(string)string" --arg "\"Algorand Fan\"" --on-completion optin --app-id $APPID --from $ACCOUNT 2>&1 || true) EXPECTED="method optIn(string)string succeeded with output: \"hello Algorand Fan\"" if [[ $RES != *"${EXPECTED}"* ]]; then - date '+app-abi-add-test FAIL the method call to optIn(string)string should not fail %Y%m%d_%H%M%S' + date '+app-abi-method-test FAIL the method call to optIn(string)string should not fail %Y%m%d_%H%M%S' false fi @@ -32,7 +32,7 @@ fi RES=$(${gcmd} app method --method "add(uint64,uint64)uint64" --arg 1 --arg 2 --app-id $APPID --from $ACCOUNT 2>&1 || true) EXPECTED="method add(uint64,uint64)uint64 succeeded with output: 3" if [[ $RES != *"${EXPECTED}"* ]]; then - date '+app-abi-add-test FAIL the method call to add(uint64,uint64)uint64 should not fail %Y%m%d_%H%M%S' + date '+app-abi-method-test FAIL the method call to add(uint64,uint64)uint64 should not fail %Y%m%d_%H%M%S' false fi @@ -40,7 +40,25 @@ fi RES=$(${gcmd} app method --method "add(uint64,uint64)uint64" --arg 18446744073709551614 --arg 1 --app-id $APPID --from $ACCOUNT 2>&1 || true) EXPECTED="method add(uint64,uint64)uint64 succeeded with output: 18446744073709551615" if [[ $RES != *"${EXPECTED}"* ]]; then - date '+app-abi-add-test FAIL the method call to add(uint64,uint64)uint64 should not fail %Y%m%d_%H%M%S' + date '+app-abi-method-test FAIL the method call to add(uint64,uint64)uint64 should not fail %Y%m%d_%H%M%S' + false +fi + +goal clerk send --from $ACCOUNT --to $ACCOUNT --amount 1000000 -o "${TEMPDIR}/pay-txn-arg.tx" + +# Payment with return true +RES=$(${gcmd} app method --method "payment(pay,uint64)bool" --arg ${TEMPDIR}/pay-txn-arg.tx --arg 1000000 --app-id $APPID --from $ACCOUNT 2>&1 || true) +EXPECTED="method payment(pay,uint64)bool succeeded with output: true" +if [[ $RES != *"${EXPECTED}"* ]]; then + date '+app-abi-method-test FAIL the method call to payment(pay,uint64)bool should not fail %Y%m%d_%H%M%S' + false +fi + +# Payment with return false +RES=$(${gcmd} app method --method "payment(pay,uint64)bool" --arg ${TEMPDIR}/pay-txn-arg.tx --arg 1000001 --app-id $APPID --from $ACCOUNT 2>&1 || true) +EXPECTED="method payment(pay,uint64)bool succeeded with output: false" +if [[ $RES != *"${EXPECTED}"* ]]; then + date '+app-abi-method-test FAIL the method call to payment(pay,uint64)bool should not fail %Y%m%d_%H%M%S' false fi @@ -48,7 +66,7 @@ fi RES=$(${gcmd} app method --method "closeOut()string" --on-completion closeout --app-id $APPID --from $ACCOUNT 2>&1 || true) EXPECTED="method closeOut()string succeeded with output: \"goodbye Algorand Fan\"" if [[ $RES != *"${EXPECTED}"* ]]; then - date '+app-abi-add-test FAIL the method call to closeOut()string should not fail %Y%m%d_%H%M%S' + date '+app-abi-method-test FAIL the method call to closeOut()string should not fail %Y%m%d_%H%M%S' false fi @@ -56,6 +74,6 @@ fi RES=$(${gcmd} app method --method "delete()void" --on-completion deleteapplication --app-id $APPID --from $ACCOUNT 2>&1 || true) EXPECTED="method delete()void succeeded" if [[ $RES != *"${EXPECTED}"* ]]; then - date '+app-abi-add-test FAIL the method call to delete()void should not fail %Y%m%d_%H%M%S' + date '+app-abi-method-test FAIL the method call to delete()void should not fail %Y%m%d_%H%M%S' false fi diff --git a/test/scripts/e2e_subs/tealprogs/app-abi-add-example.teal b/test/scripts/e2e_subs/tealprogs/app-abi-method-example.teal similarity index 93% rename from test/scripts/e2e_subs/tealprogs/app-abi-add-example.teal rename to test/scripts/e2e_subs/tealprogs/app-abi-method-example.teal index a2c8168765..dbc831d7a7 100644 --- a/test/scripts/e2e_subs/tealprogs/app-abi-add-example.teal +++ b/test/scripts/e2e_subs/tealprogs/app-abi-method-example.teal @@ -48,7 +48,7 @@ txn OnCompletion int NoOp == txna ApplicationArgs 0 -byte 0x535a47ba +byte 0x3e3b3d28 == && bnz main_l8 @@ -158,7 +158,19 @@ int pay == assert byte 0x151f7c75 +txn GroupIndex +int 1 +- +gtxns Amount +load 5 +btoi +== +bnz sub5_l2 +byte 0x00 +b sub5_l3 +sub5_l2: byte 0x80 +sub5_l3: concat log -retsub \ No newline at end of file +retsub