Asynchronous Debt: Enabling Cross-Chain Composability

Daniel Hong
9 min readApr 25, 2021

Freshly launched layer 1s have a significant problem: there is nothing to do there.

It doesn’t matter whether your chain is faster, more secure, more energy efficient, decentralized, provides incredible features, or technically advanced. Ethereum is, and will always be, the king of everything we do on the blockchain.

Why is that? The platform dilemma.

If there are no users on your platform, there will be no developers to learn how to build apps for that platform and ship apps for end users. No developers mean no apps, and no apps mean even less incentives to bring new users onboard.

Recently there has been a surge in efforts trying to bridge this gap by building standardized transport and messaging protocols in between different blockchains. From centralized token transfer bots to IBC and XCMP, such solutions allow access to assets and features that only exist on one blockchain from another.

Even with such standardized transport protocols, however, a foreign chain cannot directly access logic that only exists on a single blockchain. For instance, while Binance Smart Chain has access to most major assets that exist on Ethereum over token transfer bridges, it still cannot directly call and use Uniswap or Compound that resides on the Ethereum blockchain; BSC will need to build such logic on its own chain (e.g. PancakeSwap) in order to enable AMMs or money markets.

This is because on-chain runtimes inherently cannot handle asynchronous operations.

Blockchains are, in essence, state machines that are extremely rigid with enforced consensus. State machines are a fancy term for computers. Under a standard server-to-client paradigm, asynchronous processing is fairly common — simply because waiting for every round-trip request to a server to complete doesn’t make sense. Such requests may fail at any time, and there is no guarantee that such a request will always succeed or complete within a set timeout period.

On blockchains, there is no concept of asynchronous processing. This extremely limits the ability for one blockchain to directly access logic residing on another. Consider the following scenario:

Alice is a user holding wrapped TrueUSD on the Near blockchain. She wants to deposit her TUSD to Compound on Ethereum; but as TUSD is not a supported asset on Compound she needs to swap them to DAI first then deposit them into Compound to receive cDAI, and finally send over those cDAI tokens over to her Near wallet. She then discovers PancakeSwap offers lower fees and higher liquidity than AMMs on Ethereum for swapping TrueUSD to DAI.

For Alice to perform the above, she will have to sign every single token transfer, swap and deposit transaction on each of the chains manually. Bots may be able to automate them, but this will also mean that Alice will have to build automated bots to handle every possible use case, and that for every step involved there will be separate transactions with no transaction atomicity at all. Atomicity is essential for composability; without it, protocols built on top of DeFi building blocks like yearn.finance simply would not have been possible.

If Alice was only interacting with protocols on the Ethereum blockchain with an Ethereum wallet, all of the required operations could have been done in a single transaction, and a separate wallet for every chain would not have been required. This effectively locks in users onto one blockchain, especially Ethereum, even when they have its own limitations.

Can we solve this problem, such that logic residing on different chains can be composed on-chain — similar to the way third-party contracts acting as building blocks for new protocols on Ethereum?

Enabling Asynchronous Processing On-Chain

Those familiar with programming languages supporting asynchronous processing should be used to the concept of Promises, or Futures. A Promise, by definition, is an object that represents a value that will exist some time in the future. This means that Promises may be used as a placeholder object for an asynchronous task under a synchronous setting, of which further execution may be held until the Promise is fully resolved to a definite value.

Interestingly enough, Futures in asynchronous programming is conceptually similar to Futures contracts in finance, which “resolves” to a fixed trading price at its expiry date. This is perfect, as blockchain is the only industry in the world where one can blend both concepts into one.

Let’s assume that Alice is holding token APPLE on CHAIN_A, and wants to perform some action with the APPLE token of which logic is only available on CHAIN_B, that yields some output token PEAR. Typically, such an operation would be done like this

CHAIN_A                      CHAIN_B
| |
SEND: APPLE -----RELAY-----> RECEIVE: APPLE
| |
| LOGIC:
| IN: APPLE
| OUT: PEAR
| |
RECEIVE: PEAR <----RELAY---- SEND: PEAR

This would result in a minimum of two transactions on CHAIN_A (the “client”) for a round-trip, and a minimum of three transactions on CHAIN_B (the “server”) for receiving the input token APPLE from CHAIN_A, processing logic that accepts input token APPLE yielding output token PEAR, and sending back output token PEAR back to CHAIN_A.

This is, obviously, non-atomic. If Alice wants to do something else with PEAR, she cannot do so within a single transaction, and must manually invoke another transaction after waiting for the round-trip operation to settle.

But if we can enable Promise objects on CHAIN_A in the form of Futures tokens, Alice can effectively perform operations asynchronously on CHAIN_A without having to wait for a previous round-trip operation to settle.

CHAIN_A                      CHAIN_B
| |
SEND: APPLE -----RELAY-----> RECEIVE: APPLE
MINT: fPEAR |
SWAP: fPEAR -> PEAR LOGIC:
| IN: APPLE
| OUT: PEAR
| |
RECEIVE: PEAR <----RELAY---- SEND: PEAR
(LOCK) |
| |
SWAP: PEAR -> fPEAR |
BURN: fPEAR |
CLAIM: PEAR |
(UNLOCK) |

In plaintext, this is what happened:

  1. Alice has initiated this action on CHAIN_A by sending over APPLE tokens to CHAIN_B. However, this time she was minted a Futures derivative token that targets the value of its intended output token (i.e. representing a token that will be returned sometime in the future), called fTokens. In this case, Alice was immediately minted fPEAR within a single transaction.
  2. Within the same transaction, Alice has swapped fPEAR back to PEAR on an AMM available on CHAIN_A. As this is effectively selling off futures for its intended output asset (i.e. devalued based on future consensus and transaction costs with failure risks), Alice faces a loss, but can preserve complete atomicity of the transaction. This means that she can use the resulting PEAR tokens with logic that resides on CHAIN_A or another blockchain, say CHAIN_C, all within the same transaction.
  3. Meanwhile, the transaction has completed round-tripping, but this time the resulting PEAR tokens get locked in a contract on CHAIN_A. A third-party arbitrager, Bob, may buy back fPEAR from the on-chain AMM with PEAR he already holds to buy fPEAR at a lower price than its intended peg, and burn fPEAR to claim PEAR locked in a contract for profit.

Unlike Promises and Future contracts, fTokens have unique properties that differs from both of them:

  • All fTokens are fungible per operation: if you are dealing with the same contract with the same input and output token, then fTokens for the specified output token are always fungible. Essentially fTokens are minted with its consecutive transaction on the foreign chain as collateral, and could be treated as asynchronous debt; liquidating this debt is equivalent to resolving Promise objects. Due to this property, fToken pools share common risk profiles — more on this later.
  • fTokens have no specified expiry date: they may be redeemed back to its underlying asset at any time, but is subject to additional risks the longer someone holds onto them.
  • fTokens may be redeemed back to its underlying token, as long there is enough liquidity within the output pool: on Future expiry, fTokens target a 1:1 peg. However, due to potential frontrunning attacks or operation failure, there may not be enough liquidity within the output pool to redeem all fTokens one-to-one.

Configuration & Logic

Components are as follows:

  • An oracle feed for transaction simulation: Periodically feed expected output amount in output token denomination. Should also specify maximum tolerated slippage for this fToken, as there may be loss involved with transaction processing delays on the foreign chain. May be replaced with on-chain logic that simulates transaction results on the foreign chain.
  • Transaction relayers: Relay token transfers and arbitrary messages between CHAIN_A and CHAIN_B. Any basic token transfer bridge should work, although arbitrary messaging support would be a plus.
  • Job queue & Jobkeepers: Maintain a queue of jobs due for processing on the origin chain; jobkeepers stake tokens to participate in an auction. If an operation fails, staked tokens are slashed to compensate for fToken pool loss; successful keepers are rewarded with jobkeeper fees with subsidized transaction fees. May be replaced with third-party decentralized DevOps such as keep3r.network, or temporarily a centralized bot. Ideally an IBC module could be built that tracks this job queue and maintains transaction ordering; this is set aside for now as future work.
  • Arbitragers: External parties that manage the fToken — Token AMM pool, as well as to perform arbitrage on this pool to buy back fTokens below peg with Tokens, burn them, and receive back more Tokens. This is equivalent to resolving a Promise object combined with bond liquidation properties.

Full logic, roughly, could be put as follows:

  • Alice calls an on-chain entry contract from CHAIN_A. The contract transfers specified APPLE balances from Alice’s wallet to the contract account, and stores them within an input queue present on CHAIN_A.
  • The entry contract calculates how much PEAR Alice would have received (transaction simulation), and mints equivalent amounts of fPEAR. Actual transaction execution is sent over to a job queue, with unsigned transaction metadata including contract ABIs, input token amount, destinations, etc.
  • Jobkeepers run an auction to process jobs listed on the job queue. As transaction metadata already exist, jobkeepers first transfer required APPLE balances to CHAIN_B over a transaction relayer, and run the transaction as specified. Resulting token PEAR should be returned to an exit contract present on CHAIN_A.
  • The entity holding fPEAR now has the right to claim equivalent amounts of PEAR from the exit contract by burning them. This entity may be Alice, or external arbitragers (if Alice sold off resulting fPEAR in one transaction for receiving PEAR).

Attack Factors & Future Work

There could be two primary attack vectors here: loss due to transaction failure on the foreign chain (including foreign chain failures, oracle failures, and transaction simulation failures), and sandwich frontrunning by jobkeepers or external parties.

Jobkeepers should retry until Future is fully resolved, but this will also incur additional gas costs; in cases where a particular job fails, this will result in a net loss for the output pool, resulting in fTokens permanently going off-peg. Promises in programming have (pending, fulfilled, rejected) states per object of which will solve this issue, but this will harm fungibility — unless each Promise object is built as NFTs. Current design relies on slashing to cover potential failure losses, but economic implications and effectiveness of such a mechanism require further research.

Sandwich frontrunning is also an issue. This means that, a jobkeeper or an external party may inject transactions in front of and after the target transaction to slowly drain funds within the exit contract. This is very similar to MEV attacks on the mempool or sandwiching bot attacks currently present on Ethereum-based DeFi protocols.

  • Jobkeeper Cindy receives a job for trading APPLE to PEAR on Uniswap. She first inserts a APPLE -> PEAR swap transaction in front of the job transaction, and performs Alice’s APPLE -> PEAR swap transaction next, resulting in less PEAR tokens being returned to the exit contract.
  • Cindy can then insert a PEAR -> APPLE swap transaction immediately after Alice’s swap transaction, earning an instant profit.

Setting slippage tolerance to a lower value should solve this issue, but this also conflicts with operation rejection loss issues (of which slippage tolerance should be set higher). Calculating an optimal value for this should be done in addition to considering both attack scenarios.

Last but not least, there is the issue of capital inefficiency. There needs to be minimal liquidity for the fToken — Token trading pair on every “client” chain, but this means that some fToken debt will not be finalized forever. Any additional loss within the exit contract token pool could be incurred to liquidity providers in addition to impermanent loss.

Any feedback is welcome — please leave a comment, or feel free to send an email at me@unifiedh.com.

--

--

Daniel Hong

🌈(🇰🇷,🏳️‍🌈) bitcoiner since 2008 | hobo @nonce_community | building universes