Deposits (Ethereum > Cosmos transfers) happen as single operations. Each deposit creates a single oracle event that is voted on see the deposit spec. Withdraws are more complicated, since Ethereum gas is very expensive we want to spread out the cost of verifying the validator signatures across as many transactions as possible. Hence batches.
When a user calls MsgSendToEth they lock up some tokens and some fee value into the 'Gravity tx pool' which is a pool of transactions waiting to enter batches.
Transaction batches contain only a single uniform ERC20 token token type for all transactions and fees.
Relayers observe the pool and query for what fees they might be paid for a batch of a given ERC20 contract. Relayers (or anyone) may request a batch be created for a specific token type. Once that is done the validators sign off on this batch via their orchestrators.
Batch creation may fail if there are no transactions of that token type in the pool or if the new batch would not have a higher total fee amount than an existing batch that is waiting to execute.
At this point any relayer (the one that requested the batch or otherwise) may bundle those signatures and submit the result to Ethereum. Paying the gas fees in return for all of the fees for all the transactions in that batch.
While relayers request batches they are created by the Gravity Cosmos module itself, with the highest fee transactions in the pool for that particular token type going first. Up to a current max of 100 transactions per batch.
While transactions are in the pool it is possible for the user to request a refund and get their tokens back by sending a MsgCancelSendToEth but this is no longer possible once a transaction is in a batch. As it may be possible for that batch to execute on Ethereum as soon as signatures start coming in.
Once a MsgSendToEth transaction is out of the pool and in a batch one of three things must happen to the funds.
- The batch executes on Ethereum, the transfer is complete, locked tokens are burned on Cosmos
- The batch times out, once it's timeout height in blocks is reached, the locked tokens are then returned to the pool
- A later batch is executed, invalidating this one and making it impossible to submit. The locked tokens are then returned to the pool
With the general flow of batches resolved we should focus in on the largest problem of batch creation. Ensuring that permissionless requests always result in a batch that may be relayed. Even given a large amount of low fee spam and hostile batch requestors.
Suppose we have an infinite stream of low fee spam transactions going into the Gravity tx pool. Any given batch creation event will then lock up high fee 'good transactions' with dozens of spam transactions.
In this case no relayer will ever chose to relay a batch because all of them will be unprofitable.
In order to ensure that profitable batches are eventually created we must avoid locking up the high fee 'good transactions' into obviously bad batches. To add to the difficulty we don't actually know what any token in this process is worth or what ETH gas costs.
The solution this patch provides is to ensure that a new batch always has higher fees than the last batch. This means that even if there are infinite spam transactions, and infinite spamming of the permission less create batch request batch creation will halt long enough for enough good transactions to build up in the pool and a new more profitable batch to be created.
Over a long enough timescale the old batches either time out. Returning profitable transactions to the pool to create a new even better batch. Or the profitability of the latest batch continues to increase until one is relayed, also freeing the previous good transactions to go into the next batch.
So given this condition we can say that no matter the inputs a successful batch will eventually be created.
Remember the relayers can freely observe prices on Ethereum and know what the exchange rate for a given token is. They may also have different preferences for which token they are paid in, for example if you already have DAI liquidating that DAI to ETH to pay for more batches is cheaper per DAI. A $200 DAI reward is only worth $150 if it costs you $50 to exchange it for ETH on uniswap. But if you already have $1k in DAI that $50 doesn't seem so bad.
This is why batch requests must be permissionless and open. Not only are the prices for any given token unknowable by the Cosmos chain itself without a very complicated token price oracle but the relayers preference is totally subjective.