v1-contracts
v1-contracts copied to clipboard
Market limit orders for Uniswap
Reasoning & Overview
I'm proposing market limit orders that would allow users to purchase all the tokens up to a certain price point. This can be implemented as a wrapper contract for V1 and might make sense to be a core function for V2.
Currently, Uniswap lets users trade based on volume and enforce minimum returns on the volume of their trade. This trade type would be the counterpart and have the volume dynamically set based on the price Uniswap is offering to the user.
Implementation Without Fees
Eth to Token Market Limit:
ethToTokenSwapLimit(price_limit: uint256) -> uint256:
price_accuracy: uint256 = 10**9 #9 decimals of price precision
start_input_reserve: uint256 = self.balance
start_output_reserve: uint256 = self.token.balanceOf(self)
invariant: uint256 = start_input_reserve * start_output_reserve
target_input_reserve: uint256 = sqrt(invariant * target_price / price_accuracy)
trade_volume: uint256 = target_input_reserve - start_input_reserve
self.ethToTokenInput(trade_volume, 1, -1, msg.sender, msg.sender)
This method will purchase all the available tokens such that the cost of the last wei of token is <= price_limit
.
Dealing with Uniswap's Fees
This works perfectly fine before you introduce fees to the system. Due to the nature of how Uniswap's pricing mechanism works, I am having a hard time predicting token cost with the introduction of fees. For example, simply setting price_limit
to price_limit * 997/1000
is too conservative a strategy, resulting in the cost of the last wei of token being well under the price_limit
.
It is my hope that someone smarter than me will be able to make this formula work with fees in the way Uniswap has implemented them.
I think this is a very good idea (and one that I believe Hayden's thought about as well).
~I believe this is the formula you're looking for (incorporating fees):~ EDIT: this formula was incorrect; see corrected one below.
![Deriving Formula](https://user-images.githubusercontent.com/1187252/59171959-5e4be500-8afa-11e9-9655-373ac95b0a83.png)
~It is admittedly quite gnarly. I suspect it might be simpler if fees were charged on the way out rather than the way in, although I haven't done the math yet.~
Hmm, what are your thoughts on having the user do this maths off chain then send a trade on chain with the result and some checks on the state of the market?
You can already do that by doing the calculation and placing a normal market order that fails if it gets worse execution than expected. The usefulness of adding this function, I would think, is its different way of handling the race condition when other transactions have changed the price between when the user submitted the tx and when it is executed on chain.
To be clear, the computation is inelegant but it’s not burdensome—it could probably be done in only a few hundred gas more than your computation.
Well, without fees you can pass the sqrt of the invariant to the contract and save a good portion of the gas costs. That said, I don't think the same approach would work as well when fees get involved due to the ever growing invariant. That said, you could have the trade fallback on calculating the sqrt if a trade occurs before yours.
I'm interested in your thoughts on front-running this type of transaction. I imagine that frontrunners would be incentivized to buy the first half of your order before you, then you purchase the second half of your order, then they sell into the new price you created.
This would mean in the worst case, these types of orders return slightly more than 50% of their "true" volume. Does this seem accurate?
I'm interested in your thoughts on front-running this type of transaction. I imagine that frontrunners would be incentivized to buy the first half of your order before you, then you purchase the second half of your order, then they sell into the new price you created.
I believe this is correct (ignoring fees) if there is only one front-runner and Uniswap is the only way they can buy or sell the asset.
- The result changes if there are multiple front-runners. Assuming (unrealistically) that they queue up rather than competing in a gas auction, the first would trade the asset halfway, the second would push it half of the rest of the way (so to 75% of the original order), and so on in accordance with Zeno's paradox.
- The optimal strategy also changes if the front-runner is able to combine the front-running with some arbitrage. If, for example, the front-runner can buy and sell arbitrary amounts of the token on Coinbase at the starting price, then they should let your entire buy order execute, buy that same quantity on Coinbase, and sell the full amount back on Uniswap (pushing the price back to the starting price).
This would mean in the worst case, these types of orders return slightly more than 50% of their "true" volume. Does this seem accurate?
If you have fees, then yes, I think that if someone optimally front-runs you (and the caveats above don't apply), a little more than half of your order will execute (although you will receive less than half of what you would have received, because the half that does execute is executing at a worse price).
Good news: the formula I derived above was wrong, and the true formula is much simpler! (The error is that I was assuming the marginal price of adding to the trade was the same as the marginal price at the start of the next trade—it actually isn't).
Here's the actual formula:
![Screen Shot 2019-06-11 at 2 05 35 PM](https://user-images.githubusercontent.com/1187252/59306837-07592380-8c52-11e9-8e5b-9fb02c35aec2.png)
So it's only two additional divisions by 997/1000 (so multiplications by 1000/997).
I implemented this and tested i with variable fees, it appears holds true for all fees < 49.99%. I will probably implement a wrapper contract for Uniswap and do some tests relating to gas cost. Will also experiment with computing the sqrt off-chain. Thanks a tonne for your help!