Uniswap v1#
Uniswap v1 is the first version, with relatively simple functionality and logic. It only supports trading pairs between ETH and ERC20 tokens, with a fixed fee of 3%.
The basic functionality implemented by Uniswap v1 is the constant product market maker system, where the product of the number of ETH and ERC20 tokens in a single trading pair remains constant. In the AMM mechanism, each trader's counterparty is the trading pool itself.
The code of Uniswap v1 is divided into two parts: factory and exchange. The following explains the implementation mechanism and principles of each part.
Factory#
The factory contract is used to maintain all exchange trading pairs. It does not have core trading logic, but includes the following core methods:
initializeFactory
: Used to set the exchangeTemplate contract address during creation, which will be used for all exchange trading pair contracts in the future. It can only be set once.createExchange
: Used to create a trading pair for a token.create_with_code_of
is used to copy the code of the specified contract and deploy a new contract.
In addition, the factory also records the following mapping relationships for easy querying of trading pairs and token information:
tokenCount
: Records the total number of trading pairs created.token_to_exchange
: Records the mapping of tokens to exchange trading pair addresses.exchange_to_token
: Records the mapping of exchange trading pair addresses to tokens.id_to_token
: Records the mapping of trading pair IDs to tokens.
@public
def initializeFactory(template: address):
assert self.exchangeTemplate == ZERO_ADDRESS
assert template != ZERO_ADDRESS
self.exchangeTemplate = template
@public
def createExchange(token: address) -> address:
assert token != ZERO_ADDRESS
assert self.exchangeTemplate != ZERO_ADDRESS
assert self.token_to_exchange[token] == ZERO_ADDRESS
exchange: address = create_with_code_of(self.exchangeTemplate)
Exchange(exchange).setup(token)
self.token_to_exchange[token] = exchange
self.exchange_to_token[exchange] = token
token_id: uint256 = self.tokenCount + 1
self.tokenCount = token_id
self.id_to_token[token_id] = token
log.NewExchange(token, exchange)
return exchange
Exchange#
Interface#
The core content of each trading pair is in the exchange contract. The core methods are shown in the following interface, which can be mainly divided into the following parts:
- Liquidity management: Adding or removing liquidity, mainly using the object "lp".
- Price query:
- Calculate how much ETH can be obtained by selling a certain amount of tokens.
- Calculate how many tokens can be obtained by selling a certain amount of ETH.
- Calculate how much ETH is needed to buy a certain amount of tokens.
- Calculate how many tokens can be bought by a certain amount of ETH.
- Providing ETH to exchange for tokens: Similar to the price query above, there are four trading methods.
- Providing tokens to exchange for ETH: Similar to the price query above, there are four trading methods.
- Token-to-token exchange: Similar to the price query above, there are four trading methods.
interface UniswapExchangeInterface {
// Liquidity
function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256);
function removeLiquidity(uint256 amount, uint256 min_eth, uint256 min_tokens, uint256 deadline) external returns (uint256, uint256);
// Price query
function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought);
function getEthToTokenOutputPrice(uint256 tokens_bought) external view returns (uint256 eth_sold);
function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 eth_bought);
function getTokenToEthOutputPrice(uint256 eth_bought) external view returns (uint256 tokens_sold);
// Providing ETH to exchange for tokens
function ethToTokenSwapInput(uint256 min_tokens, uint256 deadline) external payable returns (uint256 tokens_bought);
function ethToTokenTransferInput(uint256 min_tokens, uint256 deadline, address recipient) external payable returns (uint256 tokens_bought);
function ethToTokenSwapOutput(uint256 tokens_bought, uint256 deadline) external payable returns (uint256 eth_sold);
function ethToTokenTransferOutput(uint256 tokens_bought, uint256 deadline, address recipient) external payable returns (uint256 eth_sold);
// Providing tokens to exchange for ETH
function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline) external returns (uint256 eth_bought);
function tokenToEthTransferInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline, address recipient) external returns (uint256 eth_bought);
function tokenToEthSwapOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline) external returns (uint256 tokens_sold);
function tokenToEthTransferOutput(uint256 eth_bought, uint256 max_tokens, uint256 deadline, address recipient) external returns (uint256 tokens_sold);
// Token-to-token exchange
function tokenToTokenSwapInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address token_addr) external returns (uint256 tokens_bought);
function tokenToTokenTransferInput(uint256 tokens_sold, uint256 min_tokens_bought, uint256 min_eth_bought, uint256 deadline, address recipient, address token_addr) external returns (uint256 tokens_bought);
function tokenToTokenSwapOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address token_addr) external returns (uint256 tokens_sold);
function tokenToTokenTransferOutput(uint256 tokens_bought, uint256 max_tokens_sold, uint256 max_eth_sold, uint256 deadline, address recipient, address token_addr) external returns (uint256 tokens_sold);
}
Core Process#
Although there are multiple types of trades involved in Uniswap, it may seem complex, but the core process of Uniswap v1 can be represented by these two diagrams.
As two roles using Uniswap, LP is responsible for adding and moving liquidity, while players are responsible for swapping in the trading pair.
- As an LP, whether adding or removing liquidity, it needs to be done proportionally.
- As a player, swapping in the trading pair needs to follow the constant product agreement.
Uniswap provides many sets of methods, and their names follow a certain rule: A-To-B-swap/transfer-input/output.
- A and B represent the token and ETH combination.
- swap/transfer: Choose one of the two types of transactions.
- input/output: Choose one of the two transaction directions.
For token-ETH input-output, there are four combinations of price query methods:
// Price query
function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought);
function getEthToTokenOutputPrice(uint256 tokens_bought) external view returns (uint256 eth_sold);
function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 eth_bought);
function getTokenToEthOutputPrice(uint256 eth_bought) external view returns (uint256 tokens_sold);