Uniswap v1#
uniswap v1 は最初のバージョンで、実装された機能とロジックは比較的シンプルです。eth-erc20 の取引ペアのみをサポートし、固定手数料は 3% です。
uniswap v1 が実現した最も基本的な機能は、恒定積のマーケットメイキングシステムであり、単一の取引ペア内の eth と erc20 トークンの数の積が全体として一定に保たれます。AMM メカニズムでは、各トレーダーの対戦相手は取引プール自体です。
uniswap v1 のコードは factory と exchange の 2 つの部分に分かれており、以下でそれぞれのコードに対してその実装メカニズムと原理を説明します。
factory#
ファクトリーコントラクトは、すべての exchange 取引ペアを維持するために使用され、コアの取引ロジックはありません。コアのメソッドは以下の 2 つです:
initializeFactory
は、作成時に exchangeTemplate コントラクトアドレスを設定し、以降のすべての exchange 取引ペアコントラクトに使用されます。設定は一度だけ許可されます。createExchange
は、トークンの取引ペアを作成するために使用され、create_with_code_of は指定されたコントラクトのコードをコピーして新しいコントラクトをデプロイするために使用されます。
また、factory では以下のマッピング関係も記録されており、取引ペアとトークン情報の照会が容易になります。
- tokenCount:現在作成された取引ペアの総数を記録
- token_to_exchange:トークンから exchange 取引ペアアドレスへのマッピングを記録
- exchange_to_token:exchange 取引ペアアドレスからトークンへのマッピングを記録
- id_to_token:取引ペア id からトークンへのマッピングを記録
@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#
インターフェース#
各取引ペアの取引の核心内容は exchange コントラクトにあり、コアメソッドは以下のインターフェースに示されています。主に以下の部分に分かれます:
- 流動性管理:流動性の追加または削除、主に使用されるオブジェクトは lp
- 価格照会:
- 一定の額のトークンを売却することで得られる eth の計算
- 一定の額の eth を売却することで得られるトークンの計算
- 一定の額のトークンを購入することで得られる eth の計算
- 一定の額の eth を購入することで得られるトークンの計算
- ETH をトークンに交換する提供:上記の価格照会と同様に、4 つの取引方法があります。
- トークンを ETH に交換する提供:上記の価格照会と同様に、4 つの取引方法があります。
- トークン間の交換:上記の価格照会と同様に、4 つの取引方法があります。
interface UniswapExchangeInterface {
// 流動性
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);
// 価格照会
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);
// ETHを提供してトークンを交換
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);
// トークンを提供して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);
// トークン間の相互交換
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);
}
コアプロセス#
uniswap で関与する取引タイプや取引方向が多いため、少し複雑に見えますが、uniswap v1 のコアプロセスはこの 2 つの図で表現できます。
uniswap を使用する 2 つの役割として、lp は流動性を追加および移動する役割を担い、player は取引ペア内でスワップを行います。
- lp としては、流動性を追加または削除する際には比率に従う必要があります。
- player としては、取引ペア内でスワップする際には恒定積の規約に従って入出を行う必要があります。
uniswap が提供するメソッドは多くのグループがあり、命名にはルールがあります:A-To-B-swap/transfer-input/output
- A と B はトークンと eth の組み合わせです。
- swap transfer:取引タイプは二者択一です。
- input output:取引方向は二者択一です。
トークン交換#
uniswap v1 の取引ペアはすべて token-eth で構成されていますが、tokenA-tokenB の交換も提供されています。
実装方法は、2 つのプール間で eth を中間ブリッジとして使用して 2 回のスワップを完了させます。
価格換算#
スワップ時に約定された恒定積に基づいて、価格照会には 2 つの基本的な方法があります:
getInputPrice
入力に基づいて出力を計算getOutputPrice
出力に基づいて入力を計算
これに基づいて、以下のような方法が派生しました。形式は:A-To-B-Input/Output です。
- A は積極的に取引を行うトークンであり、A-input:つまり、A を購入することで得られる B の計算、A-output:つまり、A を売却することで得られる B の計算です。
したがって、token-eth の input-output の組み合わせから 4 つの価格照会方法が生成されます:
// 価格照会
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);