viewとpure
viewは状態を読みますが変更しません。pureは状態もブロック情報も読まず、引数だけから結果を計算します。どちらも書き込みトランザクションを作りません。
読み取りと計算SOLIDITY
contract Calculator {
uint256 public base = 10;
function plusBase(uint256 value) external view returns (uint256) {
return base + value;
}
function double(uint256 value) external pure returns (uint256) {
return value * 2;
}
}実行結果OUTPUT
plusBase(5) → 15
double(7) → 14modifierで条件を再利用する
所有者チェックSOLIDITY
contract Owned {
address public owner;
constructor() { owner = msg.sender; }
modifier onlyOwner() {
require(msg.sender == owner, "owner only");
_;
}
function resetOwner(address next) external onlyOwner {
require(next != address(0), "zero address");
owner = next;
}
}実行結果OUTPUT
Account 0でresetOwner(account1) → owner: Account 1
Account 1でresetOwner(account2) → owner: Account 2
Account 2でresetOwner(address(0)) → revert "zero address"
Account 0でresetOwner(account2) → revert "owner only"_;の位置に本体が展開されます。modifierを付けるだけで条件を忘れにくくなりますが、複雑な処理をmodifierへ詰め込みすぎないようにします。
require、revert、assert
利用者の入力や権限の確認にはrequire、条件に応じて明示的に中断するにはrevert、絶対に成立するはずの内部不変条件にはassertを使います。失敗すると、そのトランザクション内の状態変更は巻き戻されます。
カスタムエラーSOLIDITY
contract Limited {
error TooLarge(uint256 value);
function accept(uint256 value) external pure returns (uint256) {
if (value > 100) revert TooLarge(value);
return value;
}
}実行結果OUTPUT
accept(80) → 80
accept(120) → revert TooLarge(120)戻り値と名前付き戻り値
複数の結果を返す場合は、戻り値に名前を付けることもできます。呼び出し側が扱いやすいデータ形を最初に決めることが大切です。
練習
投票関数にonlyOwnerと「同じアドレスは一度だけ」というmappingの条件を追加してみましょう。次はEther・イベント・ABIです。