メインコンテンツへスキップ
Change page

スマートコントラクト言語

イーサリアムの長所は、比較的デベロッパーフレンドリーな言語を使ってスマートコントラクトを記述できることです。 Pythonや中括弧を使ってブロックを表現する言語(opens in a new tab)を使用している方は、見慣れたような構文を持つ言語を使うことができます。

最も活発にメンテナンスされている言語は、以下の2つです。

  • Solidity
  • Vyper

また、経験豊富なデベロッパーであれば、イーサリアム仮想マシン用の中間言語であるYulや、Yulを拡張したYul+を使うのもよいでしょう。

開発中の新しい言語に興味があり、テストに協力したいとお考えの場合は、Feというまだ登場したばかりのスマートコントラクト言語を試してみることができます。

前提知識

プログラミング言語、特にJavaScriptやPythonの知識は、スマートコントラクト言語の違いを理解するのに役立ちます。 また、スマートコントラクトをコンセプトとして理解し、言語比較を深く掘り下げることをお勧めします。 スマートコントラクトの紹介

Solidity

  • スマートコントラクトを実装するためのオブジェクト指向の高級言語
  • C++に強い影響を受けた中括弧を使ってブロックを表現する言語
  • 静的型付け(変数型はコンパイル時に決定される)
  • 以下の言語機能をサポートしている
    • 継承(他のコントラクトを拡張できる)
    • ライブラリ(他のオブジェクト指向言語における静的クラスで定義された静的関数のように、さまざまなコントラクトから呼び出すことができる再利用可能なコードを作成できる)
    • 複雑なユーザー定義型

コントラクトのコード例

1// SPDX-License-Identifier: GPL-3.0
2pragma solidity >= 0.7.0;
3
4contract Coin {
5 // The keyword "public" makes variables
6 // accessible from other contracts
7 address public minter;
8 mapping (address => uint) public balances;
9
10 // Events allow clients to react to specific
11 // contract changes you declare
12 event Sent(address from, address to, uint amount);
13
14 // Constructor code is only run when the contract
15 // is created
16 constructor() {
17 minter = msg.sender;
18 }
19
20 // Sends an amount of newly created coins to an address
21 // Can only be called by the contract creator
22 function mint(address receiver, uint amount) public {
23 require(msg.sender == minter);
24 require(amount < 1e60);
25 balances[receiver] += amount;
26 }
27
28 // Sends an amount of existing coins
29 // from any caller to an address
30 function send(address receiver, uint amount) public {
31 require(amount <= balances[msg.sender], "Insufficient balance.");
32 balances[msg.sender] -= amount;
33 balances[receiver] += amount;
34 emit Sent(msg.sender, receiver, amount);
35 }
36}
すべて表示
コピー

この例は、Solidityのコントラクト構文がどのようなものか理解するのに役立つでしょう。 関数と変数のより詳細な説明については、ドキュメント(opens in a new tab)を参照してください。

Vyper

  • Python的なプログラミング言語
  • 強い型付け
  • コンパクトでわかりやすいコンパイラコード
  • 効率的なバイトコード生成
  • コントラクトの安全性を確保し、監査が容易になることを目的として、意図的にSolidityよりも機能を絞っている。 Vyperは以下をサポートしていない
    • modifier修飾子
    • 継承
    • インラインアセンブリ
    • 関数のオーバーロード
    • オペレータのオーバーロード
    • 再帰呼び出し
    • 無限ループ
    • 2進固定小数点

詳細については、Vyperのドキュメント(opens in a new tab)を参照してください。

1# Open Auction
2
3# Auction params
4# Beneficiary receives money from the highest bidder
5beneficiary: public(address)
6auctionStart: public(uint256)
7auctionEnd: public(uint256)
8
9# Current state of auction
10highestBidder: public(address)
11highestBid: public(uint256)
12
13# Set to true at the end, disallows any change
14ended: public(bool)
15
16# Keep track of refunded bids so we can follow the withdraw pattern
17pendingReturns: public(HashMap[address, uint256])
18
19# Create a simple auction with `_bidding_time`
20# seconds bidding time on behalf of the
21# beneficiary address `_beneficiary`.
22@external
23def __init__(_beneficiary: address, _bidding_time: uint256):
24 self.beneficiary = _beneficiary
25 self.auctionStart = block.timestamp
26 self.auctionEnd = self.auctionStart + _bidding_time
27
28# Bid on the auction with the value sent
29# together with this transaction.
30# The value will only be refunded if the
31# auction is not won.
32@external
33@payable
34def bid():
35 # Check if bidding period is over.
36 assert block.timestamp < self.auctionEnd
37 # Check if bid is high enough
38 assert msg.value > self.highestBid
39 # Track the refund for the previous high bidder
40 self.pendingReturns[self.highestBidder] += self.highestBid
41 # Track new high bid
42 self.highestBidder = msg.sender
43 self.highestBid = msg.value
44
45# Withdraw a previously refunded bid. The withdraw pattern is
46# used here to avoid a security issue. If refunds were directly
47# sent as part of bid(), a malicious bidding contract could block
48# those refunds and thus block new higher bids from coming in.
49@external
50def withdraw():
51 pending_amount: uint256 = self.pendingReturns[msg.sender]
52 self.pendingReturns[msg.sender] = 0
53 send(msg.sender, pending_amount)
54
55# End the auction and send the highest bid
56# to the beneficiary.
57@external
58def endAuction():
59 # It is a good guideline to structure functions that interact
60 # with other contracts (i.e. they call functions or send ether)
61 # into three phases:
62 # 1. checking conditions
63 # 2. performing actions (potentially changing conditions)
64 # 3. interacting with other contracts
65 # If these phases are mixed up, the other contract could call
66 # back into the current contract and modify the state or cause
67 # effects (ether payout) to be performed multiple times.
68 # If functions called internally include interaction with external
69 # contracts, they also have to be considered interaction with
70 # external contracts.
71
72 # 1. Conditions
73 # Check if auction endtime has been reached
74 assert block.timestamp >= self.auctionEnd
75 # Check if this function has already been called
76 assert not self.ended
77
78 # 2. Effects
79 self.ended = True
80
81 # 3. Interaction
82 send(self.beneficiary, self.highestBid)
すべて表示
コピー

この例は、Vyperのコントラクト構文がどのようなものか理解するのに役立つでしょう。 関数と変数のより詳細な説明については、ドキュメント(opens in a new tab)を参照してください。

YulとYul+

イーサリアムを使い始めたばかりで、スマートコントラクト言語を使ってコードを書いたことがない場合は、SolidityやVyperを利用することをお勧めします。 YulやYul+を検討するのは、スマートコントラクトのセキュリティの最善の方法や、EVMとの連携の具体的な内容に精通してからにしてください。

Yul

  • イーサリアムのための中間言語
  • EVMおよびEwasm(opens in a new tab)というイーサリアム向けのWebAssemblyをサポートしており、両方のプラットフォームで使用可能な共通部分として設計されている
  • EVMとeWASMの両方のプラットフォームに同程度のメリットをもたらす、高度な最適化段階を経る対象となる

Yul+

  • Yulに高効率な拡張機能を施した低レベル言語
  • コントラクトのオプティミスティック・ロールアップのために設計された
  • Yulに新しい機能を追加した実験的なアップグレード案として捉えることができる

コントラクトのコード例

以下の簡単な例では、べき乗関数を実装しています。 solc --strict-assembly --bin input.yulを使用してコンパイルすることができます。 この例は、input.yulに記述されます。

1{
2 function power(base, exponent) -> result
3 {
4 switch exponent
5 case 0 { result := 1 }
6 case 1 { result := base }
7 default
8 {
9 result := power(mul(base, base), div(exponent, 2))
10 if mod(exponent, 2) { result := mul(base, result) }
11 }
12 }
13 let res := power(calldataload(0), calldataload(32))
14 mstore(0, res)
15 return(0, 32)
16}
すべて表示

スマートコントラクトの経験が豊富な場合は、YulによるERC20の完全な実装(opens in a new tab)をご覧ください。

Fe

  • イーサリアム仮想マシン(EVM)向けの静的型付け言語
  • PythonとRustの影響を受けている
  • イーサリアムのエコシステムに不慣れなデベロッパーでも簡単に学習できる言語であることを目標としている
  • Feの開発は未だ初期段階にあり、2021年1月にアルファ版がリリースされた

コントラクトのコード例

Feで実装されたシンプルなコントラクトのコード例を以下に示します。

1type BookMsg = bytes[100]
2
3contract GuestBook:
4 pub guest_book: map<address, BookMsg>
5
6 event Signed:
7 book_msg: BookMsg
8
9 pub def sign(book_msg: BookMsg):
10 self.guest_book[msg.sender] = book_msg
11
12 emit Signed(book_msg=book_msg)
13
14 pub def get_msg(addr: address) -> BookMsg:
15 return self.guest_book[addr].to_mem()
16
すべて表示

選択方法

他のプログラミング言語と同様に、個人的な好みだけでなく、行いたいことに最適なツールを選択することが重要です。

まだどの言語も試していない場合に考慮すべき事項をいくつか紹介します。

Solidityの長所

  • 初心者向けに、多くのチュートリアルや学習ツールが用意されている。 詳細については、コーディングで学ぶセクションを参照
  • 優れた開発ツールが利用可能
  • Solidityには大きなデベロッパーコミュニティがあり、質問に対する答えをすぐに見つけることができる

Vyperの長所

  • Pythonデベロッパーが、スマートコントラクトの記述を始めるのに最適な方法である
  • Vyperの機能の数は絞られているため、アイデアから素早くプロトタイプを構築可能
  • Vyperは監査が容易で、最大限に人間が読めるようにすることを目指している

YulとYul+の長所

  • シンプルで機能的な低レベル言語
  • 生のEVMに近づくことができ、コントラクトのガス使用量を最適化するのに役立つ

言語比較

コントラクトのライフサイクル、インターフェイス、演算子、データ構造、関数、制御フローなどの基本的な構文の比較については、 Auditlessによるチートシート(opens in a new tab)を参照してください。

参考文献

この記事は役に立ちましたか?