ARC-4: Application Binary Interface (ABI)
Conventions for encoding method calls in Algorand Application
Author | Jannotti, Jason Paulos |
---|---|
Discussions-To | https://github.com/algorandfoundation/ARCs/issues/44 |
Status | Final |
Type | Standards Track |
Category | Interface |
Created | 2021-07-29 |
Table of Contents
Algorand Transaction Calling Conventions
Abstract
This document introduces conventions for encoding method calls, including argument and return value encoding, in Algorand Application call transactions. The goal is to allow clients, such as wallets and dapp frontends, to properly encode call transactions based on a description of the interface. Further, explorers will be able to show details of these method invocations.
Definitions
- Application: an Algorand Application, aka “smart contract”, “stateful contract”, “contract”, or “app”.
- HLL: a higher level language that compiles to TEAL bytecode.
- dapp (frontend): a decentralized application frontend, interpreted here to mean an off-chain frontend (a webapp, native app, etc.) that interacts with Applications on the blockchain.
- wallet: an off-chain application that stores secret keys for on-chain accounts and can display and sign transactions for these accounts.
- explorer: an off-chain application that allows browsing the blockchain, showing details of transactions.
Specification
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in RFC-2119.
Comments like this are non-normative.
Interfaces are defined in TypeScript. All the objects that are defined
are valid JSON objects, and all JSON string
types are UTF-8 encoded.
Overview
This document makes recommendations for encoding method invocations as Application call transactions, and for describing methods for access by higher-level entities. Encoding recommendations are intended to be minimal, intended only to allow interoperability among Applications. Higher level recommendations are intended to enhance user-facing interfaces, such as high-level languages, dapps, and wallets. Applications that follow the recommendations described here are called ARC-4 Applications.
Methods
A method is a section of code intended to be invoked externally with an Application call transaction. A method must have a name, it may take a list of arguments as input when it is invoked, and it may return a single value (which may be a tuple) when it finishes running. The possible types for arguments and return values are described later in the Encoding section.
Invoking a method involves creating an Application call transaction to specifically call that method. Methods are different from internal subroutines that may exist in a contract, but are not externally callable. Methods may be invoked by a top-level Application call transaction from an off-chain caller, or by an Application call inner transaction created by another Application.
Method Signature
A method signature is a unique identifier for a method. The signature
is a string that consists of the method’s name, an open parenthesis, a
comma-separated list of the types of its arguments, a closing
parenthesis, and the method’s return type, or void
if it does not
return a value. The names of the arguments MUST NOT be included in a
method’s signature, and MUST NOT contain any whitespace.
For example, add(uint64,uint64)uint128
is the method signature for a
method named add
which takes two uint64 parameters and returns a
uint128. Signatures are encoded in ASCII.
For the benefit of universal interoperability (especially in HLLs),
names MUST satisfy the regular expression [_A-Za-z][A-Za-z0-9_]*
.
Names starting with an underscore are reserved and MUST only
be used as specified in this ARC or future ABI-related ARC.
Method Selector
Method signatures contain all the information needed to identify a method, however the length of a signature is unbounded. Rather than consume program space with such strings, a method selector is used to identify methods in calls. A method selector is the first four bytes of the SHA-512/256 hash of the method signature.
For example, the method selector for a method named add
which takes
two uint64 parameters and returns a uint128 can be computed as
follows:
Method signature: add(uint64,uint64)uint128
SHA-512/256 hash (in hex): 8aa3b61f0f1965c3a1cbfa91d46b24e54c67270184ff89dc114e877b1753254a
Method selector (in hex): 8aa3b61f
Method Description
A method description provides further information about a method beyond its signature. This description is encoded in JSON and consists of a method’s name, description (optional), arguments (their types, and optional names and descriptions), and return type and optional description for the return type. From this structure, the method’s signature and selector can be calculated. The Algorand SDKs provide convenience functions to calculate signatures and selectors from such JSON files.
These details will enable high-level languages and dapps/wallets to properly encode arguments, call methods, and decode return values. This description can populate UIs in dapps, wallets, and explorers with description of parameters, as well as populate information about methods in IDEs for HLLs.
The JSON structure for such an object is:
interface Method {
/** The name of the method */
name: string;
/** Optional, user-friendly description for the method */
desc?: string;
/** The arguments of the method, in order */
args: Array<{
/** The type of the argument */
type: string;
/** Optional, user-friendly name for the argument */
name?: string;
/** Optional, user-friendly description for the argument */
desc?: string;
}>;
/** Information about the method's return value */
returns: {
/** The type of the return value, or "void" to indicate no return value. */
type: string;
/** Optional, user-friendly description for the return value */
desc?: string;
};
}
For example:
{
"name": "add",
"desc": "Calculate the sum of two 64-bit integers",
"args": [
{ "type": "uint64", "name": "a", "desc": "The first term to add" },
{ "type": "uint64", "name": "b", "desc": "The second term to add" }
],
"returns": { "type": "uint128", "desc": "The sum of a and b" }
}
Interfaces
An Interface is a logically grouped set of methods. All method selectors in an Interface MUST be unique. Method names MAY not be unique, as long as the corresponding method selectors are different. Method names in Interfaces MUST NOT begin with an underscore.
An Algorand Application implements an Interface if it supports all of the methods from that Interface. An Application MAY implement zero, one, or multiple Interfaces.
Interface designers SHOULD try to prevent collisions of method selectors between Interfaces that are likely to be implemented together by the same Application.
For example, an Interface
Calculator
providing addition and subtraction of integer methods and an InterfaceNumberFormatting
providing formatting methods for numbers into strings are likely to be used together. Interface designers should ensure that all the methods inCalculator
andNumberFormatting
have distinct method selectors.
Interface Description
An Interface description is a JSON object containing the JSON descriptions for each of the methods in the Interface.
The JSON structure for such an object is:
interface Interface {
/** A user-friendly name for the interface */
name: string;
/** Optional, user-friendly description for the interface */
desc?: string;
/** All of the methods that the interface contains */
methods: Method[];
}
Interface names MUST satisfy the regular expression [_A-Za-z][A-Za-z0-9_]*
.
Interface names starting with ARC
are reserved to interfaces defined in ARC.
Interfaces defined in ARC-XXXX
(where XXXX
is a 0-padded number) SHOULD
start with ARC_XXXX
.
For example:
{
"name": "Calculator",
"desc": "Interface for a basic calculator supporting additions and multiplications",
"methods": [
{
"name": "add",
"desc": "Calculate the sum of two 64-bit integers",
"args": [
{ "type": "uint64", "name": "a", "desc": "The first term to add" },
{ "type": "uint64", "name": "b", "desc": "The second term to add" }
],
"returns": { "type": "uint128", "desc": "The sum of a and b" }
},
{
"name": "multiply",
"desc": "Calculate the product of two 64-bit integers",
"args": [
{ "type": "uint64", "name": "a", "desc": "The first factor to multiply" },
{ "type": "uint64", "name": "b", "desc": "The second factor to multiply" }
],
"returns": { "type": "uint128", "desc": "The product of a and b" }
}
]
}
Contracts
A Contract is a declaration of what an Application implements. It includes the complete list of the methods implemented by the related Application. It is similar to an Interface, but it may include further details about the concrete implementation, as well as implementation-specific methods that do not belong to any Interface. All methods in a Contract MUST be unique; specifically, each method MUST have a unique method selector.
Method names in Contracts MAY begin with underscore, but these names are reserved for use by this ARC and future extensions of this ARC.
OnCompletion Actions and Creation
In addition to the set of methods from the Contract’s definition, a Contract MAY allow Application calls with zero arguments, also known as bare Application calls. Since method invocations with zero arguments still encode the method selector as the first Application call argument, bare Application calls are always distinguishable from method invocations.
The primary purpose of bare Application calls is to allow the
execution of an OnCompletion (apan
) action which requires no
inputs and has no return value. A Contract MAY allow this for
all of the OnCompletion actions listed below, for only a subset of
them, or for none at all. Great care should be taken when allowing
these operations.
Allowed OnCompletion actions:
- 0: NoOp
- 1: OptIn
- 2: CloseOut
- 4: UpdateApplication
- 5: DeleteApplication
Note that OnCompletion action 3, ClearState, is NOT allowed to be invoked as a bare Application call.
While ClearState is a valid OnCompletion action, its behavior differs significantly from the other actions. Namely, an Application running during ClearState which wishes to have any effect on the state of the chain must never fail, since due to the unique behavior about ClearState failure, doing so would revert any effect made by that Application. Because of this, Applications running during ClearState are incentivized to never fail. Accepting any user input, whether that is an ABI method selector, method arguments, or even relying on the absence of Application arguments to indicate a bare Application call, is therefore a dangerous operation, since there is no way to enforce properties or even the existence of data that is supplied by the user.
If a Contract elects to allow bare Application calls for some OnCompletion actions, then that Contract SHOULD also allow any of its methods to be called with those OnCompletion actions, as long as this would not cause undesirable or nonsensical behavior.
The reason for this is because if it’s acceptable to allow an OnCompletion action to take place in isolation inside of a bare Application call, then it’s most likely acceptable to allow the same action to take place at the same time as an ABI method call. And since the latter can be accomplished in just one transaction, it can be more efficient.
If a Contract requires an OnCompletion action to take inputs or to return a value, then the RECOMMENDED behavior of the Contract is to not allow bare Application calls for that OnCompletion action. Rather, the Contract should have one or more methods that are meant to be called with the appropriate OnCompletion action set in order to process that action.
A Contract MUST NOT allow any of its methods to be called with the ClearState OnCompletion action.
To reinforce an earlier point, it is unsafe for a ClearState program to read any user input, whether that is a method argument or even relying on a certain method selector to be present. This behavior makes it unsafe to use ABI calling conventions during ClearState.
If an Application is called with greater than zero Application call arguments (i.e. NOT a bare Application call) and the OnCompletion action is NOT ClearState, the Application MUST always treat the first argument as a method selector and invoke the specified method. This behavior MUST be followed for all OnCompletion actions, except for ClearState. This applies to Application creation transactions as well, where the supplied Application ID is 0.
Similar to OnCompletion actions, if a Contract requires its creation transaction to take inputs or to return a value, then the RECOMMENDED behavior of the Contract should be to not allow bare Application calls for creation. Rather, the Contract should have one or more methods that are meant to be called in order to create the Contract.
Contract Description
A Contract description is a JSON object containing the JSON descriptions for each of the methods in the Contract.
The JSON structure for such an object is:
interface Contract {
/** A user-friendly name for the contract */
name: string;
/** Optional, user-friendly description for the interface */
desc?: string;
/**
* Optional object listing the contract instances across different networks
*/
networks?: {
/**
* The key is the base64 genesis hash of the network, and the value contains
* information about the deployed contract in the network indicated by the
* key
*/
[network: string]: {
/** The app ID of the deployed contract in this network */
appID: number;
}
}
/** All of the methods that the contract implements */
methods: Method[];
}
Contract names MUST satisfy the regular expression [_A-Za-z][A-Za-z0-9_]*
.
The desc
fields of the Contract and the methods inside the Contract
SHOULD contain information that is not explicitly encoded in the other fields,
such as support of bare Application calls, requirement of specific
OnCompletion action for specific methods, and methods to call for creation
(if creation cannot be done via a bare Application call).
For example:
{
"name": "Calculator",
"desc": "Contract of a basic calculator supporting additions and multiplications. Implements the Calculator interface.",
"networks": {
"wGHE2Pwdvd7S12BL5FaOP20EGYesN73ktiC1qzkkit8=": { "appID": 1234 },
"SGO1GKSzyE7IEPItTxCByw9x8FmnrCDexi9/cOUJOiI=": { "appID": 5678 },
},
"methods": [
{
"name": "add",
"desc": "Calculate the sum of two 64-bit integers",
"args": [
{ "type": "uint64", "name": "a", "desc": "The first term to add" },
{ "type": "uint64", "name": "b", "desc": "The second term to add" }
],
"returns": { "type": "uint128", "desc": "The sum of a and b" }
},
{
"name": "multiply",
"desc": "Calculate the product of two 64-bit integers",
"args": [
{ "type": "uint64", "name": "a", "desc": "The first factor to multiply" },
{ "type": "uint64", "name": "b", "desc": "The second factor to multiply" }
],
"returns": { "type": "uint128", "desc": "The product of a and b" }
}
]
}
Method Invocation
In order for a caller to invoke a method, the caller and the method implementation (callee) must agree on how information will be passed to and from the method. This ABI defines a standard for where this information should be stored and for its format.
This standard does not apply to Application calls with the ClearState OnCompletion action, since it is unsafe for ClearState programs to rely on user input.
Standard Format
The method selector must be the first Application call argument (index 0),
accessible as txna ApplicationArgs 0
from TEAL (except for bare Application
calls, which use zero application call arguments).
If a method has 15 or fewer arguments, each argument MUST be placed in order in the following Application call argument slots (indexes 1 through 15). The arguments MUST be encoded as defined in the Encoding section.
Otherwise, if a method has 16 or more arguments, the first 14 MUST be placed in order in the following Application call argument slots (indexes 1 through 14), and the remaining arguments MUST be encoded as a tuple in the final Application call argument slot (index 15). The arguments must be encoded as defined in the Encoding section.
If a method has a non-void return type, then the return value of the method
MUST be located in the final logged value of the method’s execution,
using the log
opcode. The logged value MUST contain a specific 4 byte
prefix, followed by the encoding of the return value as defined in the
Encoding section. The 4 byte prefix is defined as the first 4
bytes of the SHA-512/256 hash of the ASCII string return
. In hex, this is
151f7c75
.
For example, if the method
add(uint64,uint64)uint128
wanted to return the value 4160, it would log the byte array151f7c7500000000000000000000000000001040
(shown in hex).
Implementing a Method
An ARC-4 Application implementing a method:
-
MUST check if
txn NumAppArgs
equals 0. If true, then this is a bare Application call. If the Contract supports bare Application calls for the current transaction parameters (it SHOULD check the OnCompletion action and whether the transaction is creating the application), it MUST handle the call appropriately and either approve or reject the transaction. The following steps MUST be ignored in this case. Otherwise, if the Contract does not support this bare application call, the Contract MUST reject the transaction. -
MUST examine
txna ApplicationArgs 0
to identify the selector of the method being invoked. If the contract does not implement a method with that selector, the Contract MUST reject the transaction. -
MUST execute the actions required to implement the method being invoked. In general, this works by branching to the body of the method indicated by the selector.
-
The code for that method MAY extract the arguments it needs, if any, from the application call arguments as described in the Encoding section. If the method has more than 15 arguments and the contract needs to extract an argument beyond the 14th, it MUST decode
txna ApplicationArgs 15
as a tuple to access the arguments contained in it. -
If the method is non-void, the Application MUST encode the return value as described in the Encoding section and then
log
it with the prefix151f7c75
. Other values MAY be logged before the return value, but other values MUST NOT be logged after the return value.
Calling a Method from Off-Chain
To invoke an ARC-4 Application, an off-chain system, such as a dapp or wallet, would first obtain the Interface or Contract description JSON object for the app. The client may now:
- Create an Application call transaction with the following parameters:
- Use the ID of the desired Application whose program code implements the method being invoked, or 0 if they wish to create the Application.
- Use the selector of the method being invoked as the first Application call argument.
- Encode all arguments for the method, if any, as described in the Encoding section. If the method has more than 15 arguments, encode all arguments beyond (but not including) the 14th as a tuple into the final Application call argument.
- Submit this transaction and wait until it successfully commits to the blockchain.
- Decode the return value, if any, from the ApplyData’s log information.
Clients MAY ignore the return value.
An exception to the above instructions is if the app supports bare Application calls for some transaction parameters, and the client wishes to invoke this functionality. Then the client may simply create and submit to the network an Application call transaction with the ID of the Application (or 0 if they wish to create the application) and the desired OnCompletion value set. Application arguments MUST NOT be present.
Encoding
This section describes how ABI types can be represented as byte strings.
Like the EthereumABI, this encoding specification is designed to have the following two properties:
- The number of non-sequential “reads” necessary to access a value is
at most the depth of that value inside the encoded array
structure. For example, at most 4 reads are needed to retrieve a
value at
a[i][k][l][r]
. - The encoding of a value or array element is not interleaved with other data and it is relocatable, i.e. only relative “addresses” (indexes to other parts of the encoding) are used.
Types
The following types are supported in the Algorand ABI.
uint<N>
: AnN
-bit unsigned integer, where8 <= N <= 512
andN % 8 = 0
. When this type is used as part of a method signature,N
must be written as a base 10 number without any leading zeros.byte
: An alias foruint8
.bool
: A boolean value that is restricted to either 0 or 1. When encoded, up to 8 consecutivebool
values will be packed into a single byte.ufixed<N>x<M>
: AnN
-bit unsigned fixed-point decimal number with precisionM
, where8 <= N <= 512
,N % 8 = 0
, and0 < M <= 160
, which denotes a valuev
asv / (10^M)
. When this type is used as part of a method signature,N
andM
must be written as base 10 numbers without any leading zeros.<type>[<N>]
: A fixed-length array of lengthN
, whereN >= 0
.type
can be any other type. When this type is used as part of a method signature,N
must be written as a base 10 number without any leading zeros, unlessN
is zero, in which case only a single 0 character should be used.address
: Used to represent a 32-byte Algorand address. This is equivalent tobyte[32]
.<type>[]
: A variable-length array.type
can be any other type.string
: A variable-length byte array (byte[]
) assumed to contain UTF-8 encoded content.(T1,T2,…,TN)
: A tuple of the typesT1
,T2
, …,TN
,N >= 0
.- reference types
account
,asset
,application
: MUST NOT be used as the return type. For encoding purposes they are an alias foruint8
. See section “Reference Types” below.
Additional special use types are defined in Reference Types and Transaction Types.
Static vs Dynamic Types
For encoding purposes, the types are divided into two categories: static and dynamic.
The dynamic types are:
<type>[]
for anytype
- This includes
string
since it is an alias forbyte[]
.
- This includes
<type>[<N>]
for any dynamictype
(T1,T2,...,TN)
ifTi
is dynamic for some1 <= i <= N
All other types are static. For a static type, all encoded values of that type have the same length, irrespective of their actual value.
Encoding Rules
Let len(a)
be the number of bytes in the binary string a
. The
returned value shall be considered to have the ABI type uint16
.
Let enc
be a mapping from values of the ABI types to binary
strings. This mapping defines the encoding of the ABI.
For any ABI value x
, we recursively define enc(x)
to be as follows:
- If
x
is a tuple ofN
types,(T1,T2,...,TN)
, wherex[i]
is the value at indexi
, starting at 1:enc(x) = head(x[1]) ... head(x[N]) tail(x[1]) ... tail(x[N])
- Let
head
andtail
be mappings from values in this tuple to binary strings. For eachi
such that1 <= i <= N
, these mappings are defined as:- If
Ti
(the type ofx[i]
) is static:- If
Ti
isbool
:- Let
after
be the largest integer such that allT(i+j)
arebool
, for0 <= j <= after
. - Let
before
be the largest integer such that allT(i-j)
arebool
, for0 <= j <= before
. - If
before % 8 == 0
:head(x[i]) = enc(x[i]) | (enc(x[i+1]) >> 1) | ... | (enc(x[i + min(after,7)]) >> min(after,7))
, where>>
is bitwise right shift which pads with 0,|
is bitwise or, andmin(x,y)
returns the minimum value of the integersx
andy
.tail(x[i]) = ""
(the empty string)
- Otherwise:
head(x[i]) = ""
(the empty string)tail(x[i]) = ""
(the empty string)
- Let
- Otherwise:
head(x[i]) = enc(x[i])
tail(x[i]) = ""
(the empty string)
- If
- Otherwise:
head(x[i]) = enc(len( head(x[1]) ... head(x[N]) tail(x[1]) ... tail(x[i-1]) ))
tail(x[i]) = enc(x[i])
- If
- If
x
is a fixed-length arrayT[N]
:enc(x) = enc((x[0], ..., x[N-1]))
, i.e. it’s encoded as if it were anN
element tuple where every element is typeT
.
- If
x
is a variable-length arrayT[]
withk
elements:enc(x) = enc(k) enc([x[0], ..., x[k-1]])
, i.e. it’s encoded as if it were a fixed-length array ofk
elements, prefixed with its length,k
encoded as auint16
.
- If
x
is anN
-bit unsigned integer,uint<N>
:enc(x)
is theN
-bit big-endian encoding ofx
.
- If
x
is anN
-bit unsigned fixed-point decimal number with precisionM
,ufixed<N>x<M>
:enc(x) = enc(x * 10^M)
, wherex * 10^M
is interpreted as auint<N>
.
- If
x
is a boolean valuebool
:enc(x)
is a single byte whose most significant bit is either 1 or 0, ifx
is true or false respectively. All other bits are 0. Note: this means that a value of true will be encoded as0x80
(10000000
in binary) and a value of false will be encoded as0x00
. This is in contrast to most other encoding schemes, where a value of true is encoded as0x01
.
Other aliased types’ encodings are already covered:
string
andaddress
are aliases forbyte[]
andbyte[32]
respectivelybyte
is an alias foruint8
- each of the reference types is an alias for
uint8
Reference Types
Three special types are supported only as the type of an argument. They can be embedded in arrays and tuples.
account
represents an Algorand account, stored in the Accounts (apat
) arrayasset
represents an Algorand Standard Asset (ASA), stored in the Foreign Assets (apas
) arrayapplication
represents an Algorand Application, stored in the Foreign Apps (apfa
) array
Some AVM opcodes require specific values to be placed in the “foreign
arrays” of the Application call transaction. These three types allow
methods to describe these requirements. To encode method calls that
have these types as arguments, the value in question is placed in the
Accounts (apat
), Foreign Assets (apas
), or Foreign Apps (apfa
)
arrays, respectively, and a uint8
containing the index of the value
in the appropriate array is encoded in the normal location for this
argument.
Note that the Accounts and Foreign Apps arrays have an implicit value
at index 0, the Sender of the transaction or the called Application,
respectively. Therefore, indexes of any additional values begin at 1.
Additionally, for efficiency, callers of a method that wish to pass the
transaction Sender as an account
value or the called Application as
an application
value SHOULD use 0 as the index of these values
and not explicitly add them to Accounts or Foreign Apps arrays.
When passing addresses, ASAs, or apps that are not required to be
accessed by such opcodes, ARC-4 Contracts SHOULD use the base
types for passing these types: address
for accounts and uint64
for asset or Application IDs.
Transaction Types
Some apps require that they are invoked as part of a larger transaction group, containing specific additional transactions. Seven additional special types are supported (only) as argument types to describe such requirements.
txn
represents any Algorand transactionpay
represents a PaymentTransaction (algo transfer)keyreg
represents a KeyRegistration transaction (configure consensus participation)acfg
represent a AssetConfig transaction (create, configure, or destroy ASAs)axfer
represents an AssetTransfer transaction (ASA transfer)afrz
represents an AssetFreezeTx transaction (freeze or unfreeze ASAs)appl
represents an ApplicationCallTx transaction (create/invoke a Application)
Arguments of these types are encoded as consecutive transactions in the same transaction group as the Application call, placed in the position immediately preceding the Application call. Unlike “foreign” references, these special types are not encoded in ApplicationArgs as small integers “pointing” to the associated object. In fact, they occupy no space at all in the Application Call transaction itself. Allowing explicit references would create opportunities for multiple transaction “values” to point to the same transaction in the group, which is undesirable. Instead, the locations of the transactions are implied entirely by the placement of the transaction types in the argument list.
For example, to invoke the method deposit(string,axfer,pay,uint32)void
,
a client would create a transaction group containing, in this order:
- an asset transfer
- a payment
- the actual Application call
When encoding the other (non-transaction) arguments, the client MUST act as if the transaction arguments were completely absent from the method signature. The Application call would contain the method selector in ApplicationArgs[0], the first (string) argument in ApplicationArgs[1], and the fourth (uint32) argument in ApplicationArgs[2].
ARC-4 Applications SHOULD be constructed to allow their invocations to be
combined with other contract invocations in a single atomic group if
they can do so safely. For example, they SHOULD use gtxns
to examine
the previous index in the group for a required pay
transaction,
rather than hardcode an index with gtxn
.
In general, an ARC-4 Application method with n
transactions as arguments SHOULD
only inspect the n
previous transactions. In particular, it SHOULD NOT
inspect transactions after and it SHOULD NOT check the size of a transaction
group (if this can be done safely).
In addition, a given method SHOULD always expect the same
number of transactions before itself. For example, the method
deposit(string,axfer,pay,uint32)void
is always preceded by two transactions.
It is never the case that it can be called only with one asset transfer
but no payment transfer.
The reason for the above recommendation is to provide minimal composability support while preventing obvious dangerous attacks. For example, if some apps expect payment transactions after them while other expect payment transaction before them, then the same payment may be counted twice.
Rationale
Security Considerations
None.
Copyright
Copyright and related rights waived via CCO.
Citation
Please cite this document as:
Jannotti, Jason Paulos, "ARC-4: Application Binary Interface (ABI)," Algorand Requests for Comments, no. 4, July 2021. [Online serial]. Available: https://github.com/algorandfoundation/ARCs/blob/main/ARCs/arc-0004.md.