In Uniswap, there is a separate exchange contract for each ERC20 token. These exchanges hold reserves of both ETH and their associated ERC20. Instead of waiting to be matched in an order-book, users can make trades against the reserves at any time. Reserves are pooled between a decentralized network of liquidity providers who collect fees on every trade.
Pricing is automatic, based on the x * y = k
market making formula which automatically adjusts prices based off the relative sizes of the two reserves and the size of the incoming trade. Since all tokens share ETH as a common pair, it is used as an intermediary asset for direct trading between any ERC20 ⇄ ERC20 pair.
The variables needed to determine price when trading between ETH and ERC20 tokens is:
For sell orders (exact input), the amount bought (output) is calculated:
// Sell ETH for ERC20const inputAmount = userInputEthValueconst inputReserve = web3.eth.getBalance(exchangeAddress)const outputReserve = tokenContract.methods.balanceOf(exchangeAddress).call()// Sell ERC20 for ETHconst inputAmount = userInputTokenValueconst inputReserve = tokenContract.methods.balanceOf(exchangeAddress).call()const outputReserve = web3.eth.getBalance(exchangeAddress)// Output amount boughtconst numerator = inputAmount * outputReserve * 997const denominator = inputReserve * 1000 + inputAmount * 997const outputAmount = numerator / denominator
For buy orders (exact output), the cost (input) is calculated:
// Buy ERC20 with ETHconst outputAmount = userInputTokenValueconst inputReserve = web3.eth.getBalance(exchangeAddress)const outputReserve = tokenContract.methods.balanceOf(exchangeAddress).call()// Buy ETH with ERC20const outputAmount = userInputEthValueconst inputReserve = tokenContract.methods.balanceOf(exchangeAddress).call()const outputReserve = web3.eth.getBalance(exchangeAddress)// Costconst numerator = outputAmount * inputReserve * 1000const denominator = (outputReserve - outputAmount) * 997const inputAmount = numerator / denominator + 1
There is a 0.3% liquidity provider fee built into the price formula. This can be calculated:
fee = inputAmount * 0.003
The exchange rate is simply the output amount divided by the input amount.
const rate = outputAmount / inputAmount
The variables needed to determine price when trading between two ERC20 tokens is:
For sell orders (exact input), the amount bought (output) is calculated:
// TokenA (ERC20) to ETH conversionconst inputAmountA = userInputTokenAValueconst inputReserveA = tokenContractA.methods.balanceOf(exchangeAddressA).call()const outputReserveA = web3.eth.getBalance(exchangeAddressA)const numeratorA = inputAmountA * outputReserveA * 997const denominatorA = inputReserveA * 1000 + inputAmountA * 997const outputAmountA = numeratorA / denominatorA// ETH to TokenB conversionconst inputAmountB = outputAmountAconst inputReserveB = web3.eth.getBalance(exchangeAddressB)const outputReserveB = tokenContract.methods.balanceOf(exchangeAddressB).call()const numeratorB = inputAmountB * outputReserveB * 997const denominatorB = inputReserveB * 1000 + inputAmountB * 997const outputAmountB = numeratorB / denominatorB
For buy orders (exact output), the cost (input) is calculated:
// Buy TokenB with ETHconst outputAmountB = userInputEthValueconst inputReserveB = web3.eth.getBalance(exchangeAddressB)const outputReserveB = tokenContractB.methods.balanceOf(exchangeAddressB).call()// Costconst numeratorB = outputAmountB * inputReserveB * 1000const denominatorB = (outputReserveB - outputAmountB) * 997const inputAmountB = numeratorB / denominatorB + 1// Buy ETH with TokenAconst outputAmountA = userInputEthValueconst inputReserveA = tokenContractA.methods.balanceOf(exchangeAddressA).call()const outputReserveA = web3.eth.getBalance(exchangeAddressA)// Costconst numeratorA = outputAmountA * inputReserveA * 1000const denominatorA = (outputReserveA - outputAmountA) * 997const inputAmountA = numeratorA / denominatorA + 1
There is a 0.30% liquidity provider fee to swap from TokenA to ETH on the input exchange. There is another 0.3% liquidity provider fee to swap the remaining ETH to TokenB.
const exchangeAFee = inputAmountA * 0.003const exchangeBFee = inputAmountB * 0.003
Since users only inputs Token A, it can be represented to them as:
const combinedFee = inputAmountA * 0.00591
The exchange rate is simply the output amount divided by the input amount.
const rate = outputAmountB / inputAmountA
Many Uniswap functions include a transaction deadline
that sets a time after which a transaction can no longer be executed. This limits miners holding signed transactions for extended durations and executing them based off market movements. It also reduces uncertainty around transactions that take a long time to execute due to issues with gas price.
Deadlines are calculated by adding the desired amount of time (in seconds) to the latest Ethereum block timestamp.
web3.eth.getBlock('latest', (error, block) => {deadline = block.timestamp + 300 // transaction expires in 300 seconds (5 minutes)})
Uniswap allows traders to swap tokens and transfer the output to a new recipient
address. This allows for a type of payment where the payer sends one token and the payee receives another.
Coming soon…
Coming soon…
Coming soon…