Solidity チートシート
コントラクトの構造
すべてのファイルはライセンスと pragma で始まります。コントラクトはデプロイの単位です。
| 構文 | 意味 |
|---|---|
// SPDX-License-Identifier: MIT | コンパイラが 1 行目に期待するライセンスコメント |
pragma solidity ^0.8.0; | このファイルが対象とするコンパイラのバージョン |
contract Counter { ... } | コントラクトを宣言(状態 + 関数) |
constructor(uint start) { count = start; } | デプロイ時に一度だけ実行される |
import "./Token.sol"; | 別のファイルをインポート |
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; | ライブラリパッケージをインポート |
interface IToken { function balanceOf(address a) external view returns (uint); } | インターフェースを宣言(本体なし) |
library Math { function min(uint a, uint b) internal pure returns (uint) { ... } } | 再利用可能な関数のライブラリ |
値型
整数は固定サイズで小数を持ちません。0.8 ではオーバーフロー時にリバートします。
| 型 | 格納する値 |
|---|---|
uint256 / uint | 符号なし整数、0 〜 2^256 - 1(uint は uint256) |
uint8, uint16, ... uint128 | 8 ビット刻みの、より小さい符号なし整数 |
int256 / int | 符号付き整数(負の値も可) |
bool | true または false |
address | 20 バイトのアカウントまたはコントラクトのアドレス |
address payable | transfer/send で Ether を受け取れるアドレス |
bytes32, bytes1... | 固定長のバイト配列 |
enum Status { Open, Closed } | 名前付き定数の集合。uint8 として格納 |
1 ether, 1 gwei, 1 wei | Ether の単位:1 ether = 10^18 wei |
1 days, 2 hours, 30 minutes | 時間の単位(秒換算) |
参照型とデータロケーション
配列、文字列、bytes、struct、mapping は storage、memory、calldata のいずれかに置かれます。
| 構文 | 意味 |
|---|---|
string memory name | 動的な UTF-8 文字列(一時的なコピー) |
bytes memory data | 動的なバイト配列 |
uint[] public scores; | storage 上の動的配列 |
uint[3] fixed; | 要素数 3 の固定長配列 |
uint[] memory tmp = new uint[](5); | memory 配列を確保 |
scores.push(42); | storage 配列の末尾に追加 |
scores.pop(); | 最後の要素を削除 |
scores.length | 要素数 |
storage | 永続的でオンチェーン、書き込みコストが高い |
memory | 一時的で、1 回の呼び出しの間だけ存在 |
calldata | 読み取り専用の external 関数の入力。最も安価 |
mapping と struct
mapping は長さも反復もないハッシュテーブルです。struct はフィールドをまとめます。
| 構文 | 意味 |
|---|---|
mapping(address => uint) public balances; | キーと値のストア。すべてのキーが存在する(デフォルトは 0) |
balances[msg.sender] += 1; | キーで読み書き |
mapping(address => mapping(address => uint)) allowance; | ネストした mapping |
struct User { string name; uint age; bool active; } | struct を定義 |
User memory u = User("Ada", 36, true); | memory に struct を作成 |
User memory u = User({name: "Ada", age: 36, active: true}); | 名前付きフィールドで作成 |
users[msg.sender] = u; | struct を mapping に格納 |
users[msg.sender].age = 37; | storage 上のフィールドを 1 つ更新 |
delete users[msg.sender]; | デフォルト値にリセット |
関数と可視性
すべての関数は、誰が呼び出せるか、状態を読むか書くかを宣言します。
| 構文 | 意味 |
|---|---|
function add(uint a, uint b) public pure returns (uint) { return a + b; } | パラメータと戻り値を持つ関数 |
public | どこからでも呼び出せる(内部・外部の両方) |
external | コントラクトの外部からのみ呼び出せる |
internal | このコントラクトと、それを継承するコントラクト |
private | このコントラクトのみ |
view | 状態を読むが、書き込まない |
pure | 状態を一切読まない |
payable | 呼び出しとともに Ether を受け取れる |
returns (uint sum, bool ok) | 複数の名前付き戻り値 |
(uint s, bool ok) = f(); | 複数の戻り値を分解代入 |
uint public count; | public 状態変数には自動でゲッター count() が生成される |
modifier、定数、継承
modifier でチェックを再利用し、constant と immutable で値を固定します。
| 構文 | 意味 |
|---|---|
modifier onlyOwner() { require(msg.sender == owner, "Not owner"); _; } | modifier を定義(_ = 関数本体を実行) |
function withdraw() public onlyOwner { ... } | modifier を適用 |
uint public constant MAX = 100; | コンパイル時定数 |
address public immutable owner; | コンストラクタで一度だけ設定し、以後は固定 |
contract Token is ERC20, Ownable { ... } | 他のコントラクトを継承 |
function f() public virtual { ... } | オーバーライドを許可 |
function f() public override { ... } | 親の関数をオーバーライド |
super.f(); | 親の実装を呼び出す |
abstract contract Base { function f() public virtual; } | 未実装の関数を持つコントラクト |
制御フロー
C 系言語の一般的な文。switch はなく、ループは反復ごとにガスがかかります。
| 構文 | 意味 |
|---|---|
if (x > 5) { ... } else if (x > 2) { ... } else { ... } | 条件分岐 |
for (uint i = 0; i < n; i++) { ... } | カウンタ付きループ |
while (x < 10) { x++; } | 条件が成り立つ間ループ |
do { ... } while (cond); | 少なくとも 1 回は実行 |
break; / continue; | ループを抜ける / 次の反復へスキップ |
x > 0 ? a : b | 三項演算子 |
a / b | 整数除算(余りを切り捨て) |
a % b | 余り |
a ** 2 | べき乗 |
unchecked { x++; } | オーバーフローチェックを省略(ガス節約、慎重に使用) |
エラー:require、revert、assert
チェックに失敗するとトランザクション全体が取り消され、未使用のガスは返金されます。
| 構文 | 意味 |
|---|---|
require(amount > 0, "Amount must be positive"); | 入力や状態を検証し、メッセージ付きでリバート |
revert("Not allowed"); | 無条件に中断 |
error Insufficient(uint available, uint requested); | カスタムエラーを宣言(文字列より安価) |
revert Insufficient(balance, amount); | カスタムエラーでリバート |
assert(total == a + b); | 決して失敗してはならない不変条件をチェック |
try token.transfer(to, amt) returns (bool ok) { ... } catch { ... } | 失敗する外部呼び出しを処理 |
イベント
イベントはオフチェーンのアプリが購読できるログを書き込みます。コントラクトからは読み取れません。
| 構文 | 意味 |
|---|---|
event Transfer(address indexed from, address indexed to, uint value); | イベントを宣言 |
emit Transfer(msg.sender, to, amount); | イベントを発行 |
indexed | フィルタ可能なパラメータ(イベントごとに最大 3 つ) |
event Log(string message); | 任意の ABI 型をログに記録できる |
Ether、アドレス、グローバル変数
トランザクションのコンテキストは msg、block、tx から取得します。
| 構文 | 意味 |
|---|---|
msg.sender | この関数を呼び出したアドレス |
msg.value | 呼び出しとともに送られた wei(payable が必要) |
block.timestamp | 現在のブロック時刻(エポックからの秒数) |
block.number | 現在のブロック高 |
tx.origin | トランザクションを開始した外部所有アカウント(認証には使わない) |
address(this).balance | このコントラクトの Ether 残高 |
payable(to).transfer(1 ether); | Ether を送金。失敗時はリバート |
(bool ok, ) = to.call{value: amt}(""); | 低レベルの送金。成功フラグを返す |
receive() external payable {} | 単純な Ether 送金時に実行される |
fallback() external payable {} | 一致する関数がないときに実行される |
keccak256(abi.encodePacked(a, b)) | 値をハッシュ化 |
abi.encode(x), abi.decode(data, (uint)) | ABI データをエンコード / デコード |
よく使う Solidity 構文を 1 ページに。この Solidity チートシートは、Ethereum とすべての EVM チェーンのスマートコントラクト言語のクイックリファレンスです。コントラクトの宣言、型の選択、mapping と struct へのデータ保存、適切な可視性と状態可変性を持つ関数の記述、require、modifier、イベントによるガードまでを網羅します。
ここで扱う構文は Solidity 0.8 です。デフォルトで算術オーバーフローをチェックし、Remix、Hardhat、Foundry で動作します。必要な部分をコピーするか、Solidity プレイグラウンドでライブに試してみてください。コントラクトを書き、コンパイルし、ブラウザ内の EVM で実行できます。