-
Notifications
You must be signed in to change notification settings - Fork 36
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
EIP-0031 - Babel Fees #54
Conversation
0028 already taken by #53 :) |
Oh, sorry I did not notice it - when I was creating my proposal "EIP-0028" name was still available. As I can see, your ErgoAuth proposal is still "WIP" and has a few "TODOs" in it. Babel Fees proposal is completed and is (should be) ready to be review and hopefully merged. Because of this, I expect that it would be probably merged earlier than the ErgoAuth proposal. Since I've used "EIP-0028" a lot across the babel fees proposal (branch name, in the proposal, in ergoplayground...) and it (probably) would be merged earlier, it would make me happier if I could just stay with the current "EIP-0028" name and the ErgoAuth proposal would be assigned with the next number. Would it be a problem for you? :) Thank you for your understanding. Sincerely, Martin. |
I think this proposal is way behind ErgoAuth, which is already implemented in Appkit and Wallet App. |
Did so. Could you review the EIP now? :) |
eip-0031.md
Outdated
-------------------------------------------- | ||
|
||
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 “babel fees“, please see the following IOHK article: | ||
[https://iohk.io/en/blog/posts/2021/02/25/babel-fees/](https://iohk.io/en/blog/posts/2021/02/25/babel-fees/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Historically, the idea was first proposed on ErgoForum back in 2019 https://www.ergoforum.org/t/paying-fee-in-ergomix-in-primary-tokens/73 , and implemented in Ergomixer in 2020
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The purpose of this part of text (to which you left your comment), is to give credit to the place from which I've got inspiration for the name of this EIP. And since this EIP concept has the same name as the similar concept on Cardano, I thought it'd be appropriate to explain the difference to the EIP reader. Should I also mention somewhere in the EIP that the similar idea was implemented in ErgoMixer for mixing transactions fee payments?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that it would be reasonable to mention ErgoForum ideas and ErgoMixer, to give credits to their authors, and also there are babel fees variants there which can be useful in some use-cases.
eip-0031.md
Outdated
|
||
val babelFeeBoxCreator = SELF.R4[SigmaProp].get | ||
|
||
val recreatedBox = OUTPUTS(OUTPUTS.size - 2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this index chosen?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I'm right, the Ergo core protocol does not specify transactions fee payment, however, the reference implementation has a hardcoded ErgoTree value on which it expects fees to the miner. If I'm also right (based on my observation), the reference implementation (ergo node) doesn't care about the index of such fee output and accepts it as fee on any transaction output index. Most of the wallet implementations (or to be more precise, the libraries which are used by the wallet developers) put fees UTXO as the last output of a transaction. Thus, I can consider the last output index of a transaction as reserved for fee paying in ERG. So I decided to use the penultimate (second from the end) output as "reserved" for fee paying in case that Babel Fees are used. Based on my observation, many smartcontracts written in ErgoScript use the first output of transaction (output with index = 0) for checking of recreation of some boxes etc. So selecting the penultimate output index as the index "reserved" for Babel Fees paying would be compliant with usage of such smartcontracts - the output indexes wouldn't be in conflict. Of course there can be also smartcontracts which will be using the penultimate output index for some purpose, but I haven't seen such a contract, while I have seen a lot of contracts using first transaction output from which I assume that this choice would be compliant with most of the contracts which are currently being used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the tx has a change box, for whatever reason, then it will be at index OUTPUTS.size-2. I could be wrong, but that's what I have noticed when using AppKit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit: I am wrong, the change box is last. Even then though, the miner fee box would end up in index OUTPUTS.size-2
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change box is last, that is correct. But change box is not mandatory
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if the tx has a change box then you can't use this contract with Appkit, shouldn't EIP contracts take into account use with AppKit, isn't that the point of having a library?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With sigma-rust we also haven't a way to set a outputs order, so I just reorder the outputs after transaction is generated.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I have been doing some more work on GuapSwap and may need to use more than one babel box, depending on the token, in one transaction. So OUTPUTS(OUTPUTS.size-2)
is very restrictive in this case. Is doing:
OUTPUTS.filter{ output: Box => output.propositionBytes == SELF.propositionBytes }(0)
possible instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another option is to set output index in context extension of spending input.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
that's cool way to do it
babelFeeBoxRecreated && exchangeOK && (nanoErgsDifference >= 0) | ||
) | ||
) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way to enforce that the erg swapped from the babel box should be sent to a miner?
I can see a scenario were the price of a certain token spikes above the rate settled on R5
, and anyone can make use of babel boxes for arbitrage. I don't know if this is something desired.
PS: I'm on my first steps on ErgoScript so It's almost certain that I'm missing something. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's ok. Babel fee box is in reality just an order book order to sell ERG in exchange for specified tokens at specified price. The babel fee box creator doesn't care about how these ERGs are spent - he earned revenue by selling these ERGs for a higher price than the ordinary market price (see text about financial incentives in this EIP). It's fully up to him to monitor whether he still wants to sell ERGs for the price specified in the R5, in case he no longer wants doing so, he can withdraw all funds from his babel fee box (and recreate it with new price in R5 in case he just wanted to update the price).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There can be coded offchain bots which will monitor current market price for some token and always recreate babel fee box with price equal to market price times some constant (in case that it would be worth spending ERG to pay fees for box recreation).
eip-0031.md
Outdated
-------------------------------------------- | ||
|
||
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 “babel fees“, please see the following IOHK article: | ||
[https://iohk.io/en/blog/posts/2021/02/25/babel-fees/](https://iohk.io/en/blog/posts/2021/02/25/babel-fees/) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that it would be reasonable to mention ErgoForum ideas and ErgoMixer, to give credits to their authors, and also there are babel fees variants there which can be useful in some use-cases.
Two remarks:
|
@MrStahlfelge I can provide example ergoTree (which is the same for both mainnet/testnet) together with tokenId for which was the ErgoScript source compiled. Getting an address for some token would then mean just replacing this tokenId with a new tokenId inside of this ergoTree bytes and then converting those ergoTree bytes to the P2S address (simple operation). What do you think about such a solution? Example tokenId:
Example ergoTree:
Answer to the second of your remarks: Yes, it is the equivalent of an order book buy entry. The babel fees exchange providers don't need to care about to whom they are selling the ERGs, nor how the buyer would use them. They do it simply for profit, thus they setup the babel fee box with price (probably a much higher than the ordinary market price on exchange) which will bring them some profit, while utilizing the fact that the users are willing to pay more in exchange for the opportunity to pay the transaction cost in babel token (eg. SigmaUSD) instead of having to bother with buying ERGs. |
Yes, it could be done this way. Or the tokenId could be a register parameter, which has ups and downs. By the way: the precondition |
Unique ergoTree per tokenId is the way to go IMO, makes it scalable and easer to find babel boxes. |
Okay then, in any case the ergotree should be added to the EIP so that wallets and liquidity providers know where to look for/store the babel fee contract. |
During the last community chat an idea come up: why not to make babel contract to allow two-way swaps? With this, liquidity providers can potentially take advantage of arbitrage actors to make profits from babel boxes. Here is an exampleLet's say that ERG is at 2 SigUSD at current market price, a liquidity provider can create an ERG/SigUSD box, optionally filled with only ERG, accepting to sell ERG for, let's say, 2.5 SigUSD, and rebuy ERG with the accumulated SigUSD for 1.9 SigUSD. This way a liquidity provider will be buying low and selling hight, something remotely analogue to a grid trading system. Pros
Cons
|
Maybe I am understanding wrong, but anyone can create a babel fee box according to the EIP, so this already works in two ways. |
Could you not just search for a box with the right babel contract and the user's PK in R4? |
Of course! Specially with ergo-graphql ;) But this is intended to be used when a LP wants to withdraw their funds.
I'm sorry for not being clear enough. Let me try again: I'm looking for a way to create a mechanism that offers better incentives for liquidity providers by taking advantage of arbitrage agents. Hopefully this will bring and retain more liquidity to babel boxes. So the contract should allow three distinct operations, namely:
However, I'm not sure if this is something desired, in my opinion, Babel Fees are something intended to be heavily used, specially in composition with another contracts, so if the contract get too complex, the weigh and computational costs can affect adoption, so a balance should be met. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After second review, I've found that there is no protection from spending multiple babel fee box with a single output.
See ErgoScript whitepaper https://storage.googleapis.com/ergo-cms-media/Ergo_Script_39efbe3c3a/Ergo_Script_39efbe3c3a.pdf , pg 9 :
"This script ensures that the box can be spent only in a transaction that produces an output with
60 tokens of type token1 and gives them to Alice (Alice can reclaim the box after the deadline).
Moreover, the last condition (OUTPUTS(0).R4[Col[Byte]].get == SELF.id) ensures that if Alice
has multiple such boxes outstanding at a given time, each will produce a separate output that
identifies the corresponding input. This condition prevents the following attack: if Alice has two
such boxes outstanding but the last condition is not present, then they can be both used in a single
transaction that contains just one output with 60 tokens of type “token1” — the script of each
input box will be individually satisfied, but Alice will get less only half of what owed to her."
I guess similar check is needed here as well
Sorry for such a long time without response (I was busy and later away from PC). Firstly (few weeks ago) I wanted to reply to @lucagdangelo that his proposed solution for multiple babel fee boxes using Later (a week ago) I realized that there already exists the very same critical vulnerability in the current version of contract. However, it needs a very specific scenario to be exploitable (following one): When there exists two babel fee boxes belonging to the very same owner for the very same babel token, with very same price value set and also "compliant" amount of nanoERGs on each of them to fulfill the Then somebody could exploit this, spent both boxes while recreating only one of them, thus draining funds from the affected user (owner of the affected babel fee boxes). However, when I was thinking about the possible solutions to this problem (happening only in the very specific scenario (explained earlier)) only more complicated ones came to my mind. One of them was an usage of special identification NFTs which would complicate the smartcontract a bit but will allow to implement similar scalability solution as proposed earlier by me in the InERGitance documentation; see here. This could make searching for the babel fees belonging to a specific user (owner) much faster. However, such a solution would probably be non-necessary overkill (assuming that there will not be thousands of babel fee boxes for some specific token, thus we don't need such a scaling solution). @kushti 's proposed solution is simple and would be more than enough to fix this problem. With combination of this solution with "dynamic" search for the recreated babel fee box in the OUTPUTS list we can solve two another "problems":
I'm going to update the smartcontract accordingly. Thanks for the feedback of all of you who commented :) |
For GuapSwap I am working on a subproject that would let miners contribute to providing liquidity to babel boxes. So it might happen that 1000+ babel boxes for one token could exist. |
As I've promised, I made an update to the smartcontract code - using @kushti 's approach. This way we solved not only the critical vulnerability which was possible in very specific scenario (see comments above), but also made contract more general - right now, a recreated babel fee box can be located at any index in the transaction output list. Also, multiple babel fee boxes can be safely spent in one transaction (feature requested by @lucagdangelo ). Instead of making a new commit to the EIP markdown source, I'm posting a new version of the contract here in the comments for review and discussion. Current changes:
New smartcontract source code: {
val babelFeeBoxCreator = SELF.R4[SigmaProp].get
val isRecreatedBabelFeeBox = {(outputBox: Box) => (
outputBox.propositionBytes == SELF.propositionBytes &&
outputBox.R4[SigmaProp].get == SELF.R4[SigmaProp].get &&
outputBox.R5[Long].get == SELF.R5[Long].get &&
outputBox.tokens(0)._1 == tokenId &&
outputBox.R6[Coll[Byte]].get == SELF.id
)}
val recreatedBox = OUTPUTS.filter(isRecreatedBabelFeeBox).getOrElse(0, SELF)
val nanoErgsDifference = SELF.value - recreatedBox.value
val babelTokensBefore = if(SELF.tokens.size > 0){ SELF.tokens(0)._2 }else{ 0L }
val babelTokensDifference = recreatedBox.tokens(0)._2 - babelTokensBefore
val exchangeOK = babelTokensDifference * SELF.R5[Long].get >= nanoErgsDifference
val allOK = { if(OUTPUTS.exists(isRecreatedBabelFeeBox)) { exchangeOK && (nanoErgsDifference >= 0) } else { false } }
sigmaProp(
babelFeeBoxCreator || allOK
)
} Reference updated ErgoScript playground here Looking forward for your feedback, gonna post new |
The only thing I would change now is using a variable for |
And maybe use the babelFeeBoxCreator variable in the isRecreatedBabelFeeBox check? |
I rewrote the contract to include the use of context variables for the output index, as kushti suggested, so the filter and exist loops can be removed and you don't have to check for the box being in the output two separate times. This is just an idea. {
// ===== 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.
// ===== Relevant Variables ===== //
val babelFeeBoxCreator: SigmaProp = SELF.R4[SigmaProp].get
val ergPricePerToken: Long = SELF.R5[Long].get
val recreatedBabelBoxIndex: Int = getVar[Int](0).get
val tokenId: Coll[Byte] = _tokenId
// ===== Outputs ===== //
val recreatedBabelBox: Box = OUTPUTS(recreatedBabelBoxIndex)
// Check conditions for a valid babel fee swap
val validBabelFeeSwap: Boolean = {
// Check that the babel fee box is recreated correctly
val validBableFeeBoxRecreation: 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) || babelFeeBoxCreator
} |
Thank you for your effort to improve current babel fee contract @lucagdangelo. In the first place, there are a few typos in your proposed contract which need to be fixed in order to render your contract to be compilable. Your version of contract has a few disadvantages - it allows only one babel fee box to be spent in transaction (thus it's going against your requirement mentioned a few comments earlier) and requires additional efforts to add context extension while creating the transaction. I tried to do a comparison to see the size (amount of Bytes in corresponding ergoTree) difference between various compiled versions of contract. See table bellow:
Total size denotes the total size occupied in transaction, in case of version LGD there are 2 more Bytes consumed by Int serialization inside of context extension. Versions legend:
As we can see, version NewOK has the most abilities, however it has a 48% bigger ergoTree footprint than the original version. Version NewAP has the same abilities as the NewOK version and it has only 1 Byte higher footprint than the NewFV version, however there's the problem with AOT costing. LGD version lack functionality while having same footprint as the NewAP version. I propose that we should find a way how to fix AOT costing problem in NewAP version while trying to keep resulting footprint as low as possible. If anybody want to experiment with it, it can be easily done via ErgoScript playground, just include import org.ergoplatform.Pay2SAddress
//contract definition
//contract compilation
println(Pay2SAddress(babelFeesContract.ergoTree)) Now just copy compiled P2S address and use node's endpoint to get ergoTree from address (needs to be done on testnet node), for example: |
How would it prevent multiple boxes from being spent? Each input babel box must be provided an output index context variable for its corresponding recreated box. What am I not seeing? Also using context extensions is super easy with appkit. Also, where is the NewAP version you mention? |
Sorry, my fault (I mistakenly assumed that the context extension is the same for all inputs - I never used it). You are right, your code then should be semantically equivalent to mine. However, I would still prefer to not require an implicit output index via the context extension.
|
eip-0031.md
Outdated
|
||
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 and the availability of babel fee boxes (UTXOs) for the specific tokens of interest in the blockchain should be subsequently checked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As pointed out before, the P2S addresses can change when a new compiler version is used. The addresses for a token should be defined in the EIP to avoid having diverging addresses in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no need for that.
- Address will change from token to token;
- It's very unlikely to have compiler serializations changes, I could be wrong but it requires a hard fork to happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is some misconception, in the EIP text "smartcontract for each token which user holds should be compiled".
When we talk about smart contracts we should really mean contract templates (see EIP-5) or at least ErgoTree.
ErgoScript should be out of question as well as any compilation
. Compilation should not be part of any on-chain protocol. First, it is unstable, second it is not standardized.
Contract Template capture the parameterized nature of smart contracts and ideally any App which want to support pre-defined list of contracts should actually use a predefined list of Contract Templates.
Potentially unlimited P2S addresses can belong to the same Contract Template.
At the same time, any protocol usually require fixed list of Contract Tempates which can be hard-coded.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, I didn't know about EIP-5, contract template sounds like a great addition to have on EIP text. Thanks for the clarification )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see no need for that.
I see a high need for a more defined approach, as the contract in the EIP does not compile for me. So changes are needed on my side to get it working, and the chances are high that I end up with another ergotree than others.
Maybe EIP-5 is a solution as well.
Btw, ergotree I came up with is 100604000e20 tokenId 0400040005000500d803d601e30004d602e4c6a70408d603e4c6a7050595e67201d804d604b2a5e4720100d605b2db63087204730000d606db6308a7d60799c1a7c17204d1968302019683050193c27204c2a7938c720501730193e4c672040408720293e4c672040505720393e4c67204060ec5a796830201929c998c7205029591b1720673028cb272067303000273047203720792720773057202
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Contract fixes pushed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is your ergo tree the same?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yup
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What stops us from adding it to the eip? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Contract Template" subsection added
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@nitram147 wrote the original playground for the previous version of the contract. I tried editing it but I don't know how to use context variables in the simulator, it doesn't seem like that functionality is available. Do we need the playground example?
I don't think so, I can link AppKit and Nautilus as reference implementations, should be enougth. |
Added missing |
Hi,
please review my EIP-0028 Babel Fees proposal and let me know whether is there something to be changed :)