Exponential Price Decay Contract

Background

The ENS registrar controller uses a price premium contract to calculate the premium of a name, which decays to 0 over 28 days. This uses a linear price function and has historically been $2000 up until recently when many names have been sniped and was changed in EP5 to $100k: [EP5] [Executable] Set the temporary premium start price to $100,000

This is probably as high as we can go for linear based pricing curve as the price will start to decay very quickly as you increase the price. E.g. at $100k over 28 days, that is already $3571 a day or $148 an hour. While that isn’t the worst, if we increase the price to $1m over 28 days, it’ll decrease at $1480 an hour, which would provide pretty bad UX for someone trying to buy a name at a specific price.

We have already had someone snipe a 3 letter name at $100k. In a dutch auction ideally we don’t want anyone to be able to buy the name at the top price otherwise the auction itself is not functioning properly. However with a linear function, to further increase the start price would decrease user experience in one of 2 ways. Either the price drops too quickly or the name will be in this auction for a much longer period of time, which is also a bad user experience. Therefore all things considered, an exponential price curve could be a better solution.

The initial price curve we have discussed is reducing the price by 50% per day, which can be expressed as the following equation:

Price = StartPrice * 0.5 ^ Days

This would create a price graph like this:

With a starting price of $100m, the price would drop to $3 mil in 5 days, $100k in 10 days, $100 in 20 days and $.0.37 in 28 days.

Here is the list of prices from day 0 to day 28:

As you can see after the initial high prices, the premium price has a much more manageable decrease in price

Implementation

To efficiently implement this in solidity, we needed a way to do floating point exponentiation in Solidity. Since this isn’t possible, the only way we could do it is fixed point exponentiation, but this means to get meaningful precision for something like 0.5^25.56 (25 days with 2 decimal precision) , we’d have to do 5^2556, which would give us a number with almost 2000 digits in it, which would overflow in the EVM. Therefore to represent the fractional part of the days with a reasonable amount of accuracy, we precomputed values using JavaScript and hardcoded these in a lookup table that would allow us to compute the exponents using much smaller numbers. The hardcoded lookup table gives us 16 bit precision, which can represent an accurate price every ~1.3 seconds, which is accurate enough for our use case.

For the whole days, we can then just right shift the number to get a cheap divide by 2. E.g. for 3 days we right shift by 3. For 3.5 days, we right shift by 3 and then calculate the fractional exponent using our table and combine them.

Finally to create a smooth transition to 0 premium after 28 days, we take the final value, which is around $0.37 and minus that from the value when calculating, so instead of having the price start at $100m and end at $0.37, we start at ($100m - $0.37) and end at 0.

The PR for the contract is available here:

From my benchmarks, the function takes around 30k gas. And if you’re wondering why it’s not in an array and looped rather than a bunch of if statements, it’s because we can’t make arrays constants in solidity yet, so it saves about 5k gas.

I’d appreciate any feedback on the implementation.

And thanks to @nick.eth for all the ideas for an efficient implementation.

5 Likes

I am not sure what it would take to make a slightly more complex curve, but I would like to see something that looks more like this:

What this does is preserve the basically linear drop from 2K to zero over around 14 days.

Is there a difference in terms of gas? Security? I would like to see the data on that.

This is interesting - especially to see the price visualized over 28 days.

I still would drag my feet over the gargantuan starting price of $100mm. As someone who hopes to one day own a three-letter ENS (fig.eth) this is no longer a possibility unless due to two weeks of human error.

I acknowledge the smoother distribution and price decay, however, is there an ability to choose a happy medium of a starting price? This will encourage a healthy exchange of ENS names.

There is some excitement to being able to “snipe” a name you have been waiting on for a year.

the high starting price is supposed to be so high no one would pay it, which then allows price discovery as the price decreases. we don’t want sniping, which is really just gas auctions

2 Likes

Fair point, forgot about the gas implications here.

It’s not clear to me why this would be an improvement. Can you elaborate?

If you look at these numbers basically the price movement from day 20 to day 28 serves almost no purpose, with a drop of only around $100. The majority of domains sold during the auction will probably sell in the $100 - $2000 range. I think the goal should be to give potential buyers enough time to find their price point. @jefflau.eth numbers would descend from $2K - $100 in around 5 days. My example shows that this period is expanded to around 15 days.

I see a different logic for each of the price ranges, which can blend into each other, but are actually quite different from each other. The first phase is the basically infinite price to the highest expected price, which I think could happen in the first 24 hours. From the highest expected price to around $2K, I think should take around half the remaining time, and then the final decent from $2K to the end of the auction should take the rest of the days.

I am imagining basically making a price decent curve that does this basically by design, i.e. by hand, then finding a line of best fit using automated tools. Then we can simply use the equation that is derived. If the equation adds complexity i.e. gas to the transaction, then maybe it’s not worth it, but maybe it’s fine. I am basically advocating for human design over basic math.

I disagree that it “serves no purpose”. I think having a whole week for the average (not rich) user to be able to register their name for relatively cheap, is the purpose here.

4 Likes

I see your point on the number of days dedicate to the $100-$0 range, particularly given the current gas cost of registering a domain. However, I don’t think this is something we should tweak to a fair-thee-well; an exponential curve has the easy to understand property that it diminishes by the same percentage each period, and I think this is very close to how people perceive cost. Baking in hardcoded assumptions around what a “highest expected price” etc seems more likely to require frequent changes, too.

One possible tweak would be to subtract ~$50 from all values and terminate the premium at day 21. That would give the following figures:

0, 99999952.32
1, 49999952.32
2, 24999952.32
3, 12499952.32
4, 6249952.32
5, 3124952.32
6, 1562452.32
7, 781202.32
8, 390577.32
9, 195264.82
10, 97608.57
11, 48780.44
12, 24366.38
13, 12159.35
14, 6055.84
15, 3004.08
16, 1478.20
17, 715.26
18, 333.79
19, 143.05
20, 47.69
21, 0.00
3 Likes

= human design.

"Humans are underrated,” - Elon Musk

I agree with subtracting ~$50 though for the current model.

1 Like

Is there any consideration for a piecewise solution?

I think on the previous thread, my thought was exponential decay followed by a slower linear auction, and I’m again met with the same thought here.

This would seem to produce a more agreeable price curve across a broader range of price/time-based objectives. But it would increase implementation complexity.

Ultimately, we can try to tune the exponential to capture all these objectives at once, but the market behaves differently on the two ends of this auction curve. So I think we should talk through why we do or do not like piecewise strategies.

I just don’t see the advantage of a piecewise solution. It seems to me that an exponential curve - shifted a little on the Y axis for convenience - solves our problems elegantly and with little extra complexity.

1 Like

Just wanted to thank you for the well-written elaboration on your post :slight_smile:
I love to see technical posts, let alone ones that solve problems elegantly, thanks again to you and to @nick.eth for the great work.

5 Likes

Is there any advantage of pricing this in dollars? Since it’s a Dutch auction anyway, feels that the currency is irrelevant and we could drop the need for an oracle by using ETH (or even ENS?) directly.

Only disadvantage I see is if the price movement of the underlying token is stronger than the dutch auction: ie, if ether doubles in price in a day, the effective dutch auction price would be the same. But since that’s unpredictable, then the user should not account for that and simply buy when it reaches their target eth price (seems most ens names are price in eth anyway)

A 21 day premium termination seems like a good tweak. I can’t think of any drawbacks to it actually.

My only suggestion might be for the UI to easily convey the price decay/dutch auction mechanics. Maybe mouse-over pricing of the decay line. Maybe Day 1 - 21 numbering on x axis.

But I guess UI tweaks can be worked out later. Just wanted to put that out there because the concept of dutch auction, premium starts, decay rates is foreign to many users. I would like to see the tools they use to interact with ENS App as easy as possible, including buying/planning to buy expired names. I think this is same spirit behind new UI design coming out.

The current page already shows in both USD and ETH regardless of being premium period and I think it’s better to keep showing USD premium amount (as well as ETH) so that it’s easier to see how much premium it currently has relatively to the base price (which is $5~ denominated in USD)

Showing in $ENS only makes sense to me if you are proposing to charge in $ENS which is a whole separate discussion topic discussed at ENS Registration Fees.

Interesting thought - but I think that given that we’re charging for duration in USD, it’s less confusing to use it for the premium too.

1 Like

I think this solution is probably the best for UX.

Someone suggested trying out https://github.com/paulrberg/prb-math for the fixed point maths. The library works great, but adds around 2k gas. After discussing with Nick, I think we won’t use it as it adds complexity for more gas. However it’s a possibility to use if we decide to change the curve at all.

In terms of piecewise, I also think it doesn’t make sense unless the benefits are much better to avoid any unnecessary complexity. I think Nick’s idea of chopping off $50 actually makes the auction UX much better on the tail end.

Well we can still display it in the frontend in dollar terms. The main advantage would be to simplify the contract, make it more gas efficient and remove the oracle dependency. Would need to calculate how much gas it would save…

1 Like

The reduction in oracle dependency does seem like a decent trade-off.

I’m mulling over the idea of taking the premium in $ENS - it would take another approval (making it less gas efficient), but probably isn’t such a big deal for people paying premiums for names.

We’re already taking ETH, just in USD terms, so to adjust the curve to ether we would need to make the curve end closer to 0.001 given the amount ETH is worth, rather than end the curve at $0.50 to $1. Same thing with taking $ENS token, the price curve would need to stop around 0.1-0.2 tokens at current price, and if the price went up or down, it may need to be readjusted. USD oracle version has the benefit of ‘auto-adjusting’ for us (unless our pricing curve is just completely off)

The other consideration is, we will still be using an oracle for renewals/registrations anyway, so we are still dependent on the oracle for basic registrations, since we want to keep those at a reasonable USD price. I can’t see a way around not having an oracle for registration/renewal fees

1 Like