Application args vs. signature mode app args

I am trying to develop a toy contract akin to the one described here:

I would like the contract to allow the dispersal of funds to one of several addresses, where the number of addresses that are allowed is variable. For example, if the contract was created with 2 arguments, where both of the 2 arguments are account addresses, then either of those addresses would be able to withdraw funds from the account. The PyTeal code looks like:

def contract_prog():
  safety_cond = And(
    Txn.type_enum() == TxnType.Payment,
    Txn.close_remainder_to() == Global.zero_address(),
    Txn.rekey_to() == Global.zero_address(),
  )

  one_receiver = If(
    Txn.application_args.length() == Int(1),
    Txn.receiver() == Arg(0),
    Int(0),
  )

  two_receivers = If(
    Txn.application_args.length() == Int(2),
    Or(
      Txn.receiver() == Arg(0),
      Txn.receiver() == Arg(1),
    ),
    Int(0),
  )

  three_receivers = If(
    Txn.application_args.length() == Int(3),
    Or(
      Txn.receiver() == Arg(0),
      Txn.receiver() == Arg(1),
      Txn.receiver() == Arg(2),
    ),
    Int(0),
  )

  return And(safety_cond, Or(one_receiver, two_receivers, three_receivers))

However, this does not seem to work with the following transaction because the Txn.application_args.length() appears to always be 0:

{
  "lsig": {
    "arg": [
      "U1BSSElEVDRTRlBXNkJEQVg2UlNHUDRUSVBKQktSNklSNFBLV0dTRlVHNlQ0R1pXVUFJUVhUREJGSQ==",
      "VFBVRzc2N0xLUDU2V1VMVFFWVk5HMklFTEw1SU5JQ1AzSkNIWFVYSkwyVVlPUU1aWDVDTlVBVVI1UQ=="
    ],
    "l": "// version 2\nintcblock 1 0\ntxn TypeEnum\nintc_0\n==\ntxn CloseRemainderTo\nglobal ZeroAddress\n==\n&&\ntxn RekeyTo\nglobal ZeroAddress\n==\n&&\ntxn NumAppArgs\nintc_1\n==\n&&\n"
  },
  "txn": {
    "amt": 199999,
    "fee": 1000,
    "fv": 180,
    "gen": "sandnet-v1",
    "gh": "oraQ+Ev9huLz3SO+aJquqUSsmM05JFD9YTy/QKzhtcg=",
    "lv": 1180,
    "rcv": "PG5NPXZNXI52RH5TTVDC4C7EXMMKZ5ERAM2CI75K25GYIUPUPISIWNVJ4E",
    "snd": "DHJXPUZXP4RU4E7UUXKTJCWISMPE3FEP5JSLGYH257N5EOH5LOHM3Q2FUQ",
    "type": "pay"
  }
}

Digging further, it appears that pyteal.Arg, used by signature contracts, is different than Txn.application_args, which is only usable by applications. Is there a similar field for signature contracts that allows me to determine the number of arguments being provided? Or is it assumed that signature contracts always have a predefined number of arguments with pyteal.Arg being used to access them?

Thank you for your time and help.

Best,
Joshua

This approach has a couple of issues. One, stateless contracts do not hold any state, so if you compile them with a check for receiver == to arg n, this will work for anyone who wants to pass in an address. If you compile with the hard address not tied to a parameter then it will work fine. You could template the contract or build it on the fly with the SDK based on some parameters which hard code the address into the stateless contract then use the SDK to compile it. Then fund it and let any of the address added before compile time pull funds out. Two, in stateless contracts parameters are not covered under the signature. So if you are using this for something like a delegated contract you have to be very careful using arguments for justification of a transaction.

On the question of args, in stateless we currently do not have a way of knowing the number of args passed in, but you could always interpret the first parameter is the number of total parameters to get around that issue.

You can always combine stateful and stateless by using atomic transfers. Ie the stateless will always fail if a call to the stateful is not made and vice versa. Take a look at this article for more details:

I see - thank you very much for the quick and detailed response! I was under the mistaken impression that the logic sig representing the compiled code contained the arguments that were passed as hard values.

Thanks again,
Joshua

Yea we need to clear that up in the docs.

Hello Husafan,

One way you can do this is to hardcode in all the addresses that the contract is allowed to disperse to. Then check if the recipient of the funds matches any of the hardcoded addresses.

Thank you Alexander - this is very helpful.