Simulate Smart Contract Evaluation on Algorand

Anne Kenyon
Algorand
Published in
6 min readMar 15, 2023

The release of go-algorand 3.15 introduces a new mode of interaction for Algorand: simulate. Simulate allows transactions to be evaluated by a node without altering the state of the blockchain.

Simulate has a myriad of uses, including free read access to contract state, efficient testing, and easier debugging.

Simulate Basics

Simulate is a new endpoint that mirrors the transaction submission endpoint. It can be called using the same exact format on any network, including Mainnet.

Simulate evaluates the transaction group submitted using the network’s current state. Simulate’s response will include:

  • would-succeed: a boolean indicating whether the transaction group is valid.
  • failure-message: if the transaction group is not valid, simulate returns the error.
  • missing-signatures: simulate will complete the evaluation even if the transaction group is missing signatures. This field will indicate whether any signatures are missing.
  • txn-results: simulate will return partial information about what effects this transaction group would have had on the blockchain state: any new assets or apps created, global/local state changes, etc. In future versions of simulate, this will also explicitly include changes to Algo balances and box states.
    If the evaluation ran into an error, simulate will return the partial information to help debugging efforts: any changes that were already calculated up to the point when the evaluation failed.

Reading State for Free with Simulate

Imagine that you want to know a smart contract’s state (global, local, or box state).

For example, you’re part of a DAO and you want to know whether alice.algo has voted on the latest proposal yet. How can you find out?

The voting smart contract will store voting information in alice.algo’s local state. Using the API for algod or for an indexer, you can get the local state data. However, the data will likely not be very useful to you — it’ll be encoded however the smart contract organized it, so it’ll be gobbledygook.

You can read the smart contract to understand the encoding, making it possible to decode Alice’s voting status. Although — can you read smart contracts on chain?

Rather than trying to read the smart contract code, why can’t the smart contract just tell us the answer, since it knows how to decode its own data? It could have a method did_they_vote(account)bool that returns a boolean representing the account’s voting status. That makes it easy: just issue a transaction to call that method to get your answer. Unfortunately, calling the app costs a network fee, unlike the read-and-decode solution.

Simulate offers us the best of both worlds. You can call did_they_vote(alice.algo) using simulate, which will give you the answer without charging a network fee.

A more complex example, where the smart contract is not only decoding their state for you but also doing some calculation, is calling simulate on get_current_slippage(trade_size, pair) to an AMM.

Another expected usage is to run a transaction using simulate right before submitting it to the network. This allows you to verify that the transaction resulted in the expected changes to the blockchain. However, there is an important subtlety here to be aware of: state could change between the time when you call simulate and when your transaction is accepted by the network.

Streamlined Testing with Simulate

There are many ways to test Algorand Smart Contracts. One of the most common is to spin up a local network, issue transactions to set up a known blockchain state for the test, call the app, and then assert against the resulting state. This is a powerful test that is quite faithful to how the app would behave on Mainnet, but it is heavy. It can take time (too much time) to write and run each test, which causes some developers to write and run fewer tests than they might otherwise.

Perhaps the most time-consuming part of this test is setting up the state for the test. This state-setting can consist of: creating and funding accounts, creating assets, opting accounts into the assets, deploying apps, opting accounts into the apps, and making a series of initial app calls to set up global and local states. All this before the actual single test app call is made and the resulting state can be verified.

Running the next test requires resetting the local network to zero and starting over again, repeating the whole state-setting process.

How does simulate help us with this costly and inefficient testing process? Simulate allows you to run several tests against the same state. After setting up the state, instead of making the app call, thereby altering the state and needing to reset it for further tests, you can use simulate to see what would happen if the app call were submitted. You can thus run many tests against the same state — calling different contract methods, with many different arguments, even fuzz testing. You will also get richer output from simulate than you would from a normal execution, so you can run more detailed dapp-specific test assertions.

For example, imagine that Maria is running an auction dapp and wants to test it. Maria sets up her blockchain state such that the auction is in progress. Maria’s test then runs several simulate calls:

  • One call that bids on the item for auction with a price above the previous bid plus the minimum bid increment (happy path, should succeed).
  • One call that bids on the item with a price above the previous bid but not higher than the previous bid plus the minimum bid increment (should fail).
  • One call that bids on the item with a price that is lower than the previous bid (should fail).
  • One call that attempts to claim the item (even though the auction is still in progress, so should fail).

These are just a few examples. A thorough developer would likely include many more cases, but hopefully it demonstrates how tests can now be more efficiently run.

As John Clarke of Algofi says, “Simulate will dramatically improve the efficiency of … development, enabling more robust test suites to be built for AVM smart contracts.”

Improved Debugging with Simulate

As a github external contributor once commented, “debugging is always urgent.” Indeed, debugging is at the center of every developer’s workflow (I sometimes hear of mythical programmers who write perfect code on the first try, but I don’t believe in fairytales). Debugging Algorand smart contracts has been, let’s be honest, a middling experience so far. It’s time to get good — simulate is the first, and key, step.

From Dryrun to Simulate

Before simulate, a core tool used for debugging was “dryrun”. On the surface, dryrun seems similar to simulate: it will evaluate your program without committing anything to the blockchain.

However, dryrun is built with a different architecture which severely limits it. Dryrun does not use the real ledger from the blockchain, nor the real block evaluator. It uses a thin version of the evaluation logic against external state passed in through the endpoint.

Dryrun does fine for evaluating a single app call, but cannot evaluate a transaction group properly. It cannot update the state from one transaction to the next in the transaction group, since it is just using the passed-in state as-is.

Most dapps use transaction groups, so this limitation is quite problematic. Simulate solves the situation by handling transactions individually or as group transactions. When evaluating a transaction group, simulate properly tracks the state changes from one transaction to the next in the group.

Another limitation of dryrun’s architecture is that it needs to be updated each time there is an update to the AVM. Dryrun fell out of date when inner transactions were introduced, and again with contract-to-contract calls, and again when boxes were added. Simulate, built alongside the evaluation logic itself, will never require such maintenance and should not fall out of date.

The Future with Simulate

Simulate lays the foundation for a slew of useful features. Now that the core architecture is in place, the field is open to suggestions of what will be most useful for the developer community. Here are some features we are hoping to add in the near future:

  • Allow simulate to run without log limitations.
  • Report opcode budget used by app and by smart signature.
  • Report fee credit (assuming no congestion, how many excess fees were used for this transaction group).
  • Report the execution trace: for each step of the evaluation, details the stack, scratch slots, changes to local/global/box state, etc.
  • Report foreign resources used.
  • Allow simulate to run without any foreign resource limits (without checking foreign arrays).
  • Use different suggested params in a simulate run.

We seek your suggestions! Reach us on github or discord (algoanne#5743), as usual.

Prominent API Providers of the ecosystem, Algonode and Purestake, will both have the simulate endpoint available for use soon after release.

For technical details of how to use simulate, see our technical overview article.

--

--