Atomic messaging

Has anyone thought about how to enable atomic cross-canister messaging? Would be cool if there was a system-level way to do this, likely with higher fees.

This has many applications, but in the financial space there are two big ones:

  • Atomic cross-canister asset swaps without a centralized custodian
  • Flash loans, which allow uncollateralized loans (in Ethereum, all “loans” are permissionless but require posting 100%+ collateral). In a flash loan, borrowers receive and repay assets in one single transaction, allowing them to use the loaned asset for other profitable opportunities. If they cannot repay the debt, the whole transaction simply reverts

As described here:

A function that does not await in its body is guaranteed to execute atomically - in particular, the environment cannot change the state of the actor while the function is executing. If a function performs an await , however, atomicity is no longer guaranteed. Between suspension and resumption around the await , the state of the enclosing actor may change due to concurrent processing of other incoming actor messages. It is the programmer’s responsibility to guard against non-synchronized state changes.

Cross-canister calls are not guaranteed. And I test three canisters: canister A call canister B and canister C in order, and successfully executed in B, asserted in C. B’s state var is changed and C’s is not.

So when we code, we should check first, and modify last. Also, flash loans may be impossible


Ah…I was really hoping inter-canister calls were atomic. Is this something the team will be able to enable in the future? This is going to make implementing transactions much more difficult, since you’ll have to keep track of state changes across canisters somehow and roll them back…I was hoping a simple panic/trap would revert all memory

1 Like

inter-canister calls are guaranteed with either a reply or error response, which hopefully can facilitate the implementation of a cross-canister transactional semantics if so desired.

When it comes to supporting transactions, there is a tradeoff between ease of use and scalability. It is a conscious choice that atomicity is per-message and per-canister in our design. Each application is free to pick and build a semantics that suits them, rather than succumb to a system imposed one that is not scalable (e.g. EVM).


I could imagine that Motoko could eventually provide an “atomic async”, but this would obviously require imposing a global lock on the canister. Given that all calls have a guaranteed response, this should work.