Loading...
In this article, we will know how Uniswap V1 determines the price of a token in-depth, along with its code implementation.
The price of a token in Uniswap is determined by a formula
x * y = k
where;
x = token reserve or number of tokens present in the exchange
y = ethers reserve or number of ethers present in the exchange
This equation suggests that if x increases y decreases and vice versa.
Here we can see that at some moment we will meet a point where we can drain the entire exchange either leaving the ethers only into the exchange or leaving the tokens only into the exchange
so to address this issue
let's rethink upon the equation
x * y = k -------> (i)
As the equation suggests that if x increases y decreases and vice versa.
Case 1: if x increases y decreases ( Tokens increases Ethers decreases in exchange)
let's say x increased by Δx and y decreased by Δy
so the equation becomes
(x + Δx) * (y - Δy) = k
=> (x + Δx) * (y - Δy) = xy from eq.(i) => Δy = (yΔx) / (x+Δx) -------> (ii)
where;
x = token reserve or number of tokens present in the exchange
Δx = amount of tokens sold into the exchange (which increases the token amount in the exchange)
y = ethers reserve or number of ethers present in the exchange
Δy = amount of ethers got from the exchange (which decreases the ether amount in the exchange)
Case 2: if x decreases y increases ( Tokens decreases Ethers increases in exchange)
similar can be achieved when
y increased by Δy and x decreased by Δx
Δx = (xΔy) / (y+Δy) -------> (iii)
where;
x = token reserve or number of tokens present in the exchange
Δx = amount of tokens got from the exchange (which decreases the token amount in the exchange)
y = ethers reserve or number of ethers present in the exchange
Δy = amount of ethers sold into the exchange (which increases the ether amount in the exchange)
so as a generalised formula we can say by observing eq. (ii) and (iii) that
output amount = (output reserve)(input amount) / (input reserve + input amount) ---->(iv)
Hyperbola never crosses x or y, thus neither of the reserves is ever 0. This makes reserves infinite!
This will be the equation that is being used in uniswap v1 smart contract.
Uniswap v1 has 2 smart contract one is factory contract (that creates and tracks all the exchanges) other one is exchange smart contract that implements the above logic.
function getAmount(
uint256 inputAmount,
uint256 inputReserve,
uint256 outputReserve
) private pure returns (uint256) {
require(inputReserve > 0 && outputReserve > 0, "invalid reserves");
return (inputAmount * outputReserve) / (inputReserve + inputAmount);
}
for case 1, eq. (ii)
function getTokenAmount(uint256 _ethSold) public view returns (uint256) {
require(_ethSold > 0, "ethSold is too small");
uint256 tokenReserve = getReserve();
return getAmount(_ethSold, address(this).balance, tokenReserve);
}
for case 2, eq. (iii)
function getEthAmount(uint256 _tokenSold) public view returns (uint256) {
require(_tokenSold > 0, "tokenSold is too small");
uint256 tokenReserve = getReserve();
return getAmount(_tokenSold, tokenReserve, address(this).balance);
}
Here in the graph the price function causes price slippage. The bigger the amount of tokens traded in relative to reserves, the higher the price would be.
Pricing in Uniswap is determined as follows
Px = y / x = price of token
Py = x / y = price of ethers
function getPrice(uint256 inputReserve, uint256 outputReserve)
public
pure
returns (uint256)
{
require(inputReserve > 0 && outputReserve > 0, "invalid reserves");
return (inputReserve * 1000) / outputReserve;
}
Let's say we have input reserve = 1000 and output reserve = 2000, the resulting price would be 0.5. But in solidity it will display 0. We avoid this issue by increasing the precision to 1000.
let's have a function that returns token balance of an exchange:
function getReserve() public view returns (uint256) {
return IERC20(tokenAddress).balanceOf(address(this));
}
Let's now implement swap functionality
Case 1: for ether to token swap
function ethToTokenSwap(uint256 _minTokens) public payable {
uint256 tokenReserve = getReserve();
uint256 tokensBought = getAmount(
msg.value,
address(this).balance - msg.value,
tokenReserve
);
require(tokensBought >= _minTokens, "insufficient output amount");
IERC20(tokenAddress).transfer(msg.sender, tokensBought);
}
`tokensBought` will provide the amount of tokens we would get when ethers provided by making the function itself `payable` to the exchange
we keep `_minTokens` to avoid slippage tolerance
Case 2: for token to eth swap
function tokenToEthSwap(uint256 _tokensSold, uint256 _minEth) public {
uint256 tokenReserve = getReserve();
uint256 ethBought = getAmount(
_tokensSold,
tokenReserve,
address(this).balance
);
require(ethBought >= _minEth, "insufficient output amount");
IERC20(tokenAddress).transferFrom(msg.sender, address(this), _tokensSold);
payable(msg.sender).transfer(ethBought);
}
The function basically transfers `_tokensSold` of tokens to the exchange and sends `ethBought` of ethers from the exchange to the user.
We will be diving deep into Uniswap further.
Stay tuned...........
References:-