WIP: SushiSwap のマイグレーション
#SushiSwap #Compound
アドレス
Deployer https://etherscan.io/address/0xf942dba4159cb61f8ad88ca4a83f5204e8f4a6bd
EOA、コントラクトをデプロイするためのアカウントの様子
Migrator https://etherscan.io/address/0x93ac37f13bffcfe181f2ab359cc7f67d9ae5cdfd#code
MasterChef https://etherscan.io/address/0xc2edad668740f1aa35e4d8f227fb8e17dca888cd#code
Timelock https://etherscan.io/address/0x9a8541ddf3a932a9a922b607e9cf7301f1d47bd1#code
SushiBar https://etherscan.io/address/0x8798249c2e607446efb7ad49ec89dd1865ff4272#code
SushiMaker https://etherscan.io/address/0x54844afe358ca98e4d09aae869f25bfe072e1b1a#code
Factory https://etherscan.io/address/0xc0aee478e3658e2610c5f7a4a2e1777ce9e4f2ac#code
OldFactory https://etherscan.io/address/0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f#code
ガバナンスと tx 実行の方法的な話
https://twitter.com/SBF_Alameda/status/1302967576968880130
48H のディレイってどこからきてるんだろう?
Governance 周りは Compound から持ってきてる
GovernorAlpha.sol
Timelock.sol
実際の Timelock contract https://etherscan.io/address/0x9a8541ddf3a932a9a922b607e9cf7301f1d47bd1#code
まず QueueTransaction して https://etherscan.io/tx/0x416a19f54d85de00b5cfcb7f498e61e5867b2a88e981c8396ea3e27ab7388cac#eventlog
だいたい 48 時間後に ExecuteTransaction された https://etherscan.io/tx/0x4ec4477867b40d23223c5dd09be7e847a71a23dfb3a4b5653a0058247288410c#eventlog
Timelock contract の動作
queue した tx が48 時間後に自動的に実行されるわけではなくて、自分で execute の tx を叩く。execute された時に所定のディレイ(この例では 48 時間の秒数表現である 172800 uint256 が delay に設定されている)が経過してなければ失敗する
QueueTransaction のシグネチャが queueTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) となっていて、eta(おそらく estimated time of arrival)を含んでいる。https://github.com/sushiswap/sushiswap/blob/master/contracts/Timelock.sol#L83 で delay だけ eta が現在ブロック時刻より進んでいるかをチェック。引数全てを足し合わせた bytes の hash を bytes32 => bool な mapping に保持する。
ExecuteTransaction のシグネチャも全く同じ executeTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) になっている。https://github.com/sushiswap/sushiswap/blob/master/contracts/Timelock.sol#L104-L105 で queue した tx と全く同じ内容かどうかをチェック。引数全てを足し合わせた bytes の hash を再度計算して mapping から取得できれば確かに queue されているとして後続の処理を実行。GRACE_PERIOD に設定されている 14 days 以上経過すると queue されていても execute できなくなる。tx を実際に実行する部分は https://github.com/sushiswap/sushiswap/blob/master/contracts/Timelock.sol#L120 で、(bool success, bytes memory returnData) = target.call.value(value)(callData); なだけ。callData は https://github.com/sushiswap/sushiswap/blob/master/contracts/Timelock.sol#L111-L117 にあって、string でもらった signature を byte に変換して hash している。length = 0 の時をケアしているが signature なしで実行できるものなのかどうかはよくわからない…? #Solidity_の_abi.encode,_abi.encodeXXX_がよくわからなくなる に関連情報。
address.call.value(value)(payload) は v0.7 で address.call{value: 1 ether}(payload) のように書けるようになった(けど、SushSwap は 0.6.12 固定なので古い書き方)
Uniswap からのマイグレーションの方法
tx の event log からわかること
ExecuteTransaction の signature によれば、setMigrator(address) を呼び出している
https://github.com/sushiswap/sushiswap/blob/master/contracts/Timelock.sol#L24 で event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); と Event が定義されているので、topics[1] が txHash, topics[2] が target であることがわかる。なお、topics[0] は常に Event の signature となる。https://ethereum.stackexchange.com/questions/7835/what-is-topics0-in-event-logs
target の https://etherscan.io/address/0xc2edad668740f1aa35e4d8f227fb8e17dca888cd#code は MasterChef コントラクトで、https://github.com/sushiswap/sushiswap/blob/44e695e77ad1d4514796423f8553281e01b99273/contracts/MasterChef.sol がソースコード。
MasterChef.setMigrator(IMigratorChef _migrator) https://github.com/sushiswap/sushiswap/blob/44e695e77ad1d4514796423f8553281e01b99273/contracts/MasterChef.sol#L131 は、migrator をセットするだけ。
MasterChef.migrate(uint256 _pid)
https://github.com/sushiswap/sushiswap/blob/44e695e77ad1d4514796423f8553281e01b99273/contracts/MasterChef.sol#L136
_pid は PoolInfo[] public poolInfo として管理している array の index
poolInfo はここから確認 https://etherscan.io/address/0xc2edad668740f1aa35e4d8f227fb8e17dca888cd#readContract
input の 0 を例にすると ETH - Tether の pool なことがわかる
poolInfo.lpToken https://etherscan.io/address/0x06da0fd433C1A5d7a4faa01111c044910A184553#readContract
lpToken.token0 => WETH9 https://etherscan.io/address/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code
lpToken.token1 => TetherToken https://etherscan.io/address/0xdac17f958d2ee523a2206206994597c13d831ec7#code
_pid で指定した pool の管理する token contract に入っている 全額を migrator に approve する
migrator.migrate(IUniswapV2Pair orig) を実行。返り値として新しい token contract の address を受け取る。その token contract に approve した全額が移っていることを確認
migrator.migrate()
https://github.com/sushiswap/sushiswap/blob/44e695e77ad1d4514796423f8553281e01b99273/contracts/Migrator.sol#L26
https://github.com/sushiswap/sushiswap/blob/44e695e77ad1d4514796423f8553281e01b99273/contracts/Migrator.sol#L32-L35 で SushiSwap の factory から Pair コントラクトを作成している(Pair コントラクトについては ↓ に)
L33 は基本的に true になる想定なのかな?
Migrator コントラクトに tx がないのは、Timelock コントラクトの ExecuteTransaction でメタトランザクション的に実行されるから internal tx しか残らない。
orig が from, 新しく作成する pair が to な migration
factory.createPair(token0, token1) で Uniswap と同じやり方で Pair == Pool コントラクトを作成する
orig.transferFrom(msg.sender, address(orig), lp);
msg.sender は internal tx では internal tx を投げた contract なので MasterChef。MasterChef から orig に全額を transfer
orig.burn(address(pair));
pair.mint(msg.sender);
IUniswapV2Pair
factory から作成される通貨ペアを表すコントラクト。管理する通貨ペアのトークンコントラクトを token0, token1 として持っていたり、それらでスワップするための swap() やリザーブ数量を取得する getReserve() なんかが用意されている。
v2 factory contract https://etherscan.io/address/0x5c69bee701ef814a2b6a3edd4b1652cb9cc5aa6f
https://uniswap.org/docs/v2/smart-contracts/pair/
https://etherscan.io/address/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852#readContract が実際の Pair コントラクト(ETH - DAI)。https://uniswap.info/pair/0x0d4a11d5eeaac28ec3f61d100daf4d40471f1852 から飛ぶと簡単に探せる。
構造
Factory
LP token
token0
token1
Migrator