プログラミングにおいて、特定の処理をまとめたコードの塊のことを関数と呼びます。プログラム内で何度も呼び出して使うなど、関数を使うことによってコードの再利用性や保守性を高め、効率的なプログラミングを実現することができます。
関数
関数はスマートコントラクト内の特定の動作や処理を実行するためのブロックです。
関数は次のように定義されます。引数(インプット)の値に何かしらの処理を行い、戻り値(アウトプット)を返す、というものです。
また、詳細は後述しますが、修飾子を付与することによって、どういう特徴を持つ関数であるかを明示することができます。
function 関数名(引数) 修飾子 returns (戻り値の型) {
<処理>
...
return 戻り値;
}
実際の関数の例は、次のようなものになります。addressToPerson
はday9でも出てきた変数で、address
とPerson
型の変数を紐付けるマッピングです。ここでは、戻り値として、uint256型のbalance
(残高)を返しています。ここで定義されている、public
およびview
はそれぞれ関数に特徴をもたせる修飾子です。
contract PersonStorage {
...
function getBalanceOf(address userAddress) public view returns (uint256){
uint256 balance = addressToPerson[userAddress].balance;
return balance;
}
...
}
修飾子
修飾子とは、関数や変数などの要素に対して追加の機能や振る舞いを提供するための記述です。
ここでは、関数もしくは変数に使う修飾子を列挙して、説明します。
可視性修飾子
可視性修飾子は関数や変数がどの範囲でアクセス可能かを制御するためのものです。
public
:コントラクトの内部からもコントラクトの外部(他のコントラクトやRemixのGUI等)からもアクセス可能です。private
:関数や変数が定義されているコントラクト内からしかアクセスすることができません。internal
:関数や変数が定義されたコントラクトそのものかそこから派生したコントラクトからのみアクセス可能です。external
:外部からの呼び出し専用で、コントラクトの外部からのみアクセス可能です。
前述の関数で例示したgetBalanceOf
関数は、public
修飾子が付与されているので、コントラクトの外部から呼び出すことができます。例えば、Remixで関数の実行結果をGUIから確認したい場合には、public
を使います。
Remixでの実行例は下記のとおりですが、引数にaddress
の値を指定し、実行すると、残高として1ETHが返っています。
状態修飾子
状態修飾子も修飾子の一種で、関数がスマートコントラクトの状態に対してどのような影響を与えるかを指定します(変数ではなく、関数に対しての修飾子ですが、payable
については、address
型の修飾子として使う場合もあります)。
view
:関数がスマートコントラクトの状態を変更しない、つまりスマートコントラクト内で定義された変数の値を変更しないことを示します。状態を変更しないことによって、ガスコストが低くなる傾向があります。pure
:view
同様に関数がスマートコントラクトの状態を変更しないことに加えて、関数の外部のデータ(スマートコントラクト内の変数等)を参照できないことを示します。純粋な数学的演算や計算に使われます。状態を変更しないことによって、ガスコストが低くなる傾向があります。payable
:関数がEtherの受け取りを許可することを示します。Etherの送金、受け取りなど通貨のやり取りを伴う操作については、明示的にpayable
を指定する必要があります。
前述の例のgetBalanceOf
は、スマートコントラクト内の変数を参照はしますが、変更は何もしないので、view
が適切になります。
payable
の例を次に示します。この例では、deposit
(入金)関数の修飾子としてpayable
が付いています。この関数をコールした人(msg.sender
)とその送金額(msg.value
)をbalances
(残高)というマッピング型の変数に記録する、という処理を実施しています。
mapping(address => uint) balances;
function deposit() payable external {
balances[msg.sender] += msg.value;
}
msg.sender
とmsg.value
は特殊な変数で、次のような意味を持ちます。
msg.sender
:deposit
関数を呼んだ人またはコントラクトのアドレスmsg.value
:deposit
関数を呼ぶ際に送金されたEtherの金額
Remixでdeposit関数を呼んだ場合の例は次のようになります。Valueに送金額を入力し、depositのボタンを押すと外部(Remix)から関数を呼ぶことができます。関数が呼ばれるとdeposit関数をコールするトランザクションが発行され、トランザクションには、msg.senderとして、0x5B3…のアドレス、msg.valueとして、1000000000000000000Wei(1ETH)の数字が含まれています。
関数修飾子
modifier(関数修飾子)は、関数に動作や振る舞いを付加するために使われます。
関数間で共通に利用される処理を定義することが可能で、これによって、該当の処理の再利用性を高めることができます。
次の例では、onlyOwner
というmodifier
が定義されており、関数changeOwner
でonlyOwner
が修飾されています。これによって、changeOwner
の処理の冒頭で、この関数を呼んだ人が現在のowner
であるかのチェックが行われます。owner
と異なる場合には、エラーメッセージが表示され、関数は実行されません(トランザクションが実行されずに戻されることをrevert
と呼びます)。ownerと同じ場合には、引数で指定されたアドレスが新しいownerになります。
contract PersonStorage {
address public owner = 0x551af3f6A226a62BCb27f08B42401bd5629f2E44;
...
modifier onlyOwner() {
// エラーは、"コントラクト所有者のみが実行可能な関数です。"の意味
require(msg.sender == owner, "Only contract owner can call this function");
_; // プレースホルダー(関数の実行箇所)
}
function changeOwner(address newOwner) public onlyOwner {
owner = newOwner;
}
}
その他の修飾子
定数とは、常に一定の値を示す変数のことですが、定数を定義するための修飾子として、constant
とimmutable
の2つがあります。
constant
: 値が一度設定されたら変更することができず、実行時は常に同じ値を持ちます。変数を定義する際に、値も合わせてセットする必要があります。immutable
:constant
と同様に変更できない定数ですが、値のセットは変数を定義する際か、もしくは、コンストラクターと呼ばれるコントラクトの初期化処理の際に呼ばれる特殊な関数内、のいづれかで実行する必要があります。
次の例では、FEE(手数料)というconstant
変数およびi_owner
(所有者)というimmutable
変数が定義されています。FEEとして、0.1ETHを想定し、これは変更することはできません。i_owner
は所有者のアドレスを設定するためのものですが、変数を定義する時ではなく、PersonStorage
コントラクトが初期化(デプロイ)される際に一度だけconstructor
関数が呼ばれ、その中で、このコントラクトをデプロイした人のアドレスがセットされます。
慣習の一つとして、constant
変数は大文字で定義され、immutable
変数はi_を頭につけて定義されることがあります。
contract PersonStorage {
uint256 public constant FEE = 1e17; // 0.1ETH
address public immutable i_owner;
...
constructor() {
i_owner = msg.sender;
}
}
ご意見をお聞かせください!
この記事、または、web3チュートリアル全体について、是非、あなたのご意見をお聞かせください。
アンケートはこちらからご回答いただけます。
無料相談承ります
オンラインでの無料相談を承っています。ご希望の方は、お問い合わせフォームよりご連絡ください。
ITの専門家があなたのご質問にお答えいたします。
Comments