Request Ordering

WingRiders
4 min readFeb 1, 2022

--

In a previous blogpost we described the batching model. Users wanting to swap would create a swap order. Agents are able to find these via a browser on-chain and fulfill them in the order they were accepted into blocks. This is a guarantee that we expect from all agents.

We want to highlight some of the seemingly smaller challenges as concurrency, but equally important for all parties involved. There are multiple ways the order of fulfillment needs to be ensured. First, the agents need to find the oldest orders for a specific liquidity pool that can be fulfilled. This ordering is enforced to ensure fairness in a first-come first-served promise made to our users. Once the oldest orders are selected, an agent needs to create a batch that fits within the transaction size limits and smart contract execution units. The ordering even within these batches is crucial.

Imagine that there is a very simple batch of 2 orders:

1. Alice swaps 10,000,000 BTC to ADA which causes huge slippage

2. Brian swaps 10 ADA to BTC

Even in this case the order is important. When Brian swaps his 10 ADA after Alice’s swap was fulfilled, he will get much more BTC due to the slippage caused by Alice. However, if we had applied Brian’s swap earlier, he would get much less, but it wouldn’t hugely impact Alice.

Smart contracts on the Cardano network do not have a global scope. The contracts rely on the transaction builder (an agent) to know the order in which the swaps need to be fulfilled. But even if the agent knows the order, the situation is not simple. By design, inputs in a transaction in Cardano are not ordered. On the other hand, the smart contract sees an exact order of inputs: a canonical order of the inputs’ transaction hashes and output index.

To demonstrate this property of Cardano, we’ve created a smart contract that you can find at https://github.com/WingRiders/plutus-input-ordering

The only thing the smart contract checks is whether the transaction creator has provided the correct “index” for all script inputs. The indices express the positions of the inputs in a list in the order how they appear inside the smart contracts. To give you an example, imagine that we have 2 orders as transaction outputs how the agent sees them:

  • Alice’s transaction input id: df38f6c66a5a45b9b4d7aadadf8bb1b548a2aa33380e4401a66fa2f5dc055ee2#1
  • Brian’s transaction input id: 30819ac66d003b5b6aa7e723cbf9de517d0af09ad38837d9ee6756d22b23e13a#1

We would like to fulfill these orders in the order they were created. Alice’s first then Brian’s. So the transaction builder would assume that Alice’s input should go first into the transaction and assign the redeemer to the 0th index, which signifies the expected order in the list. Similarly, Brian’s smart contract redeemer would be 1. With the CLI tool you could try doing something like:

./cardano-cli transaction build --alonzo-era --testnet-magic 1097911063 \
--tx-in "df38f6c66a5a45b9b4d7aadadf8bb1b548a2aa33380e4401a66fa2f5dc055ee2#1" \
--tx-in-script-file ordering-script.plutus \
--tx-in-datum-value "Alice" \
--tx-in-redeemer-value 0 \
--tx-in "30819ac66d003b5b6aa7e723cbf9de517d0af09ad38837d9ee6756d22b23e13a#1" \
--tx-in-script-file ordering-script.plutus \
--tx-in-datum-value "Brian" \
--tx-in-redeemer-value 1 \
--tx-in-collateral ${COLLATERAL_UTXO} \
--protocol-params-file params.json \
--change-address $(cat mywallet.addr) \
--out-file transactions/order-tx.raw

But this would fail:

Command failed: transaction build  Error: The following scripts have execution failures:
the script for transaction input 0 (in the order of the TxIds) failed with:
The Plutus script evaluation failed: An error has occurred: User error:
The provided Plutus code called 'error'.
the script for transaction input 1 (in the order of the TxIds) failed with:
The Plutus script evaluation failed: An error has occurred: User error:
The provided Plutus code called 'error'.

The reason is, that when the smart contracts are executed on the blockchain, the smart contract would see the transaction inputs as a list in the canonical order:

  1. “30819ac66d003b5b6aa7e723cbf9de517d0af09ad38837d9ee6756d22b23e13a#1”
  2. “df38f6c66a5a45b9b4d7aadadf8bb1b548a2aa33380e4401a66fa2f5dc055ee2#1”

So if a different contract (like a liquidity pool), would want to ensure that the orders were fulfilled in the right order, it cannot just rely on the order in which the contracts receive the list of transaction inputs.

To show you a bit larger transaction with 3 inputs: Cardanoscan

You can also see the raw signed transaction in our GitHub repository: WingRiders plutus input ordering

This transaction has 3 inputs in the order listed in the binary format and in the order we would want to fulfill the requests:

Alice "c5be856b78159bee00cdf8e0028c76b8805f95ef8be150be38177dc2317af3da#0"
Bob "2ea6bc1be2800292215d45df239d2f0fbb3d26a31cd72747af574a5a2decc499#0"
Cecil "69767ac30d4c8a1c3e4f0b0ceccdef37e6a123ca8aef6d104e2d19373f17855d#0"

But the redeemers, that the transaction creator needed to define to make the contracts pass, which tell you the canonical order a smart contract would see (you can see on Cardanoscan as well):

  • Alice — 2
  • Bob — 0
  • Cecil — 1

Based on this, the contract sees the input order as

  1. Bob
  2. Cecil
  3. Alice

and shows you that smart contract developers cannot just rely on the input order as seen by the smart contract. Without additional logic, the contracts wouldn’t be able to ensure that the requests are fulfilled in the “correct” order.

We at WingRiders want to ensure that all swaps in the DEX are fulfilled in the order they were created by the users. What’s more, we want to make sure that inside a single batch, the contracts themselves can verify that all users have been compensated correctly and that the contracts would fail otherwise. We don’t want to compromise on precision of the calculations inside the smart contracts just because the contracts cannot rely on the input order. We have developed a method that allows agents to ensure ordering even inside the transactions. It’s live on TESTNET.

Feel free to discuss Ordering with us on Discord & follow our upcoming announcements on Twitter .

--

--

WingRiders

Secure & fast, DAO operated, cutting edge DEX on Cardano. Supporting HW wallets and direct Android use & giving liquidity providers up to 6 sources of gains.