Composable Transactions ======================== Normal transactions can make only a single call to a smart contract. This means that when we need to call a function many times, or interact with different smart contracts, we need a separate transaction for each smart contract interaction. Composable Transactions bring the ability to **make many calls in a single transaction**, either to the same smart contract or to different ones. ## Characteristics 1. Multiple calls 2. Atomicity 3. Sequential execution 4. Act on current state 5. Scripting 6. No blind signing ### Atomicity If any of the calls fail, the entire transaction is aborted. This means that either all calls succeed or none of them. ### Sequential Execution Execute a series of contract calls with the guarantee that they happen in sequence order with no external action occurring between them. ### Act on current state Do actions using the current values from contract and account states (by making queries to contracts and using the returned value on the same transaction) instead of a previously hardcoded value acquired at transaction building time. This is useful when another transaction arrives first and changes the contract state. ### Scripting Instead of just a list of calls, it is possible to process the returned value and act according to them. The language supports data conversion and manipulation, assertion, conditional execution, loops, and more. ### No Blind Signing The transaction can be reviewed even on hardware wallets before approval or rejection ## Use Cases **1)** Purchase airplane tickets and hotel reservation from different contracts, only if both succeed. If some of the purchases fail, the other is reverted (the user does not want one of them if the other is not acquired). **2)** Swap token and buy something with it. If the purchase fails, the user does not want that token. **3)** Swap tokens using a split route. If one of the routes fail, revert the other(s). Also check the minimum output in the transaction itself (by adding individual outputs) and revert if not satisfied. **4)** Swap to an exact amount of the output token when using a multi-hop route, by either: a. doing a reverse swap of the surplus amount, or b. querying a contract for the right amount to send (between approved range) in the same transaction. **5)** Add liquidity to a pool (2 tokens) in a single transaction **6)** Trustless swap or purchase - check if the purchased token was received, otherwise revert the transaction **7)** Transferring tokens (fungible and/or non-fungible) to multiple recipients at the same time **8)** Transferring different tokens (fungible and/or non-fungible) to one recipient in a single transaction **9)** Mint many non-fungible tokens (NFTs) in a single transaction **10)** Approve contract B to use contract A (token) and then call a function on contract B that handles the resources on contract A (eg: approve and swap, approve and add liquidity) on a single transaction **11)** Approve, use resource, and remove approval on a single transaction, with the guarantee that no other operation would happen in between while the resource/token is approved to contract B ## Implementation The call information is stored in the transaction payload in JSON format. You can view it as a list of calls processed in order: ``` [ ] ``` Each call containing the contract address, function to call, and arguments: ``` [ [call, contract, function, arg1, arg2, arg3...], [call, contract, function, arg1, arg2, arg3...], [call, contract, function, arg1, arg2, arg3...] ] ``` ### Some Examples Buy 2 products or services on a single transaction: ``` [ ["call","","transfer","","","buy","product 1"], ["call","","transfer","","","buy","product 2"], ] ``` Acquire token2 via swap and buy a product/service with it: ``` [ ["call","","transfer","","","swap",{"exact_output":""}], ["call","","transfer","","","buy","product"], ] ``` Add liquidity to a swap pair: ``` [ ["call","","transfer","","","store_token"], ["call","","transfer","","","add_liquidity"], ] ``` ### Variables Another feature is the use of variables, in the format `%name%` When a string in the above format is found, it is replaced by the corresponding value. One hard-coded variable is the returned value from the last operation: `%last result%` Example usage: ``` [ ["call","
","function1","arg"], ["call","
","function2","%last result%"] ] ``` ### Scripting This feature uses a new simple scripting language based on JSON. The first element of each operation is a command. Here are some of them: * call * store (result) * assert The `call` is a normal smart contract call The `store` command is used to store the returned value in a temporary variable The `assert` is used to ensure that a value is within expected bounds, otherwise the whole transaction is reverted Example 1: ``` [ ["call","
","function","arg"], ["assert","%last result%",">=","250"] ] ``` Example 2: ``` [ ["call","
","function1","arg"], ["call","
","function2","arg"], ["assert","%last result%",">=","250"] ] ``` Example 3: ``` [ ["call","
","function","arg"], ["store result as","result1"], ["call","
","function","arg"], ["assert","%last result%",">=","%result1%"] ] ``` Example 4: ``` [ ["call","
","function1","arg"], ["store result as","result1"], ["call","
","function2","arg"], ["store result as","result2"], ["call","
","function3","%result1%","%result2%"] ] ``` ### Commands To support a variety of use cases, composable transactions contain many different commands: * Smart contract call * Sending Aergo tokens * Checking Aergo balance * Mathematical operations * String operations * Table operations (lists and dictionaries) * Variables * Conversions * Assertion * Conditional execution * Loops * Returning a result ## List of Commands (click on a command to expand) ### contract call
call
```javascript ["call",address,function,args...] ``` Calls a function on a smart contract Examples: ```javascript ["call","%contract%","transfer","%to%","10.5"] ["call","Ammlaklkfld","transfer","Amafpoajf","10.5"] ``` If the call fails, the entire transaction is reverted
call + send
```javascript ["call + send",amount,address,function,args...] ``` Calls a function on a smart contract, also sending an amount of native AERGO tokens Examples: ```javascript ["call + send","1.5 aergo","%contract%","buy_something","%arg%"] ``` If the call fails, the entire transaction is reverted
try call
```javascript ["try call",address,function,args...] ``` Makes a protected call to a function on a smart contract. A "protected call" means that if the call fails, it continues execution on the next command. It returns 2 values: `%call succeeded%` and `%last result%` You can use that information to conditionally execute commands Examples: ```javascript ["try call","%contract%","transfer","%to%","10.5"] ["if","%call succeeded%"] ["...","%last result%"] ... ["end if"] ```
try call + send
```javascript ["try call + send",amount,address,function,args...] ``` Makes a protected call to a function on a smart contract, also sending an amount of native AERGO tokens. A "protected call" means that if the call fails, it continues execution on the next command. It returns 2 values: `%call succeeded%` and `%last result%` You can use that information to conditionally execute commands Examples: ```javascript ["try call + send","1.5 aergo","%contract%","buy_something","%arg%"] ["if","%call succeeded%"] ["...","%last result%"] ... ... ["end if"] ```
    ### aergo balance and transfer
get aergo balance
```javascript ["get aergo balance",address] ``` Returns the current balance in AERGO tokens from the given account address. It is also possible to direclty use the `%my aergo balance%` variable
send
```javascript ["send",address,amount] ``` Transfer an amount of native AERGO tokens to the informed address The amount can be a string or a bignumber
    ### variables
let
```javascript ["let",variable_name,value] ["let",variable_name,value,token_address] ``` Assign the value to the variable The value can be a number, bignumber, string, boolean, list or object It is also possible to convert an amount in decimal format to bignumber, by supplying the address of the token. Here are some examples: ```javascript ["let","min_amount","0.05","%token2%"] ["let","min_amount","1.5","%token2%"] ["let","min_amount","100","Am..."] ```
store result as
```javascript ["store result as",variable_name] ``` Store the result of the last operation in a variable with the given name Example: ```javascript ["store result as","amount"] ```
    ### tables
get
```javascript ["get",table,key_or_pos] ``` Retrieve an element from a table (list or object) For lists we inform the position: ```javascript ["get","%list%",3] ``` For dictionaries we inform the key in string format: ```javascript ["get","%result%","price"] ```
set
```javascript ["set",table,key_or_pos,value] ``` Set a value in a table (list or object) For lists we inform the position: ```javascript ["set","%list%",2,"%value%"] ``` For dictionaries we inform the key in string format: ```javascript ["set","%obj%","price","%last result%"] ```
insert
```javascript ["insert",table,item] ["insert",table,pos,item] ``` Inserts an element on a list Other elements are moved up If no position is informed, the element is inserted at the end
remove
```javascript ["remove",table,position] ``` Removes an element from a list, by position It returns the removed item
get size
```javascript ["get size",table_or_string] ``` Retrieve the number of elements on a table (array) or the length of a string
    ### math
add
```javascript ["add",value1,value2] ``` Adds 2 values and return the result The values must be of the same type They can be number or bignumber
subtract
```javascript ["subtract",value1,value2] ``` Subtract one value from the other and return the result The values must be of the same type They can be number or bignumber
multiply
```javascript ["multiply",value1,value2] ``` Multiplies 2 values and return the result The values must be of the same type They can be number or bignumber
divide
```javascript ["divide",value1,value2] ``` Divides the first value by the second and returns the result The values must be of the same type They can be number or bignumber
remainder
```javascript ["remainder",value1,value2] ``` Returns the modulo of the division of the first value by the second The values must be of the same type They can be number or bignumber
    ### strings
combine
```javascript ["combine",str1,str2,...] ``` Concatenates all the given strings into one
format
```javascript ["format",formatstring,...] ``` The same as [string.format](https://www.lua.org/manual/5.2/manual.html#pdf-string.format) in Lua It can also be used for concatenation: ```javascript ["format","%s %s","hello","world"] ```
extract
```javascript ["extract",string,start] ["extract",string,start,end] ``` The same as [string.sub](https://www.lua.org/manual/5.2/manual.html#pdf-string.sub) in Lua
find
```javascript ["find",string,pattern] ["find",string,pattern,init] ``` The same as [string.match](https://www.lua.org/manual/5.2/manual.html#pdf-string.match) in Lua
replace
```javascript ["replace",string,pattern,replace] ["replace",string,pattern,replace,n] ``` The same as [string.gsub](https://www.lua.org/manual/5.2/manual.html#pdf-string.gsub) in Lua
    ### conversions
to big number
```javascript ["to big number"] ["to big number",value] ``` Converts a value to a bignumber If no argument is given, it converts the last result
to number
```javascript ["to number"] ["to number",value] ``` Converts a value to a number If no argument is given, it converts the last result
to string
```javascript ["to string"] ["to string",value] ``` Converts a value to a string If no argument is given, it converts the last result
to json
```javascript ["to json"] ["to json",list_or_object] ``` Converts a value to a json string If no argument is given, it converts the last result
from json
```javascript ["from json"] ["from json",json_string] ``` Converts a json string to a table If no argument is given, it converts the last result
    ### assertion
assert
```javascript ["assert",] ``` If the assertion fails, the whole transaction is reverted and marked as failed If the first value is a big number, the second value can be a string representation of an amount The expression can be a single boolean value Examples: ```javascript ["assert","%call succeeded%"] ["assert","%variable_name%",">","10.5 aergo"] ["assert","%var1%","<=","%var2%"] ["assert","%var1%","<","%var2%","and","%var1%",">=",1500] ["assert","%var1%","=","text1","or","%var1%","=","text2","or","%var1%","=","text3"] ``` The operators can be: `=` `!=` `>` `>=` `<` `<=` `match` The logic operators: `and` `or`
    ### conditional execution
if
```javascript ["if",] ``` Executes the following commands only if the expression is true The expression can be a single boolean value Example expressions: ```javascript ["if","%call succeeded%"] ["if","%variable_name%",">",10] ["if","%var1%","<=","%var2%"] ["if","%var1%","<","%var2%","and","%var1%",">=",1500] ["if","%var1%","=","text1","or","%var1%","=","text2","or","%var1%","=","text3"] ``` The operators can be: `=` `!=` `>` `>=` `<` `<=` `match` The logic operators: `and` `or` Example: ```javascript ["if","%amount%",">",20], ... ["end if"] ``` **:warning: Important:** `if` statements can **NOT** be nested!
else if
```javascript ["else if",] ``` Executes the following commands only if the expression is true Example expressions: ```javascript ["else if","%variable_name%",">",10] ["else if","%var1%","<=","%var2%"] ["else if","%var1%","<","%var2%","and","%var1%",">=",1500] ["else if","%var1%","=","text1","or","%var1%","=","text2","or","%var1%","=","text3"] ``` The operators can be: `=` `!=` `>` `>=` `<` `<=` `match` The logic operators: `and` `or` Example: ```javascript ["if","%amount%",">",20], ... ["else if","%amount%",">",10], ... ["end if"] ```
else
```javascript ["else"] ``` Example: ```javascript ["if","%amount%",">",20], ... ["else"], ... ["end if"] ```
end if
```javascript ["end if"] ``` Used to close an `if` statement. Check examples above
    ### loops
for
```javascript ["for",variable,start,end,increment] ``` Example: ```javascript ["for","n",1,10], ... [...,"%n%"], ... ["loop"] ``` With a decrement: ```javascript ["for","n",50,10,-10], ... [...,"%n%"], ... ["loop"] ```
for each
Iterate on the items from a list or a dictionary To iterate on a list: ```javascript ["for each",item,"in",list] ``` Example: ```javascript ["let","list",[11,22,33]], ["for each","item","in","%list%"], ... [...,"%item%"], ... ["loop"] ``` To iterate on a dictionary: ```javascript ["for each",key,value,"in",table] ``` Example: ```javascript ["let","payments",{"Am..":"10.5","Am..":"12.3"}], ["for each","address","amount","in","%payments%"], ["send","%address%","%amount%"], ["loop"] ```
loop
```javascript ["loop"] ``` This command can be used in 3 ways: 1. With a `for` command 2. With a `for each` command 3. Alone as the last command When used alone, it will loop to the first command on the list. We can exit the loop using the `break` command or a `return` inside of an `if` statement.
break
```javascript ["break"] ["break","if",] ``` Used to exit from a loop Example: ```javascript ["for each","item","in","%list%"], ... ["break","if",...], ... ["loop"] ``` Or if already using an if statement: ```javascript ["for each","item","in","%list%"], ... ["if",...], ["break"], ["end if"], ... ["loop"] ```
    ### return result
return
```javascript ["return"] ["return",value] ``` Example: ```javascript ["return","%last result%"] ```
    ### variables There is also these predefined variables: `%my account address%` `%my aergo balance%` Example: ```javascript ["assert","%my aergo balance%",">","10 aergo"], ["call","%token address%","balanceOf","%my account address%"], ``` ## Example Scripts Here is an example script (payload) that would be used to check the returned amount of tokens on a token swap: ``` ["call","","balanceOf"], <-- get the previous balance of token B ["store result as","balance_before"], <-- store it in a variable ["call","","transfer","","","swap"], <-- swap token A for token B ["call","","balanceOf"], <-- get the new balance of token B ["subtract","%last result%","%balance_before%"], <-- subtract one balance by the other ["assert","%last result%",">=",""] <-- assert that we got the minimum amount ``` This feature allows real **TRUSTLESS TRANSACTIONS**, where the user does not trust even the swap contract! It can allow for direct token swaps between users A and B, in a complete trustless way ## Using Composable Transactions To build a transaction with composable transactions we: * Set the type to `MULTICALL` (7) * Set the recipient to null/none * Set the amount to 0 * Put the JSON script in the payload