Factory Pattern / CREATE2 とUniswapでの応用例をみる

今年最も話題になったDeFiの一角であるUniswapを参考にしながら前半ではFactoryパターンの概要について，後半ではCREATE2についてまとめます.

### Factory Patternとは
あるFactoryコントラクトによって，形式化された別のコントラクトを生成するパターン.必要に応じて新しいインスタンスを生成することができる．

## そもそもUniswapって＋実装のサマリ-
UniswapはDeFiの一角で,ERC20トークンの簡易交換所です．

Uniswapではある2種類のトークンで1ペアのトークンペアごとにPoolが存在します.
そのプールを作るのがUniswapV2Factoryコントラクトで,あるトークンペアのプールがまだ存在してない場合，このUniswapV2FactoryコントラクトがプールであるUniswapV2Pairコントラクトのインスタンスを新たに生成します．当然，生成されたPool(Pair)はほとんど共通した機能をもちます． Uniswapでは生成するpoolのアドレスをCREATE2で計算していて，トークンペアからデプロイされるアドレスがon-chainにアクセスしなくても予め分かります．

## CREATE2とは
CREATE2はEIP-1014で提案されたもので，新しいコントラクトアドレスの計算方法です．
**事前に互いが合意した，デプロイされるコントラクトなどの値からon-chainに記録される前のaddressが事前に分かることは色々利用できると言われています．**

### CREATE
普通にCREATEオペコードで新しいコントラクトを作る場合に計算されるアドレスは，senderのアドレスとそのアドレスのナンスのkeccake256ハッシュから生成されます．

`new_address = hash(sender, nonce)`

ナンスはトランザクションを起こすごとにシーケンスに増えていく自然数で，この方法で作られるコントラクトのアドレスの特徴はsenderが分かっていても，**nonceは変わりうるので，新しいアドレスを予め予想するのは難しいということです.**
ちなみに,ここでいうナンスはトランザクションナンスです

###CREATE2
別の作り方にCREATE2オペコードを使うやり方があります．さっきと違うのは作られるコントラクトのアドレスの作り方です

生成方法
 - 0xFF :CREATEで作るアドレスと衝突するのを防ぐための定数
 - sender:senderのアドレス
 - salt :senderが決められる任意の値
 - bytecode: デプロイするコントラクトのバイトコード

`new_address = hash(0xFF, sender, salt, bytecode)`

で計算される．この方法で作る場合，**デプロイする人のaddressとコントラクトは知ってる別の人はsaltを予め示しあわせておけば，デプロイされるアドレスが予測できます．** さらに，**デプロイする人がコントラクトを改変してデプロイしようとする場合，addressは別のものとなるので，デプロイされるコントラクトは予め知っているものと同じものであることが保証されます.**

## CREATE2を使ってコントラクト作る
solidity v0.6以前は,インラインアセンブリを用いると使えます
以下のUniswapV2Factoryは抜粋で不完全だが,参考までに

 - senderは`UniswapV2Factory`コントラクト
 - `type(UniswapV2Pair).creationCode`でUniswapV2Pairのバイトコードを取得している．            
 - saltは プールのトークンペアのaddressのハッシュ

```solidity

 bytes memory bytecode = type(UniswapV2Pair).creationCode;
 bytes32 salt = keccak256(abi.encodePacked(token0, token1));
 assembly {
       pair := create2(0, add(bytecode, 32), mload(bytecode), salt)
 }
```

solidity v0.6からは，`new`演算子と saltを指定すれば容易に実装できる

```solidity

 bytes32 salt = keccak256(abi.encodePacked(token0, token1));
 pair = new UniswapV2Pair{salt:salt}();
```

Uniswapでは生成するpoolのアドレスをCREATE2で計算していて，トークンペアとfactoryのアドレスを知っていればデプロイされるアドレスがon-chainにアクセスしなくても予め分かります

## CREATE2で作られたコントラクトのアドレスを計算する
逆にCREATE2でくられたコントラクトアドレスをsolidityで計算するときは，CREATE2の式に従って計算すればよくて，Uniswapではライブラリー`UniswapV2Library `にそのための関数が用意されています．

```solidity:UniswapV2Library.sol
    function pairFor(address factory, address tokenA, address tokenB)
        internal
        pure
        returns (address pair)
    {
        (address token0, address token1) = sortTokens(tokenA, tokenB);
        pair = address(
            uint256(
                keccak256(
                    abi.encodePacked(
                        hex"ff",
                        factory,
                        keccak256(abi.encodePacked(token0,token1)),
                        hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f'
                    )
                )
            )
        );
    }
```

initCodeHash部分である`hex'96e...5f'`は
`keccak256(abi.encodePacked(type(UniswapV2Pair).creationCode))`
と等価です.

参考
https://docs.openzeppelin.com/cli/2.8/deploying-with-create2
https://eips.ethereum.org/EIPS/eip-1014
https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2Factory.sol