- Author: nitram147, lucagdangelo, anon-br
- Status: Proposed
- Created: 25-January-2022
- Last edited: 03-November-2022
- License: CC0
- Forking: not needed
The term “babel fees“ refers to the concept of paying transaction fees in tokens (fe. stablecoins) instead of platform’s primary token (ERG). For more information about the origin of the term and concepts behind “babel fees“, please see the following articles:
- Babel fees - denominating transaction costs in native tokens
- Paying fee in ErgoMix in primary tokens
EIP-0031 aims to provide the standard for paying fees in tokens, and thus has the same goal as Cardano’s “babel fees“, however, it chooses a different approach, with the main difference being that EIP-0031 does not require any type of forking.
With the Cardano’s approach, user publishes “invalid"(incomplete) transaction and has to wait, hoping that somebody will take his tokens and pay the transaction fees in a primary token (ADA), therefore completing the transaction. EIP-0031, on the other hand, chooses the opposite approach.
Supporters who wish to make money out of EIP-0031 will publish UTXOs, containing primary tokens locked by smartcontract. These will contain price attribute (i.e. how much of the primary tokens is that one specific supporter willing to pay for one piece of user’s tokens (fe. stablecoins)). Let’s call this user’s token a “babel token”.
User who is willing to pay the transaction fee in babel tokens can now find whether there exist any UTXOs belonging to the P2S address specified by the corresponding smartcontract for that specific babel token. If there is any UTXO which contains enough primary tokens for required fees, the user can calculate the price of buying the required amount of primary tokens from this UTXO and then decide whether or not he wishes to use it. In case he accepts this exchange ratio (defined by the UTXO’s price attribute), he can consequently spend this UTXO in his transaction to cover the transaction fees. This spending user now has to recreate the UTXO with the same parameters and insert the required amount of babel tokens into it (primary tokens difference should be less or equal to inserted babel tokens amount times price), which is going to be ensured by the smartcontract.
Strong advantage of this approach (compared to Cardano’s one) is that user always knows in advance whether there is an opportunity to pay via “babel fees” or not, and if there is, what is the exchange ratio. He can therefore be (almost) certain that if he decides to use it, his transaction will be included in the blockchain ledger. Be aware, however, that there exist some exceptions to this rule, which is later discussed in the “Wallets implementation” section.
Many users use blockchain solely for transferring (native)tokens, such as stablecoins, or even “meme” coins. These users, understandably, do not want to be bothered with keeping an eye on the amount of blockchain’s primary token they own, or even obtaining this primary token in the first place.
Once they run out of primary token, they have to difficultly (and often costly), swap their tokens of interest for the primary tokens, that they can later use to cover transaction fees. Since primary tokens are also needed for these swaps, users may be forced to introduce new capital to their portfolio solely for the purpose of purchasing primary tokens, used for fee paying.
Since basic transactional fees on the Ergo blockchain are generally quite low, the "babel fees" users would be probaby willing to pay a fee that could be higher than that of a primary token transaction, in exchange for being able to pay in their token of interest and not having to bother with the blockchain’s primary token purchase.
This brings up a financial incentive for “EIP-0031 supporters”, who could benefit out of this arbitrage by providing the liquidity for such “babel fees” users, with primary token’s selling price (expressed in tokens of interest) being higher compared to the same token pair on the exchanges.
Here is the smartcontract's source code in ErgoScript which will be used to protect the babel fee box:
{
// ===== Contract Information ===== //
// Name: EIP-0031 Babel Fees Contract
// Description: Contract guarding the babel fee box, checking if valid output babel box was recreated and the token exchange was valid.
// Version: 1.0.0
// ===== Relevant Variables ===== //
val babelFeeBoxCreator: SigmaProp = SELF.R4[SigmaProp].get
val ergPricePerToken: Long = SELF.R5[Long].get
val tokenId: Coll[Byte] = _tokenId
val recreatedBabelBoxIndex: Option[Int] = getVar[Int](0)
// ===== Perform Babel Fee Swap ===== //
if (recreatedBabelBoxIndex.isDefined) {
// Check conditions for a valid babel fee swap
val validBabelFeeSwap: Boolean = {
// Output babel fee box
val recreatedBabelBox: Box = OUTPUTS(recreatedBabelBoxIndex.get)
// Check that the babel fee box is recreated correctly
val validBabelFeeBoxRecreation: Boolean = {
allOf(Coll(
(recreatedBabelBox.propositionBytes == SELF.propositionBytes),
(recreatedBabelBox.tokens(0)._1 == tokenId),
(recreatedBabelBox.R4[SigmaProp].get == babelFeeBoxCreator),
(recreatedBabelBox.R5[Long].get == ergPricePerToken),
(recreatedBabelBox.R6[Coll[Byte]].get == SELF.id)
))
}
// Check that the user's token was exchanged correctly
val validBabelFeeExchange: Boolean = {
val nanoErgsDifference: Long = SELF.value - recreatedBabelBox.value
val babelTokensBefore: Long = if (SELF.tokens.size > 0) SELF.tokens(0)._2 else 0L
val babelTokensDifference: Long = recreatedBabelBox.tokens(0)._2 - babelTokensBefore
allOf(Coll(
(babelTokensDifference * ergPricePerToken >= nanoErgsDifference),
(nanoErgsDifference >= 0)
))
}
allOf(Coll(
validBabelFeeBoxRecreation,
validBabelFeeExchange
))
}
sigmaProp(validBabelFeeSwap)
} else {
// ===== Perform Babel Fee Box Withdrawl ===== //
babelFeeBoxCreator
}
}
Compilation of babel fee box smartcontracts for different tokens of interest will result in different P2S addresses, leading to each token having a unique corresponding ErgoTree and consequently a unique P2S babel fee box smartcontract address. To avoid having to rebuild the contract for each token, the implementer can use the following contract template, where the {tokenId}
fragment must be replaced by the hexadecimal identifier of the token of interest.
100604000e20{tokenId}0400040005000500d803d601e30004d602e4c6a70408d603e4c6a7050595e67201d804d604b2a5e4720100d605b2db63087204730000d606db6308a7d60799c1a7c17204d1968302019683050193c27204c2a7938c720501730193e4c672040408720293e4c672040505720393e4c67204060ec5a796830201929c998c7205029591b1720673028cb272067303000273047203720792720773057202
The position of the recreated Babel Box in transaction's outputs must be set as a context extension variable in the spending Babel Box.
- Context variable
0
:- Type:
SInt
- Value: position of the recreated Babel Box in transaction's outputs
- Type:
Parameters (creator’s public key and price) are specified via registers, meaning the resulting babel fee boxes from different creators will always belong to the same P2S address, which will improve their searchability.
- Register
R4
:- Type:
SigmaProp
- Value: creator's public key
- Type:
- Register
R5
:- Type:
SLong
- Value: how much nanoErgs is the creator willing to pay for one babel token
- Type:
- Register
R6
:- Type:
SColl[Byte]
- Value: id of the Babel Box being spent by this
- Only required for recreated boxes
- Type:
Babel fee box creator is able to spent the babel box in any circumstances.
Other users on the other hand can spend this box as input to their transaction only when they also recreate it as output of their transaction with the very same R4
and R5
registers values together with R6
as described above and insertion of a required amount of babel tokens (the amount of inserted babel tokens multiplied by the price specified in the R5 register has to be equal to or bigger than the amount of nanoErgs spent from the babel fee box).
Wallet developers will need to decide whether they want to support EIP-0031 or not. If they do decide to support this standard, they should also decide on which tokens they want to support (this could be done based on user requirements – e.g. implementing big stablecoins or “meme” coins, etc.), as this could be more convenient than supporting all tokens.
As P2S addresses belonging to specific token of interest stay the same, these addresses could be easily “hardcoded” when supporting only a few tokens. If the developers decide to support any token, the previously mentioned smartcontract for each token which user holds should be compiled or "mounted" as described on "Contract template" subsection and the availability of Babel Fee boxes (UTXOs) for the specific tokens of interest in the blockchain should be subsequently checked.
The proposed babel fee smartcontract is quite general and does not impose much restriction for the transaction. It is therefore possible to transact some tokens while paying babel fees with another tokens, etc.
Once the wallet finds a babel fee box which could be used to pay required transaction fees, it should calculate the required price for the transaction fee and present it to the user, so he can decide on using this particular option or not.
The wallet should also check current mempool and determine whether there exists someone who is currently trying to spend this specific babel fee box. In that case, the wallet should construct “chained” transaction (using the mempool’s transaction output (recreated babel fee output) as the new babel fee box input). This way, many transactions spending “the same” box could be chained and mined inside a single block. There can also occur a situation when the babel fee box owner is trying to spend his own box. In that case, the wallet should select another babel fee box, if available.
Once the wallet successfully crafts and relays the transaction to the mempool, it MUST keep an eye on the transaction until it is mined. This is important because the wallet cannot prevent somebody else from trying to spend the exact same babel fee box as our user’s transaction is trying to spend. When there are two transactions trying to spend the same box, only one from them can be mined and therefore included in the blockchain, while the other one has to be recrafted with the new babel fee box and relayed again to the network. Such thing should occur rarely, but when it does, the wallet has to be able to handle the situation correctly while notifying the user that the transaction did not go through.