Solidityの細かい仕様まで学習するためのリポジトリ
Functions can be declared as
- public - any contract and account can call
- private - only inside the contract that defines the function
- internal- only inside contract that inherits an internal function
- external - only other contracts and accounts can call
How to receive Ether? A contract receiving Ether must have at least one of the functions below
- receive() external payable
- fallback() external payable receive() is called if msg.data is empty, otherwise fallback() is called.
receive()はトランザクションのインプットデータが空の時のもの、つまり純粋な送金処理時に呼ばれる。それ以外の 場合にはfallback()が呼び出される。
delegatecall is a low level function similar to call.
When contract A executes delegatecall to contract B, B's code is executed
with contract A's storage, msg.sender and msg.value.
solidiyでは32byteを1つの区切りとして認識する。
引数にした変数の長さを求めるメソッド
solidityでinline assemblyを使うメリットとして以下が挙げられると思います。
- コンパイラの制約を無視した実装ができる
- ガス代が浮く
- inline assemblyでしかできない操作を実現できる
Contracts can be deleted from the blockchain by calling selfdestruct.
selfdestruct sends all remaining Ether stored in the contract to a designated address.
solidityでは、32バイトのデータを1スロットとして考える。
Delegatecallとは、外部コントラクトへの呼び出しに対して呼び出し元のコントラクトの文脈で処理する関数です。ここで言う「コントラクトの文脈」とは、msg.senderやmsg.value、コントラクトのストレージのことなどを指しています。
delegatecallの場合は外部コントラクトの関数を自身のコントラクトのストレージ文脈で処理することが可能になります。
- delegatecall preserves context (storage, caller, etc...)
- s2torage layout must be the same for the contract calling delegatecall and the contract getting called
blockhashとblock.timestampはランダム性を確保するための信頼できるソースではありません。
msg.senderには、EOAとコントラクトアドレスの2種類が入り得る。tx.originにはEOAしか入らない。
例えば、アカウントAからコントラクトBを呼び出し、コントラクトBから別のコントラクトCを呼び出したとき、 コントラクトC内でmsg.senderはコントラクトBを指し、tx.originはアカウントAを指す。
tx.originを使用すると想定とは異なるアドレスが入ってくる可能性(本当はコントラクトのアドレスである必要がある場合)があり、セキュリティ的にリスクがあるため、tx.originの使用は控えた方が良いことになっている。
コミットメント方式を使うことでトランザクションの中身を秘密にしたまた送信することができる。
block.timestampについては、任意に操作できてしまう可能性があるので乱数などの用途には使わないこと。
強力なマイナーであれば、未来のblock.timestampからスマコンのロジックを読み解いて逆算される可能性があるため。(The Mergeでも発生する??)
署名ロジックをうまく活用すればメタトランザクションなどの応用に利用できるが、注意しないと脆弱性を突かれる可能性があるのでよく考えて設計する必要がある。
同じ署名を複数回使用して関数を実行することができるので、署名者の意図が一度の取引を承認することであった場合、有害である可能性が出てくる。
なので一回きりの署名であることを証明するためにナンスを含める必要がある。ナンスは、通常のトランザクションをsubmitする際に取得できるのでその値を埋め込むこと!
// コントラクトのアドレス、送信先、総金額、ナンスを含めて署名データを生成するようにする。
keccak256(abi.encodePacked(address(this), _to, _amount, _nonce));
DeFiプロトコルで使用される資金用の保管庫のこと。
ユーザーが入金すると、ある程度の量のシェアがミントされる。DeFiプロトコルは、ユーザーの預金を利用して(何らかの形で)利回りを発生させる。ユーザーは、自分のトークン+利回りを引き出す。
Constant product AMM XY = K
ユーザーのトランザクション送信をUpgradableコントラクトへ届ける役割のコントラクトのこと。つまりdelegatecall関数を実行するコントラクトとなる。Proxyコントラクトにはfallback関数にdelegate関数を実装している。この仕組みを上手く利用することでProxyコントラクトに記述されていない関数はfallback関数に定義されている処理を実行する形となる。
実装例は下記の通り。
delegatecallの引数の内容は下記の通り
- gas:この呼び出しのために与えるgas量です。gas opcodeは処理のために利用可能なgas量を返します。
- _impl:delegatecall先のコントラクトアドレス
- ptr:上で定義したメモリポインタ。calldataを適用するため。
- calldatasize:msg.data.lengthと同じのデータサイズ
- 0:delegatecall先コントラクトの関数から返される出力データ。現時点ではこのデータは分からないので使わない。
- 0:出力データのサイズ。これ以降、returndetasize opcodeで使用することができる。
function () payable public {
address _impl = implementation();
require(_impl != address(0));
assembly {
let ptr := mload(0x40) // フリーメモリポインタ
calldatacopy(ptr, 0, calldatasize) // calldataをコピーする処理 (msg.data.lengthと同じです)
let result := delegatecall(gas, _impl, ptr, calldatasize, 0, 0) // delegatecall opcodeの操作
let size := returndatasize
returndatacopy(ptr, 0, size)
switch result // 成功したら1 失敗したら0が格納される
case 0 { revert(ptr, size) }
default { return(ptr, size) }
}
}
Dexにおける流動性提供をしたときに取得できる LP-Token を Stake することで得られる収益を計算するためのスマートコントラクトのこと。
ガス代を安くする上で使えるのがunchecked
である。
コントラクトを壊すメソッド。コントラクトを破棄した呼び出し元にコントラクトが所持していたEtherを全て送金することができる。
EVM(Ethereum Virtual Machine)でコードを実行する際にstack、memory、storage、calldata、returndataの5つのデータ領域がある。 calldataはcallまたはdelegatecallで別のコントラクトを呼び出す時に使用するデータ領域で、calldataはbytes型で表される。
calldataは2つのサブパートに分けることができる。
- メソッドID(4バイト)
- 引数(32バイト)※引数が複数ある場合もある
最終的なcalldataはこの2つを連結させたもの。
メソッドIDは、メソッドシグネチャのkecccak256ハッシュの先頭8文字で表される。 メソッドシグネチャは、メソッドの名前とその引数の型のこと。
例
メソッドの名前 --> transferFrom
引数の型 --> (address, address, uint256)
メソッドシグネチャ --> transferFrom(address, address, uint256)
kecccak256ハッシュの先頭8文字 --> web3.abi.encodeFunctionSignature('transferFrom(address,address,uint256)') --> 0x4a6e9f4e
0x4a6e9f4eがメソッドIDとなる
calldataの引数部分の求め方
例
transferFrom(0x0123456789abcdef01223456789abcdef0123456, 0xabcdef0123456789abcdef0123456789abcdef01, 30)
0x0123456789abcdef01223456789abcdef0123456を32バイトにする --> 0x0000000000000000000000000123456789abcdef01223456789abcdef0123456
0xabcdef0123456789abcdef0123456789abcdef01を32バイトにする --> 0x000000000000000000000000abcdef0123456789abcdef0123456789abcdef01
30を32バイトにする --> 0x000000000000000000000000000000000000000000000000000000000000001d
連結する --> 0x0000000000000000000000000123456789abcdef01223456789abcdef0123456000000000000000000000000abcdef0123456789abcdef0123456789abcdef01000000000000000000000000000000000000000000000000000000000000001d
スリッページとは、お客様の注文レートと実際に約定したレートの差のこと。
- Solidity by Example
- Smart Contract Engineer
- Solidity Inline Assembly で気になったことをまとめる
- Solidityのストレージスロットとパッキングについて
- Gitpod
- ChainIDE
- How do I make my DAPP "Serenity-Proof?"
- 【Solidity】tx.originとmsg.senderの違い
- forge-std
- foundry-rs/foundry
- UniswapV3
- Weird ERC20
- アップグレード可能なスマートコントラクトを実現する具体的なアプローチ
- pancake-farm
- 【UNCHAIN-dev】openzeppelin-deepdive
- Foundry book
- BBB 資料
- How to become a smart contract auditor
- 【sherlock】GMX contest
- 【Secureum】Audit Findings 101
- イーサの送金とリエントランシー攻撃
- Reentrancy | Hack Solidity #1
- 日本円ハッカソン入門ラボ
- [図解] delegatecall callcode call の違い
- solidityのcalldataの求め方
- スマートコントラクトを使った入金システムについて全力で理解してみた
- ERC-20と ERC-721の主な違い
- 第二回コントラクト輪読会レポート〜ERC721のコントラクトを皆で読みながら理解を深めよう〜
- Writing Upgradeable Contracts
- Dex(分散型取引所)の一つであるPancakeSwapを開発面から理解する
- Caviar contest
- Uniswap v2-core
- DefiLlama
- backed protocol
- PuttyV2 protocol
- 【GitHub】Caviar contest
- Caviar contest
- 【Solidity】mapping key が存在しない場合を判定する
- Solidity Assembly入門 ~ 配列について storage/memory ~
- Caviar Demo
- Solidity開発ツール、Foundryの紹介
- AMM型NFTマーケットプレイスの仕組みとは