Hello? Contract Calling.

Algorand’s AVM 1.1: Contract-to-Contract Calls

Anne Kenyon
Algorand

--

In recent months, Algorand’s smart contract functionalities have steadily matured, enabling developers to build powerful and versatile dapps (decentralized applications). The latest development is the ability for one smart contract to call another smart contract.

Previously, smart contracts could only be called by a user initiating an application call transaction. To build complex dapps that involved multiple contracts, devs needed to store information in each contract’s local or global state that another contract could then look up and use in its execution logic. Now, a contract can simply call another one directly.

To understand the impact of this update, let us look at some examples.

Example 1: Always Getting the Best Deal

“Automated Market Makers” (AMMs) are asset-trading platforms: you can use them to swap assets with other assets. AMM’s are built on the blockchain using smart contracts.

Alice has written an AMM on Algorand. It has a liquidity pool for ALGO to USDC trading. Users can call Alice’s smart contracts to swap their ALGO for USDC and vice versa.

Bob has also written an AMM on Algorand that can be used to swap ALGO for USDC.

Maria is a crypto trader. She’s feeling optimistic about ALGO and wants to swap 10,000 USDC for some ALGO. Should she use Alice or Bob’s AMM to make the swap? At any given moment, the swap rates are fluctuating on each AMM based on the reserve ratios. How can Maria get the best rate for her swap? She can look at the current swap rate on both manually, and then submit an application call transaction to the blockchain network. Unfortunately, in the few seconds until her transaction is processed, the swap rate can change unfavorably for her.

Luckily, Maria also happens to be a developer, so she writes a smart contract to ensure she always gets the best rate for her swaps: Maria’s contract checks both rates and then calls the smart contract with the more favorable rate to actually make the swap. This all happens atomically on-chain, so is guaranteed to give Maria the largest amount of ALGO for her USDC.

This is only possible because Maria’s contract is able to call other contracts.

Example 2: Safety Checking Your Applications

Elijah is another crypto trader. He’s also interested in using Alice’s AMM to swap some USDC for ALGO, but he’s not sure he trusts Alice’s smart contracts. He audits her code and it looks pretty good, but he wants to be absolutely sure he’s not going to get tricked by using her AMM. Furthermore, given the volatility of swaps right now, he wants to initiate a swap with the confidence that he’ll get a rate he’s ok with (this tolerance to change in rates is referred to as “slippage”).

Elijah is also a programmer, so he writes a smart contract to solve his problem. Elijah’s smart contract takes as an input the swap rate Elijah is willing to trade at. The contract submits a first transaction, a call to Alice’s AMM to swap 100 USDC for some ALGO. Then, Elijah’s smart contract uses an assert statement to confirm that Elijah’s account has received the expected amount of ALGO. If the assert fails, the entire smart contract fails, and therefore all effects are rolled back — including the call to Alice’s AMM. If the assert passes, the code has guaranteed that Elijah is getting what he expected.

This is only possible because Elijah’s contract is able to call Alice’s contract.

This example can be generalized: rather than calling contract A directly, we can now create our own contract that calls contract A and asserts that what we expect to happen does, in fact, happen.

Example 3: Using Other Contracts

Fatima is building an NFT marketplace — people can buy and sell NFTs using her dapp. Fatima wants to allow people to use either USDC or ALGO to pay and be paid, user’s choice. But, what happens if a buyer wants to buy Algo Gator #54 using ALGO, and the seller wants to be paid in USDC? Fatima’s sales contract makes a call to Alice’s AMM to swap the buyer’s ALGO for some USDC, and then both the seller and the buyer get what they want out of the transaction.

This is only possible because Fatima’s contract is able to call Alice’s contract.

This example can also be generalized: with the ability for contracts to call each other, contracts can now serve as utilities for any number of dapps. Fatima could have written her own AMM and maintained a liquidity pool of USDC to ALGO, but why do that when she can easily use what is already out there?

Some Specifics Around Contract-to-Contract Calling

The first two examples above demonstrate clear functional value to the contract-calling capability. The third invites the endless possibilities of modular contracts being used in a myriad of applications.

That said, there are some technical considerations developers should be aware of as they design their dapps to use this new functionality.

  • A contract calling another contract is a type of “inner transaction”, since it is a transaction initiated from inside a smart contract. As such, contracts called by Contract A will be part of the same transaction group as contract A. This means they all execute, or none of them do.
  • It is possible for Contract A to call Contract B that calls Contract C, etc. up to depth 8.
a valid call sequence
  • Since the introduction of inner transactions in AVM 1.0, there has been a limit on the number of inner transactions per top-level contract. This limit was previously 16 inner transactions within each top-level transaction, but has been changed to 256 total inner transactions within a single transaction group.
two valid transaction groups
a transaction group disallowed by the limit on the number of inner transactions. This group has 1 contract call and 256 pay transactions, for a total of 257 inner transactions.
  • Smart contracts on Algorand are given an opcode budget of 700 to guarantee performance. When calling a contract from a contract, an additional 700 opcode budget is allocated to the entire transaction group. In other words, the opcode budget is pooled across the group, including inner contract calls.
    example: James is writing a particularly complex smart contract involving some hashing, and he knows it’s going to take more than 700 opcode budget to execute. He calls an empty contract from his top-level contract, effectively giving himself an additional 700 opcode budget — he can now use nearly 1400 opcode budget for his program. If that’s still not enough, he can make 256 inner contract calls, totaling 256*700 nearly 180,000 opcode budget for this program.
    Those familiar with developing on Algorand will recall that opcode budget is already pooled across transaction groups, which can have up to 16 transactions. The 256 inner transactions are for the entire transaction group. In other words, if a smart contract makes 256 inner pay calls, then there can be no other transactions in the transaction group calling that contract.
  • Remember that every transaction on Algorand costs a minimum fee of 0.001 ALGO. A contract that calls two other contracts will cost 0.003 ALGO to execute.
    example: if James’s contract makes 256 inner contract calls, it will cost 0.256 ALGO (at time of writing, ~0.18$) to execute it all.
  • Smart contracts on Algorand are given access to specific external resources when called: the developer has to define which addresses, applications, and assets that the contract will be interacting with (these are referred to as “foreign” apps/assets/addresses). Contracts called by a contract can be given access to a subset of the foreign objects that the top-level contract has access to. Separately, any assets or apps created in a transaction group are implicitly accessible by all contracts in the group, regardless of depth.
  • Contract A cannot call any contract that calls Contract A. This behavior is known as “re-entrancy” and is known to increase the potential for security vulnerabilities, hence the restriction.
two transactions groups disallowed by the re-entrancy restriction

For code-level details and information about other features being released alongside contract-to-contract calls, see the article on our dev portal: Contract Calls and an ABI Come to Algorand.

For information on our previous update, AVM 1.0, see here: Discover AVM 1.0

The contract-to-contract functionality is currently on Algorand’s Betanet, and will be on Mainnet and Testnet by mid-March 2022. Please come try it out on Betanet, and we will welcome all your feedback through Discord, github, or the forum.

--

--