payableでEtherを受け取る
Etherを受け取る関数にはpayableを付けます。送られた金額はmsg.valueでwei単位として取得できます。1 etherは1018 weiです。
入金を記録するSOLIDITY
contract TipJar {
mapping(address => uint256) public tips;
function tip() external payable {
require(msg.value > 0, "send ether");
tips[msg.sender] += msg.value;
}
function total() external view returns (uint256) {
return address(this).balance;
}
}実行結果OUTPUT
tip(0.1 ether) → tips[Account 0]: 0.1 ether
total() → 0.1 etherRemix VMではValue欄の単位をEtherへ切り替えて、0.1などを入力してからtipを実行します。学習中も、残高と送金先を毎回確認します。
引き出し処理を設計する
コントラクトに入ったEtherを取り出す関数は、誰でも呼べる状態にしてはいけません。次の断片は、前のTipJarにfunctions章のOwnedを同じファイルへ定義し、contract TipJar is Ownedとして継承した場合のwithdrawです。ownerとonlyOwnerはOwnedから提供されます。
Ownedを継承したTipJarの引き出しSOLIDITY
function withdraw(uint256 amount) external onlyOwner {
require(amount <= address(this).balance, "insufficient");
(bool success, ) = payable(owner).call{value: amount}("");
require(success, "transfer failed");
}実行結果OUTPUT
Account 0(owner)でwithdraw(0.05 ether) → 送金成功
Account 1でwithdraw(0.05 ether) → revert "owner only"
残高不足でwithdraw → revert "insufficient"この短い例だけを本番で使わず、再入可能性やpull paymentなどの設計を安全性の章で確認してください。
イベントで履歴を知らせる
入金イベントSOLIDITY
event Tipped(address indexed from, uint256 amount);
function tip() external payable {
require(msg.value > 0, "send ether");
emit Tipped(msg.sender, msg.value);
}実行結果OUTPUT
tip(0.1 ether) → Tipped(from: Account 0, amount: 0.1 ether)イベントは状態変数の代わりではなく、外部のWebアプリケーションやブロックエクスプローラーが履歴を追うためのログです。indexedを付けた項目は検索条件に使いやすくなります。
ABIで外部から呼び出す
ABIは関数名、引数、戻り値、イベントなどをJSON形式で表すインターフェースです。フロントエンドはABIとコントラクトアドレスを使って、読み取りやトランザクションを組み立てます。ABIが同じなら、実装の詳細を知らなくても決められた関数を呼べます。
練習
入金者ごとの累計額を記録し、Tippedイベントを出すTipJarを完成させましょう。次は安全なコントラクト設計です。