Solidity-クイックガイド

Solidityは、スマートコントラクトを実装するためのコントラクト指向の高水準プログラミング言語です。Solidityは、C ++、Python、JavaScriptの影響を強く受け、イーサリアム仮想マシン(EVM)をターゲットにするように設計されています。

Solidityは静的に型付けされ、継承、ライブラリ、および複雑なユーザー定義型プログラミング言語をサポートします。

Solidityを使用して、投票、クラウドファンディング、ブラインドオークション、マルチシグニチャウォレットなどの用途の契約を作成できます。

イーサリアムとは何ですか?

イーサリアムは分散型です。スマートコントラクトを実行するブロックチェーンプラットフォーム。つまり、ダウンタイム、検閲、詐欺、またはサードパーティの干渉の可能性なしに、プログラムされたとおりに実行されるアプリケーション。

イーサリアム仮想マシン(EVM)

EVMとしても知られるイーサリアム仮想マシンは、イーサリアムのスマートコントラクトのランタイム環境です。イーサリアム仮想マシンは、セキュリティを提供し、世界中のコンピューターによる信頼できないコードを実行することに重点を置いています。

EVMは、サービス拒否攻撃の防止に特化しており、プログラムが互いの状態にアクセスできないようにし、潜在的な干渉なしに通信を確立できるようにします。

イーサリアム仮想マシンは、イーサリアムに基づくスマートコントラクトのランタイム環境として機能するように設計されています。

スマートコントラクトとは何ですか?

スマートコントラクトは、契約の交渉または履行をデジタルで促進、検証、または実施することを目的としたコンピュータープロトコルです。スマートコントラクトは、サードパーティなしで信頼できるトランザクションの実行を可能にします。これらのトランザクションは追跡可能で不可逆的です。

スマートコントラクトの概念は、1994年にニックサボによって最初に提案されました。サボは、デジタル通貨の基礎を築くことで知られる法学者および暗号研究者です。

今スマートコントラクトを理解していなくても大丈夫です。後で詳しく説明します。

この章では、CentOSマシンでSolidityコンパイラをセットアップする方法について説明します。Linuxマシンをお持ちでない場合は、小規模な契約やSolidityの迅速な学習にオンラインコンパイラを使用できます。

方法1-npm / Node.js

これは、CentoSマシンにSolidityコンパイラをインストールする最速の方法です。SolidityCompilerをインストールするには次の手順があります-

Node.jsをインストールします

まず、CentOSマシンでnode.jsが使用可能であることを確認します。利用できない場合は、次のコマンドを使用してインストールします-

# First install epel-release
$sudo yum install epel-release

# Now install nodejs
$sudo yum install nodejs

# Next install npm (Nodejs Package Manager )
$sudo yum install npm

# Finally verify installation
$npm --version

すべてがインストールされている場合は、次のような出力が表示されます-

3.10.10

solcをインストールする

Node.jsパッケージマネージャーをインストールしたら、次のようにSolidityコンパイラのインストールに進むことができます-

$sudonpm install -g solc

上記のコマンドはsolcjsプログラムをインストールし、システム全体でグローバルに利用できるようにします。これで、次のコマンドを発行してSolidityコンパイラをテストできます-

$solcjs-version

すべてがうまくいけば、これは次のように何かを印刷します-

0.5.2+commit.1df8f40c.Emscripten.clang

これで、標準のSolidityコンパイラよりも機能が少ないsolcjsを使用する準備が整いましたが、良い出発点になります。

方法2-Dockerイメージ

Dockerイメージをプルして使用を開始し、Solidityプログラミングを開始できます。以下は簡単な手順です。以下は、SolidityDockerイメージをプルするコマンドです。

$docker pull ethereum/solc:stable

Dockerイメージがダウンロードされたら、次のコマンドを使用して確認できます。

$docker run ethereum/solc:stable-version

これにより、次のように出力されます-

$ docker run ethereum/solc:stable -version

solc, the solidity compiler commandlineinterfaceVersion: 0.5.2+commit.1df8f40c.Linux.g++

方法3:バイナリパッケージのインストール

Linuxマシンに本格的なコンパイラをインストールする場合は、公式WebサイトのSolidityコンパイラのインストールを確認してください。

Solidityソースファイルには、任意の数のコントラクト定義、インポートディレクティブ、およびプラグマディレクティブを含めることができます。

Solidityの簡単なソースファイルから始めましょう。以下はSolidityファイルの例です-

pragma solidity >=0.4.0 <0.6.0;
contract SimpleStorage {
   uint storedData;
   function set(uint x) public {
      storedData = x;
   }
   function get() public view returns (uint) {
      return storedData;
   }
}

プラグマ

最初の行は、ソースコードがSolidityバージョン0.4.0またはバージョン0.6.0までの機能を壊さないがそれを含まない新しいもの用に記述されていることを示すプラグマディレクティブです。

プラグマディレクティブは常にソースファイルに対してローカルであり、別のファイルをインポートする場合、そのファイルからのプラグマはインポートするファイルに自動的に適用されません。

したがって、バージョン0.4.0より前にコンパイルされず、バージョン0.5.0以降のコンパイラでも機能しないファイルのプラグマは、次のように記述されます。

pragma solidity ^0.4.0;

ここでは、^を使用して2番目の条件を追加します。

契約する

Solidityコントラクトは、Ethereumblockchainの特定のアドレスに存在するコード(その機能)とデータ(その状態)のコレクションです。

行uintstoredDataは、uint型のstoredDataと呼ばれる状態変数を宣言し、関数setおよびgetを使用して、変数の値を変更または取得できます。

ファイルのインポート

上記の例にはインポートステートメントはありませんが、SolidityはJavaScriptで使用できるものと非常によく似たインポートステートメントをサポートしています。

次のステートメントは、「filename」からすべてのグローバルシンボルをインポートします。

import "filename";

次の例では、メンバーが「filename」のすべてのグローバルシンボルである新しいグローバルシンボルsymbolNameを作成します。

import * as symbolName from "filename";

現在のファイルと同じディレクトリからファイルxをインポートするには、import "./x" asx;を使用します。import "x"をxとして使用する場合; 代わりに、グローバルな「インクルードディレクトリ」で別のファイルを参照できます。

予約キーワード

Solidityで予約されているキーワードは次のとおりです-

概要 エイリアス 適用する
自動 場合 キャッチ のコピー
デフォルト 定義する 最後の 不変
実装 列をなして しましょう
大きい 一致 可変 ヌル
オーバーライド 部分的 約束する
参照 再配置可能 封印 のサイズ
静的 サポート スイッチ 試してみてください
typedef typeof 未チェック

私たちは、使用しているリミックスIDEをコンパイルするには、私たちのソリディティコードベースを実行します。

Step 1 − RemixIDEコードセクションで指定されたコードをコピーします。

pragma solidity ^0.5.0;
contract SolidityTest {
   constructor() public{
   }
   function getResult() public view returns(uint){
      uint a = 1;
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

Step 2 − [コンパイル]タブで、[ Start to Compile ボタン。

Step 3 − [実行]タブで、[ Deploy ボタン。

Step 4 − [実行]タブで、[選択]を選択します SolidityTest at 0x... ドロップダウンで。

Step 5 −クリック getResult 結果を表示するボタン。

出力

0: uint256: 3

Solidityは、CスタイルとC ++スタイルの両方のコメントをサポートします。

  • //と行末の間のテキストはコメントとして扱われ、Solidityコンパイラによって無視されます。

  • 文字/ *と* /の間のテキストは、コメントとして扱われます。これは複数の行にまたがる場合があります。

次の例は、Solidityでコメントを使用する方法を示しています。

function getResult() public view returns(uint){
   // This is a comment. It is similar to comments in C++

   /*
      * This is a multi-line comment in solidity
      * It is very similar to comments in C Programming
   */
   uint a = 1;
   uint b = 2;
   uint result = a + b;
   return result;
}

任意の言語でプログラムを作成する場合、さまざまな情報を格納するためにさまざまな変数を使用する必要があります。変数は、値を格納するために予約されたメモリ位置に他なりません。これは、変数を作成するときに、メモリにいくらかのスペースを予約することを意味します。

文字、ワイド文字、整数、浮動小数点、倍精度浮動小数点、ブール値など、さまざまなデータ型の情報を格納することができます。変数のデータ型に基づいて、オペレーティングシステムはメモリを割り当て、格納できるものを決定します。予約済みメモリ。

値型

Solidityは、プログラマーに組み込みデータ型とユーザー定義データ型の豊富な品揃えを提供します。次の表に、7つの基本的なC ++データ型を示します。

タイプ キーワード
ブール値 ブール 真/偽
整数 int / uint さまざまなサイズの符号付きおよび符号なし整数。
整数 int8からint256 8ビットから256ビットまでのsignedint。int256はintと同じです。
整数 uint8からuint256 8ビットから256ビットまでのunsignedint。uint256はuintと同じです。
固定小数点数 固定/未固定 さまざまなサイズの符号付きおよび符号なしの固定小数点数。
固定小数点数 固定/未固定 さまざまなサイズの符号付きおよび符号なしの固定小数点数。
固定小数点数 fixedMxN 符号付き固定小数点数。ここで、Mはタイプごとのビット数を表し、Nは小数点を表します。Mは8で割り切れる必要があり、8から256になります。Nは0から80になります。fixedはfixed128x18と同じです。
固定小数点数 ufixedMxN 符号なし固定小数点数。ここで、Mはタイプごとのビット数を表し、Nは小数点を表します。Mは8で割り切れる必要があり、8から256になります。Nは0から80になります。ufixedはufixed128x18と同じです。

住所

addressは、Ethereumアドレスのサイズを表す20バイトの値を保持します。アドレスは、.balanceメソッドを使用して残高を取得するために使用でき、.transferメソッドを使用して別のアドレスに残高を転送するために使用できます。

address x = 0x212;
address myAddress = this;
if (x.balance < 10 && myAddress.balance >= 10) x.transfer(10);

Solidityは3種類の変数をサポートしています。

  • State Variables −値がコントラクトストレージに永続的に保存される変数。

  • Local Variables −関数が実行されるまで値が存在する変数。

  • Global Variables −ブロックチェーンに関する情報を取得するために使用されるグローバル名前空間に特別な変数が存在します。

Solidityは静的に型付けされた言語です。つまり、宣言時に状態またはローカル変数の型を指定する必要があります。宣言された各変数には、そのタイプに基づいたデフォルト値が常にあります。「未定義」または「ヌル」の概念はありません。

状態変数

値がコントラクトストレージに永続的に保存される変数。

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData;      // State variable
   constructor() public {
      storedData = 10;   // Using State variable
   }
}

ローカル変数

値が定義されている関数内でのみ使用できる変数。関数パラメーターは常にその関数に対してローカルです。

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData; // State variable
   constructor() public {
      storedData = 10;   
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return result; //access the local variable
   }
}

pragma solidity ^0.5.0;
contract SolidityTest {
   uint storedData; // State variable
   constructor() public {
      storedData = 10;   
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return storedData; //access the state variable
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

出力

0: uint256: 10

グローバル変数

これらは、グローバルワークスペースに存在し、ブロックチェーンとトランザクションのプロパティに関する情報を提供する特別な変数です。

名前 戻り値
blockhash(uint blockNumber)は(bytes32)を返します 指定されたブロックのハッシュ-現在のブロックを除く最新の256ブロックでのみ機能します
block.coinbase(支払い可能なアドレス) 現在のブロックマイナーのアドレス
block.difficulty(uint) 現在のブロックの難しさ
block.gaslimit(uint) 現在のブロックガス制限
block.number(uint) 現在のブロック番号
block.timestamp(uint) UNIXエポックからの秒数としての現在のブロックタイムスタンプ
gasleft()は(uint256)を返します 残りのガス
msg.data(バイトcalldata) 完全なcalldata
msg.sender(支払い可能なアドレス) メッセージの送信者(現在の発信者)
msg.sig(bytes4) calldata(関数識別子)の最初の4バイト
msg.value(uint) メッセージとともに送信されたweiの数
今(uint) 現在のブロックのタイムスタンプ
tx.gasprice(uint) 取引のガス価格
tx.origin(支払い可能なアドレス) トランザクションの送信者

Solidity変数名

Solidityで変数に名前を付けるときは、次のルールに注意してください。

  • Solidityの予約済みキーワードを変数名として使用しないでください。これらのキーワードについては、次のセクションで説明します。たとえば、breakまたはboolean変数名は無効です。

  • Solidity変数名は、数字(0〜9)で始めることはできません。文字または下線文字で始める必要があります。たとえば、123testは無効な変数名ですが、_123testは有効な変数名です。

  • Solidity変数名では大文字と小文字が区別されます。たとえば、Nameとnameは2つの異なる変数です。

ローカル変数のスコープは、それらが定義されている関数に制限されていますが、状態変数には3つのタイプのスコープがあります。

  • Public−パブリック状態変数には、メッセージを介してだけでなく、内部的にもアクセスできます。パブリック状態変数の場合、自動ゲッター関数が生成されます。

  • Internal −内部状態変数には、現在のコントラクトまたはこれを使用せずに派生したコントラクトから内部的にのみアクセスできます。

  • Private −プライベート状態変数は、現在のコントラクトから内部的にのみアクセスでき、派生コントラクトでは定義されていません。

pragma solidity ^0.5.0;
contract C {
   uint public data = 30;
   uint internal iData= 10;
   
   function x() public returns (uint) {
      data = 3; // internal access
      return data;
   }
}
contract Caller {
   C c = new C();
   function f() public view returns (uint) {
      return c.data(); //external access
   }
}
contract D is C {
   function y() public returns (uint) {
      iData = 3; // internal access
      return iData;
   }
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return storedData; //access the state variable
   }
}

演算子とは何ですか?

簡単な表現をしましょう 4 + 5 is equal to 9。ここで4と5は呼ばれますoperands '+'は operator。Solidityは、次のタイプの演算子をサポートしています。

  • 算術演算子
  • 比較演算子
  • 論理(または関係)演算子
  • 代入演算子
  • 条件付き(または三項)演算子

すべての演算子を1つずつ見ていきましょう。

算術演算子

Solidityは次の算術演算子をサポートします-

変数Aが10を保持し、変数Bが20を保持すると仮定すると、-

例を表示

シニア番号 オペレーターと説明
1

+ (Addition)

2つのオペランドを追加します

Ex: A + Bは30を与えます

2

- (Subtraction)

最初のオペランドから2番目のオペランドを減算します

Ex: A-Bは-10を与えます

3

* (Multiplication)

両方のオペランドを乗算します

Ex: A * Bは200を与えます

4

/ (Division)

分子を分母で割ります

Ex: B / Aは2を与えます

5

% (Modulus)

整数除算の余りを出力します

Ex: B%Aは0を与えます

6

++ (Increment)

整数値を1つ増やします

Ex: A ++は11を与えます

7

-- (Decrement)

整数値を1つ減らします

Ex: A--は9を与えます

比較演算子

Solidityは、次の比較演算子をサポートしています-

変数Aが10を保持し、変数Bが20を保持すると仮定すると、-

例を表示

シニア番号 オペレーターと説明
1

= = (Equal)

2つのオペランドの値が等しいかどうかをチェックし、等しい場合は条件が真になります。

Ex: (A == B)は真ではありません。

2

!= (Not Equal)

2つのオペランドの値が等しいかどうかをチェックし、値が等しくない場合は、条件が真になります。

Ex: (A!= B)は真です。

3

> (Greater than)

左側のオペランドの値が右側のオペランドの値より大きいかどうかを確認します。大きい場合は、条件が真になります。

Ex: (A> B)は正しくありません。

4

< (Less than)

左側のオペランドの値が右側のオペランドの値よりも小さいかどうかを確認し、小さい場合は条件が真になります。

Ex: (A <B)は真です。

5

>= (Greater than or Equal to)

左のオペランドの値が右のオペランドの値以上であるかどうかをチェックし、そうである場合は、条件が真になります。

Ex: (A> = B)は正しくありません。

6

<= (Less than or Equal to)

左のオペランドの値が右のオペランドの値以下であるかどうかをチェックし、そうである場合は、条件が真になります。

Ex: (A <= B)は真です。

論理演算子

Solidityは次の論理演算子をサポートします-

変数Aが10を保持し、変数Bが20を保持すると仮定すると、-

例を表示

シニア番号 オペレーターと説明
1

&& (Logical AND)

両方のオペランドがゼロ以外の場合、条件は真になります。

Ex: (A && B)は本当です。

2

|| (Logical OR)

If any of the two operands are non-zero, then the condition becomes true.

Ex: (A || B) is true.

3

! (Logical NOT)

Reverses the logical state of its operand. If a condition is true, then the Logical NOT operator will make it false.

Ex: ! (A && B) is false.

Bitwise Operators

Solidity supports the following bitwise operators −

Assume variable A holds 2 and variable B holds 3, then −

Show Example

Sr.No. Operator & Description
1

& (Bitwise AND)

It performs a Boolean AND operation on each bit of its integer arguments.

Ex: (A & B) is 2.

2

| (BitWise OR)

It performs a Boolean OR operation on each bit of its integer arguments.

Ex: (A | B) is 3.

3

^ (Bitwise XOR)

It performs a Boolean exclusive OR operation on each bit of its integer arguments. Exclusive OR means that either operand one is true or operand two is true, but not both.

Ex: (A ^ B) is 1.

4

~ (Bitwise Not)

It is a unary operator and operates by reversing all the bits in the operand.

Ex: (~B) is -4.

5

<< (Left Shift)

It moves all the bits in its first operand to the left by the number of places specified in the second operand. New bits are filled with zeros. Shifting a value left by one position is equivalent to multiplying it by 2, shifting two positions is equivalent to multiplying by 4, and so on.

Ex: (A << 1) is 4.

6

>> (Right Shift)

Binary Right Shift Operator. The left operand's value is moved right by the number of bits specified by the right operand.

Ex: (A >> 1) is 1.

7

>>> (Right shift with Zero)

This operator is just like the >> operator, except that the bits shifted in on the left are always zero.

Ex: (A >>> 1) is 1.

Assignment Operators

Solidity supports the following assignment operators −

Show Example

Sr.No. Operator & Description
1

= (Simple Assignment )

Assigns values from the right side operand to the left side operand

Ex: C = A + B will assign the value of A + B into C

2

+= (Add and Assignment)

It adds the right operand to the left operand and assigns the result to the left operand.

Ex: C += A is equivalent to C = C + A

3

−= (Subtract and Assignment)

It subtracts the right operand from the left operand and assigns the result to the left operand.

Ex: C -= A is equivalent to C = C - A

4

*= (Multiply and Assignment)

It multiplies the right operand with the left operand and assigns the result to the left operand.

Ex: C *= A is equivalent to C = C * A

5

/= (Divide and Assignment)

It divides the left operand with the right operand and assigns the result to the left operand.

Ex: C /= A is equivalent to C = C / A

6

%= (Modules and Assignment)

It takes modulus using two operands and assigns the result to the left operand.

Ex: C %= A is equivalent to C = C % A

Note − Same logic applies to Bitwise operators so they will become like <<=, >>=, >>=, &=, |= and ^=.

Conditional Operator (? :)

The conditional operator first evaluates an expression for a true or false value and then executes one of the two given statements depending upon the result of the evaluation.

Show Example

Sr.No. Operator and Description
1

? : (Conditional )

If Condition is true? Then value X : Otherwise value Y

While writing a contract, you may encounter a situation where you need to perform an action over and over again. In such situations, you would need to write loop statements to reduce the number of lines.

Solidity supports all the necessary loops to ease down the pressure of programming.

Sr.No Loops & Description
1

While Loop

The most basic loop in Solidity is the while loop which would be discussed in this chapter.

2

do...while Loop

The do...while loop is similar to the while loop except that the condition check happens at the end of the loop.

3

For Loop

The for loop is the most compact form of looping. It includes the following three important parts.

4

Loop Control

Solidity provides full control to handle loops and switch statements.

While writing a program, there may be a situation when you need to adopt one out of a given set of paths. In such cases, you need to use conditional statements that allow your program to make correct decisions and perform right actions.

Solidity supports conditional statements which are used to perform different actions based on different conditions. Here we will explain the if..else statement.

Flow Chart of if-else

The following flow chart shows how the if-else statement works.

Solidity supports the following forms of if..else statement −

Sr.No Statements & Description
1

if statement

The if statement is the fundamental control statement that allows Solidity to make decisions and execute statements conditionally.

2

if...else statement

The 'if...else' statement is the next form of control statement that allows Solidity to execute statements in a more controlled way.

3

if...else if... statement.

The if...else if... statement is an advanced form of if...else that allows Solidity to make a correct decision out of several conditions.

Solidity supports String literal using both double quote (") and single quote ('). It provides string as a data type to declare a variable of type String.

pragma solidity ^0.5.0;

contract SolidityTest {
   string data = "test";
}

In above example, "test" is a string literal and data is a string variable. More preferred way is to use byte types instead of String as string operation requires more gas as compared to byte operation. Solidity provides inbuilt conversion between bytes to string and vice versa. In Solidity we can assign String literal to a byte32 type variable easily. Solidity considers it as a byte32 literal.

pragma solidity ^0.5.0;

contract SolidityTest {
   bytes32 data = "test";
}

Escape Characters

Sr.No. Character & Description
1

\n

Starts a new line.

2

\\

Backslash

3

\'

Single Quote

4

\"

Double Quote

5

\b

Backspace

6

\f

Form Feed

7

\r

Carriage Return

8

\t

Tab

9

\v

Vertical Tab

10

\xNN

Represents Hex value and inserts appropriate bytes.

11

\uNNNN

Represents Unicode value and inserts UTF-8 sequence.

Bytes to String Conversion

Bytes can be converted to String using string() constructor.

bytes memory bstr = new bytes(10);
string message = string(bstr);

Example

Try the following code to understand how the string works in Solidity.

pragma solidity ^0.5.0;

contract SolidityTest {   
   constructor() public{       
   }
   function getResult() public view returns(string memory){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {
      
      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;
      
      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;
      
      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);
   }
}

Run the above program using steps provided in Solidity First Application chapter.

Output

0: string: 3

Array is a data structure, which stores a fixed-size sequential collection of elements of the same type. An array is used to store a collection of data, but it is often more useful to think of an array as a collection of variables of the same type.

Instead of declaring individual variables, such as number0, number1, ..., and number99, you declare one array variable such as numbers and use numbers[0], numbers[1], and ..., numbers[99] to represent individual variables. A specific element in an array is accessed by an index.

In Solidity, an array can be of compile-time fixed size or of dynamic size. For storage array, it can have different types of elements as well. In case of memory array, element type can not be mapping and in case it is to be used as function parameter then element type should be an ABI type.

All arrays consist of contiguous memory locations. The lowest address corresponds to the first element and the highest address to the last element.

Declaring Arrays

To declare an array of fixed size in Solidity, the programmer specifies the type of the elements and the number of elements required by an array as follows −

type arrayName [ arraySize ];

This is called a single-dimension array. The arraySize must be an integer constant greater than zero and type can be any valid Solidity data type. For example, to declare a 10-element array called balance of type uint, use this statement −

uint balance[10];

To declare an array of dynamic size in Solidity, the programmer specifies the type of the elements as follows −

type[] arrayName;

Initializing Arrays

You can initialize Solidity array elements either one by one or using a single statement as follows −

uint balance[3] = [1, 2, 3];

The number of values between braces [ ] can not be larger than the number of elements that we declare for the array between square brackets [ ]. Following is an example to assign a single element of the array −

If you omit the size of the array, an array just big enough to hold the initialization is created. Therefore, if you write −

uint balance[] = [1, 2, 3];

You will create exactly the same array as you did in the previous example.

balance[2] = 5;

The above statement assigns element number 3rd in the array a value of 5.

Creating dynamic memory arrays

Dynamic memory arrays are created using new keyword.

uint size = 3;
uint balance[] = new uint[](size);

Accessing Array Elements

An element is accessed by indexing the array name. This is done by placing the index of the element within square brackets after the name of the array. For example −

uint salary = balance[2];

The above statement will take 3rd element from the array and assign the value to salary variable. Following is an example, which will use all the above-mentioned three concepts viz. declaration, assignment and accessing arrays −

Members

  • length − length returns the size of the array. length can be used to change the size of dynamic array be setting it.

  • push − push allows to append an element to a dynamic storage array at the end. It returns the new length of the array.

Example

Try the following code to understand how the arrays works in Solidity.

pragma solidity ^0.5.0;

contract test {
   function testArray() public pure{
      uint len = 7; 
      
      //dynamic array
      uint[] memory a = new uint[](7);
      
      //bytes is same as byte[]
      bytes memory b = new bytes(len);
      
      assert(a.length == 7);
      assert(b.length == len);
      
      //access array variable
      a[6] = 8;
      
      //test array variable
      assert(a[6] == 8);
      
      //static array
      uint[3] memory c = [uint(1) , 2, 3];
      assert(c.length == 3);
   }
}

Enums restrict a variable to have one of only a few predefined values. The values in this enumerated list are called enums.

With the use of enums it is possible to reduce the number of bugs in your code.

For example, if we consider an application for a fresh juice shop, it would be possible to restrict the glass size to small, medium, and large. This would make sure that it would not allow anyone to order any size other than small, medium, or large.

Example

Try the following code to understand how the enum works in Solidity.

pragma solidity ^0.5.0;

contract test {
   enum FreshJuiceSize{ SMALL, MEDIUM, LARGE }
   FreshJuiceSize choice;
   FreshJuiceSize constant defaultChoice = FreshJuiceSize.MEDIUM;

   function setLarge() public {
      choice = FreshJuiceSize.LARGE;
   }
   function getChoice() public view returns (FreshJuiceSize) {
      return choice;
   }
   function getDefaultChoice() public pure returns (uint) {
      return uint(defaultChoice);
   }
}

Run the above program using steps provided in Solidity First Application chapter.

First Click setLarge Button to set the value as LARGE then click getChoice to get the selected choice.

Output

uint8: 2

Click getDefaultChoice Button to get the default choice.

Output

uint256: 1

Struct types are used to represent a record. Suppose you want to keep track of your books in a library. You might want to track the following attributes about each book −

  • Title
  • Author
  • Subject
  • Book ID

Defining a Struct

To define a Struct, you must use the struct keyword. The struct keyword defines a new data type, with more than one member. The format of the struct statement is as follows −

struct struct_name { 
   type1 type_name_1;
   type2 type_name_2;
   type3 type_name_3;
}

Example

struct Book { 
   string title;
   string author;
   uint book_id;
}

Accessing a Struct and its variable

To access any member of a structure, we use the member access operator (.). The member access operator is coded as a period between the structure variable name and the structure member that we wish to access. You would use the struct to define variables of structure type. The following example shows how to use a structure in a program.

Example

Try the following code to understand how the structs works in Solidity.

pragma solidity ^0.5.0;

contract test {
   struct Book { 
      string title;
      string author;
      uint book_id;
   }
   Book book;

   function setBook() public {
      book = Book('Learn Java', 'TP', 1);
   }
   function getBookId() public view returns (uint) {
      return book.book_id;
   }
}

Run the above program using steps provided in Solidity First Application chapter.

First Click setBook Button to set the value as LARGE then click getBookId to get the selected book id.

Output

uint256: 1

Mapping is a reference type as arrays and structs. Following is the syntax to declare a mapping type.

mapping(_KeyType => _ValueType)

Where

  • _KeyType − can be any built-in types plus bytes and string. No reference type or complex objects are allowed.

  • _ValueType − can be any type.

Considerations

  • Mapping can only have type of storage and are generally used for state variables.

  • Mapping can be marked public. Solidity automatically create getter for it.

Example

Try the following code to understand how the mapping type works in Solidity.

pragma solidity ^0.5.0;

contract LedgerBalance {
   mapping(address => uint) public balances;

   function updateBalance(uint newBalance) public {
      balances[msg.sender] = newBalance;
   }
}
contract Updater {
   function updateBalance() public returns (uint) {
      LedgerBalance ledgerBalance = new LedgerBalance();
      ledgerBalance.updateBalance(10);
      return ledgerBalance.balances(address(this));
   }
}

Run the above program using steps provided in Solidity First Application chapter.

First Click updateBalance Button to set the value as 10 then look into the logs which will show the decoded output as −

Output

{
   "0": "uint256: 10"
}

Solidity allows implicit as well as explicit conversion. Solidity compiler allows implicit conversion between two data types provided no implicit conversion is possible and there is no loss of information. For example uint8 is convertible to uint16 but int8 is convertible to uint256 as int8 can contain negative value not allowed in uint256.

Explicit Conversion

We can explicitly convert a data type to another using constructor syntax.

int8 y = -3;
uint x = uint(y);
//Now x = 0xfffff..fd == two complement representation of -3 in 256 bit format.

Conversion to smaller type costs higher order bits.

uint32 a = 0x12345678;
uint16 b = uint16(a); // b = 0x5678

Conversion to higher type adds padding bits to the left.

uint16 a = 0x1234;
uint32 b = uint32(a); // b = 0x00001234

Conversion to smaller byte costs higher order data.

bytes2 a = 0x1234;
bytes1 b = bytes1(a); // b = 0x12

Conversion to larger byte add padding bits to the right.

bytes2 a = 0x1234;
bytes4 b = bytes4(a); // b = 0x12340000

Conversion between fixed size bytes and int is only possible when both are of same size.

bytes2 a = 0x1234;
uint32 b = uint16(a); // b = 0x00001234
uint32 c = uint32(bytes4(a)); // c = 0x12340000
uint8 d = uint8(uint16(a)); // d = 0x34
uint8 e = uint8(bytes1(a)); // e = 0x12

Hexadecimal numbers can be assigned to any integer type if no truncation is needed.

uint8 a = 12; // no error
uint32 b = 1234; // no error
uint16 c = 0x123456; // error, as truncation required to 0x3456

In solidity we can use wei, finney, szabo or ether as a suffix to a literal to be used to convert various ether based denominations. Lowest unit is wei and 1e12 represents 1 x 1012.

assert(1 wei == 1);
assert(1 szabo == 1e12);
assert(1 finney == 1e15);
assert(1 ether == 1e18);
assert(2 ether == 2000 fenny);

Time Units

Similar to currency, Solidity has time units where lowest unit is second and we can use seconds, minutes, hours, days and weeks as suffix to denote time.

assert(1 seconds == 1);
assert(1 minutes == 60 seconds);
assert(1 hours == 60 minutes);
assert(1 day == 24 hours);
assert(1 week == 7 days);

Special variables are globally available variables and provides information about the blockchain. Following is the list of special variables −

Sr.No. Special Variable & Description
1

blockhash(uint blockNumber) returns (bytes32)

Hash of the given block - only works for 256 most recent, excluding current, blocks.

2

block.coinbase (address payable)

Current block miner's address.

3

block.difficulty (uint)

current block difficulty.

4

block.gaslimit (uint)

Current block gaslimit.

5

block.number (uint)

Current block number.

6

block.timestamp

Current block timestamp as seconds since unix epoch.

7

gasleft() returns (uint256)

Remaining gas.

8

msg.data (bytes calldata)

Complete calldata.

9

msg.sender (address payable)

Sender of the message (current call).

10

msg.sig (bytes4)

First four bytes of the calldata (i.e. function identifier)

11

msg.value (uint)

Number of wei sent with the message.

12

now (uint)

Current block timestamp (alias for block.timestamp).

13

tx.gasprice (uint)

Gas price of the transaction.

14

tx.origin (address payable)

Sender of the transaction (full call chain).

Example

Try the following code to see the use of msg, a special variable to get the sender address in Solidity.

pragma solidity ^0.5.0;

contract LedgerBalance {
   mapping(address => uint) public balances;

   function updateBalance(uint newBalance) public {
      balances[msg.sender] = newBalance;
   }
}
contract Updater {
   function updateBalance() public returns (uint) {
      LedgerBalance ledgerBalance = new LedgerBalance();
      ledgerBalance.updateBalance(10);
      return ledgerBalance.balances(address(this));
   }
}

Run the above program using steps provided in Solidity First Application chapter.

First Click updateBalance Button to set the value as 10 then look into the logs which will show the decoded output as −

Output

{
   "0": "uint256: 10"
}

Style Guide helps to maintain code layout consistent and make code more readable. Following are the best practices following while writing contracts with Solidity.

Code Layout

  • Indentation − Use 4 spaces instead of tab to maintain indentation level. Avoid mixing spaces with tabs.

  • Two Blank Lines Rule − Use 2 Blank lines between two contract definitions.

pragma solidity ^0.5.0;

contract LedgerBalance {
   //...
}
contract Updater {
   //...
}
  • One Blank Line Rule − Use 1 Blank line between two functions. In case of only declaration, no need to have blank lines.

pragma solidity ^0.5.0;

contract A {
   function balance() public pure;
   function account() public pure;
}
contract B is A {
   function balance() public pure {
      // ...
   }
   function account() public pure {
      // ...
   }
}
  • Maximum Line Length − A single line should not cross 79 characters so that readers can easily parse the code.

  • Wrapping rules − First argument be in new line without opening parenthesis. Use single indent per argument. Terminating element ); should be the last one.

function_with_a_long_name(
   longArgument1,
   longArgument2,
   longArgument3
);
variable = function_with_a_long_name(
   longArgument1,
   longArgument2,
   longArgument3
);
event multipleArguments(
   address sender,
   address recipient,
   uint256 publicKey,
   uint256 amount,
   bytes32[] options
);
MultipleArguments(
   sender,
   recipient,
   publicKey,
   amount,
   options
);
  • Source Code Encoding − UTF-8 or ASCII encoding is to be used preferably.

  • Imports − Import statements should be placed at the top of the file just after pragma declaration.

  • Order of Functions − Functions should be grouped as per their visibility.

pragma solidity ^0.5.0;

contract A {
   constructor() public {
      // ...
   }
   function() external {
      // ...
   }

   // External functions
   // ...

   // External view functions
   // ...

   // External pure functions 
   // ...

   // Public functions
   // ...

   // Internal functions
   // ...

   // Private functions
   // ...
}
  • Avoid extra whitespaces − Avoid whitespaces immediately inside parenthesis, brackets or braces.

  • Control structures − Braces should open on same line as declaration. Close on their own line maintaining the same indentation. Use a space with opening brace.

pragma solidity ^0.5.0;

contract Coin {
   struct Bank {
      address owner;
      uint balance;
   }
}
if (x < 3) {
   x += 1;
} else if (x > 7) {
   x -= 1;
} else {
   x = 5;
}
if (x < 3)
   x += 1;
else
   x -= 1;
  • Function Declaration − Use the above rule for braces. Always add a visibility label. Visibility label should come first before any custom modifier.

function kill() public onlyowner {
   selfdestruct(owner);
}
  • Mappings − Avoid whitespaces while declaring mapping variables.

mapping(uint => uint) map;
mapping(address => bool) registeredAddresses;
mapping(uint => mapping(bool => Data[])) public data;
mapping(uint => mapping(uint => s)) data;
  • Variable declaration − Avoid whitespaces while declaring array variables.

uint[] x;  // not unit [] x;
  • String declaration − Use double quotes to declare a string instead of single quote.

str = "foo";
str = "Hamlet says, 'To be or not to be...'";

Order of Layout

Elements should be layout in following order.

  • Pragma statements

  • Import statements

  • Interfaces

  • Libraries

  • Contracts

Within Interfaces, libraries or contracts the order should be as −

  • Type declarations

  • State variables

  • Events

  • Functions

Naming conventions

  • Contract and Library should be named using CapWords Style. For example, SmartContract, Owner etc.

  • Contract and Library name should match their file names.

  • In case of multiple contracts/libraries in a file, use name of core contract/library.

Owned.sol

pragma solidity ^0.5.0;

// Owned.sol
contract Owned {
   address public owner;
   constructor() public {
      owner = msg.sender;
   }
   modifier onlyOwner {
      //....
   }
   function transferOwnership(address newOwner) public onlyOwner {
      //...
   }
}

Congress.sol

pragma solidity ^0.5.0;

// Congress.sol
import "./Owned.sol";

contract Congress is Owned, TokenRecipient {
   //...
}
  • 構造体名

    −SmartCoinのようなCapWordsスタイルを使用します。

  • イベント名

    − Deposit、AfterTransferなどのCapWordsスタイルを使用します。

  • 関数名

    −initiateSupplyのようなmixedCaseスタイルを使用します。

  • ローカル変数と状態変数

    −creatorAddress、supplyのようなmixedCaseスタイルを使用します。

  • 定数

    − MAX_BLOCKSのような単語を区切るには、アンダースコア付きのすべて大文字を使用します。

  • 修飾子名

    −onlyAfterのようにmixCaseスタイルを使用します。

  • 列挙型の名前

    −TokenGroupのようなCapWordsスタイルを使用します。

関数は、プログラムのどこからでも呼び出すことができる再利用可能なコードのグループです。これにより、同じコードを何度も書く必要がなくなります。プログラマーがモジュラーコードを書くのに役立ちます。関数を使用すると、プログラマーは大きなプログラムをいくつかの小さくて管理しやすい関数に分割できます。

他の高度なプログラミング言語と同様に、Solidityは、関数を使用してモジュラーコードを記述するために必要なすべての機能もサポートしています。このセクションでは、Solidityで独自の関数を作成する方法について説明します。

関数定義

関数を使用する前に、それを定義する必要があります。Solidityで関数を定義する最も一般的な方法は、function キーワードの後に​​、一意の関数名、パラメーターのリスト(空の場合があります)、および中括弧で囲まれたステートメントブロックが続きます。

構文

基本的な構文を次に示します。

function function-name(parameter-list) scope returns() {
   //statements
}

次の例を試してください。パラメータをとらないgetResultという関数を定義します-

pragma solidity ^0.5.0;

contract Test {
   function getResult() public view returns(uint){
      uint a = 1; // local variable
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

関数の呼び出し

コントラクトの後半で関数を呼び出すには、次のコードに示すように、その関数の名前を記述する必要があります。

次のコードを試して、Solidityで文字列がどのように機能するかを理解してください。

pragma solidity ^0.5.0;

contract SolidityTest {   
   constructor() public{       
   }
   function getResult() public view returns(string memory){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {
      
      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;
      
      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;
      
      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);//access local variable
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

出力

0: string: 3

関数パラメーター

これまで、パラメーターのない関数を見てきました。ただし、関数の呼び出し中にさまざまなパラメーターを渡す機能があります。これらの渡されたパラメーターは関数内でキャプチャでき、これらのパラメーターに対して任意の操作を行うことができます。関数は、コンマで区切られた複数のパラメーターを取ることができます。

次の例を試してください。私たちは使用しましたuint2strここで機能します。1つのパラメータを取ります。

pragma solidity ^0.5.0;

contract SolidityTest {   
   constructor() public{       
   }
   function getResult() public view returns(string memory){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return integerToString(result); 
   }
   function integerToString(uint _i) internal pure 
      returns (string memory) {
      
      if (_i == 0) {
         return "0";
      }
      uint j = _i;
      uint len;
      
      while (j != 0) {
         len++;
         j /= 10;
      }
      bytes memory bstr = new bytes(len);
      uint k = len - 1;
      
      while (_i != 0) {
         bstr[k--] = byte(uint8(48 + _i % 10));
         _i /= 10;
      }
      return string(bstr);//access local variable
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

出力

0: string: 3

returnステートメント

Solidity関数にはオプションがあります returnステートメント。これは、関数から値を返したい場合に必要です。このステートメントは、関数の最後のステートメントである必要があります。

上記の例のように、uint2str関数を使用して文字列を返します。

Solidityでは、関数は複数の値を返すこともできます。以下の例を参照してください-

pragma solidity ^0.5.0;

contract Test {
   function getResult() public view returns(uint product, uint sum){
      uint a = 1; // local variable
      uint b = 2;
      product = a * b;
      sum = a + b;
  
      //alternative return statement to return 
      //multiple values
      //return(a*b, a+b);
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

出力

0: uint256: product 2
1: uint256: sum 3

関数修飾子は、関数の動作を変更するために使用されます。たとえば、関数に前提条件を追加します。

まず、パラメーターの有無にかかわらず修飾子を作成します。

contract Owner {
   modifier onlyOwner {
      require(msg.sender == owner);
      _;
   }
   modifier costs(uint price) {
      if (msg.value >= price) {
         _;
      }
   }
}

関数本体は、特殊記号「_;」の場所に挿入されます。修飾子の定義に表示されます。したがって、この関数の呼び出し中に修飾子の条件が満たされると、関数が実行され、それ以外の場合は例外がスローされます。

以下の例を参照してください-

pragma solidity ^0.5.0;

contract Owner {
   address owner;
   constructor() public {
      owner = msg.sender;
   }
   modifier onlyOwner {
      require(msg.sender == owner);
      _;
   }
   modifier costs(uint price) {
      if (msg.value >= price) {
         _;
      }
   }
}
contract Register is Owner {
   mapping (address => bool) registeredAddresses;
   uint price;
   constructor(uint initialPrice) public { price = initialPrice; }
   
   function register() public payable costs(price) {
      registeredAddresses[msg.sender] = true;
   }
   function changePrice(uint _price) public onlyOwner {
      price = _price;
   }
}

ビュー関数は、状態を変更しないことを保証します。関数は次のように宣言できますview。次のステートメントが関数に存在する場合、状態の変更と見なされ、コンパイラーはそのような場合に警告をスローします。

  • 状態変数の変更。

  • イベントの発行。

  • 他の契約の作成。

  • 自己破壊を使用します。

  • 通話によるEtherの送信。

  • ビューまたは純粋とマークされていない関数を呼び出す。

  • 低レベルの呼び出しを使用する。

  • 特定のオペコードを含むインラインアセンブリを使用する。

Getterメソッドは、デフォルトではビュー関数です。

ビュー機能を使用した以下の例を参照してください。

pragma solidity ^0.5.0;

contract Test {
   function getResult() public view returns(uint product, uint sum){
      uint a = 1; // local variable
      uint b = 2;
      product = a * b;
      sum = a + b; 
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

出力

0: uint256: product 2
1: uint256: sum 3

純粋関数は、状態を読み取ったり変更したりしないようにします。関数は次のように宣言できますpure。次のステートメントが関数に存在する場合、状態を読み取っていると見なされ、そのような場合、コンパイラーは警告をスローします。

  • 状態変数の読み取り。

  • address(this).balanceまたは<address> .balanceにアクセスします。

  • block、tx、msgの特別な変数のいずれかにアクセスします(msg.sigおよびmsg.dataを読み取ることができます)。

  • 純粋とマークされていない関数を呼び出す。

  • 特定のオペコードを含むインラインアセンブリを使用する。

純粋関数は、revert()関数とrequire()関数を使用して、エラーが発生した場合に潜在的な状態変化を元に戻すことができます。

ビュー機能を使用した以下の例を参照してください。

pragma solidity ^0.5.0;

contract Test {
   function getResult() public pure returns(uint product, uint sum){
      uint a = 1; 
      uint b = 2;
      product = a * b;
      sum = a + b; 
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

出力

0: uint256: product 2
1: uint256: sum 3

フォールバック機能は、契約で利用できる特別な機能です。以下の特徴があります-

  • コントラクトで存在しない関数が呼び出されたときに呼び出されます。

  • 外部としてマークする必要があります。

  • 名前はありません。

  • 引数はありません

  • 何も返却できません。

  • 契約ごとに1つ定義できます。

  • 支払い可能とマークされていない場合、契約がデータのないプレーンエーテルを受信すると、例外がスローされます。

次の例は、契約ごとのフォールバック関数の概念を示しています。

pragma solidity ^0.5.0;

contract Test {
   uint public x ;
   function() external { x = 1; }    
}
contract Sink {
   function() external payable { }
}
contract Caller {
   function callTest(Test test) public returns (bool) {
      (bool success,) = address(test).call(abi.encodeWithSignature("nonExistingFunction()"));
      require(success);
      // test.x is now 1

      address payable testPayable = address(uint160(address(test)));

      // Sending ether to Test contract,
      // the transfer will fail, i.e. this returns false here.
      return (testPayable.send(2 ether));
   }
   function callSink(Sink sink) public returns (bool) {
      address payable sinkPayable = address(sink);
      return (sinkPayable.send(2 ether));
   }
}

同じスコープ内の同じ関数名に対して複数の定義を持つことができます。関数の定義は、引数リスト内の引数のタイプや数によって互いに異なる必要があります。戻り値の型のみが異なる関数宣言をオーバーロードすることはできません。

次の例は、Solidityでの関数のオーバーロードの概念を示しています。

pragma solidity ^0.5.0;

contract Test {
   function getSum(uint a, uint b) public pure returns(uint){      
      return a + b;
   }
   function getSum(uint a, uint b, uint c) public pure returns(uint){      
      return a + b + c;
   }
   function callSumWithTwoArguments() public pure returns(uint){
      return getSum(1,2);
   }
   function callSumWithThreeArguments() public pure returns(uint){
      return getSum(1,2,3);
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

最初にcallSumWithTwoArgumentsボタンをクリックしてから、callSumWithThreeArgumentsボタンをクリックして結果を確認します。

出力

0: uint256: 3
0: uint256: 6

Solidityは、組み込みの数学関数も提供します。以下は頻繁に使用される方法です-

  • addmod(uint x, uint y, uint k) returns (uint)-添加は任意の精度で実行され、2でラップアラウンドしない計算する(X + Y)%のK 256

  • mulmod(uint x, uint y, uint k) returns (uint)-添加は任意の精度で実行され、2でラップアラウンドしない計算する(X * Y)%のK 256

次の例は、Solidityでの数学関数の使用法を示しています。

pragma solidity ^0.5.0;

contract Test {   
   function callAddMod() public pure returns(uint){
      return addmod(4, 5, 3);
   }
   function callMulMod() public pure returns(uint){
      return mulmod(4, 5, 3);
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

最初にcallAddModボタンをクリックしてから、callMulModボタンをクリックして結果を確認します。

出力

0: uint256: 0
0: uint256: 2

Solidityは、組み込みの暗号化機能も提供します。以下は重要な方法です-

  • keccak256(bytes memory) returns (bytes32) −入力のKeccak-256ハッシュを計算します。

  • sha256(bytes memory) returns (bytes32) −入力のSHA-256ハッシュを計算します。

  • ripemd160(bytes memory) returns (bytes20) −入力のRIPEMD-160ハッシュを計算します。

  • sha256(bytes memory) returns (bytes32) −入力のSHA-256ハッシュを計算します。

  • ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)−公開鍵に関連付けられたアドレスを楕円曲線署名から回復するか、エラー時にゼロを返します。関数パラメーターは、署名のECDSA値に対応します。r-署名の最初の32バイト。s:2番目の32バイトの署名。v:署名の最後の1バイト。このメソッドはアドレスを返します。

次の例は、Solidityでの暗号化機能の使用法を示しています。

pragma solidity ^0.5.0;

contract Test {   
   function callKeccak256() public pure returns(bytes32 result){
      return keccak256("ABC");
   }  
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

出力

0: bytes32: result 0xe1629b9dda060bb30c7908346f6af189c16773fa148d3366701fbaa35d54f3c8

引き出しパターンにより、セキュリティ上の脅威となる直接転送呼び出しが行われないことが保証されます。次の契約は、エーテルを送信するための転送呼び出しの安全でない使用を示しています。

pragma solidity ^0.5.0;

contract Test {
   address payable public richest;
   uint public mostSent;

   constructor() public payable {
      richest = msg.sender;
      mostSent = msg.value;
   }
   function becomeRichest() public payable returns (bool) {
      if (msg.value > mostSent) {
         // Insecure practice
         richest.transfer(msg.value);
         richest = msg.sender;
         mostSent = msg.value;
         return true;
      } else {
         return false;
      }
   }
}

上記のコントラクトは、最もリッチなものをフォールバック機能の失敗のコントラクトにすることにより、使用できない状態にすることができます。フォールバック関数が失敗すると、becomeRichest()関数も失敗し、コントラクトは永久にスタックします。この問題を軽減するために、引き出しパターンを使用できます。

引き出しパターンでは、各送金の前に保留金額をリセットします。これにより、発信者契約のみが失敗することが保証されます。

pragma solidity ^0.5.0;

contract Test {
   address public richest;
   uint public mostSent;

   mapping (address => uint) pendingWithdrawals;

   constructor() public payable {
      richest = msg.sender;
      mostSent = msg.value;
   }
   function becomeRichest() public payable returns (bool) {
      if (msg.value > mostSent) {
         pendingWithdrawals[richest] += msg.value;
         richest = msg.sender;
         mostSent = msg.value;
         return true;
      } else {
         return false;
      }
   }
   function withdraw() public {
      uint amount = pendingWithdrawals[msg.sender];
      pendingWithdrawals[msg.sender] = 0;
      msg.sender.transfer(amount);
   }
}

契約へのアクセス制限は一般的な方法です。デフォルトでは、コントラクト状態は、パブリックとして指定されていない限り、読み取り専用です。

コントラクトの状態を変更できるユーザーを制限したり、修飾子を使用してコントラクトの関数を呼び出すことができます。以下で説明するように、複数の修飾子を作成して使用します-

  • onlyBy −関数で使用されると、言及された呼び出し元のみがこの関数を呼び出すことができます。

  • onlyAfter −関数で使用されると、その関数は一定期間後に呼び出すことができます。

  • costs −関数で使用されると、呼び出し元は特定の値が指定されている場合にのみこの関数を呼び出すことができます。

pragma solidity ^0.5.0;

contract Test {
   address public owner = msg.sender;
   uint public creationTime = now;

   modifier onlyBy(address _account) {
      require(
         msg.sender == _account,
         "Sender not authorized."
      );
      _;
   }
   function changeOwner(address _newOwner) public onlyBy(owner) {
      owner = _newOwner;
   }
   modifier onlyAfter(uint _time) {
      require(
         now >= _time,
         "Function called too early."
      );
      _;
   }
   function disown() public onlyBy(owner) onlyAfter(creationTime + 6 weeks) {
      delete owner;
   }
   modifier costs(uint _amount) {
      require(
         msg.value >= _amount,
         "Not enough Ether provided."
      );
      _;
      if (msg.value > _amount)
         msg.sender.transfer(msg.value - _amount);
   }
   function forceOwnerChange(address _newOwner) public payable costs(200 ether) {
      owner = _newOwner;
      if (uint(owner) & 0 == 1) return;        
   }
}

Solidityのコントラクトは、C ++のクラスに似ています。コントラクトには次のプロパティがあります。

  • Constructor −コンストラクタキーワードで宣言された特別な関数で、コントラクトごとに1回実行され、コントラクトの作成時に呼び出されます。

  • State Variables −契約の状態を格納するための契約ごとの変数。

  • Functions −状態変数を変更してコントラクトの状態を変更できる、コントラクトごとの関数。

可視性定量化子

以下は、コントラクトの関数/状態変数のさまざまな可視性定量化子です。

  • external−外部関数は、他のコントラクトによって呼び出されることを意図しています。内線通話には使用できません。コントラクト内で外部関数を呼び出すには、this.function_name()呼び出しが必要です。状態変数を外部としてマークすることはできません。

  • public−パブリック関数/変数は外部と内部の両方で使用できます。パブリック状態変数の場合、Solidityは自動的にゲッター関数を作成します。

  • internal −内部関数/変数は、内部または派生コントラクトによってのみ使用できます。

  • private −プライベート関数/変数は内部でのみ使用でき、派生コントラクトによっても使用できません。

pragma solidity ^0.5.0;

contract C {
   //private state variable
   uint private data;
   
   //public state variable
   uint public info;

   //constructor
   constructor() public {
      info = 10;
   }
   //private function
   function increment(uint a) private pure returns(uint) { return a + 1; }
   
   //public function
   function updateData(uint a) public { data = a; }
   function getData() public view returns(uint) { return data; }
   function compute(uint a, uint b) internal pure returns (uint) { return a + b; }
}
//External Contract
contract D {
   function readData() public returns(uint) {
      C c = new C();
      c.updateData(7);         
      return c.getData();
   }
}
//Derived Contract
contract E is C {
   uint private result;
   C private c;
   
   constructor() public {
      c = new C();
   }  
   function getComputedResult() public {      
      result = compute(3, 5); 
   }
   function getResult() public view returns(uint) { return result; }
   function getData() public view returns(uint) { return c.info(); }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。コントラクトのさまざまな方法を実行します。E.getComputedResult()の後にE.getResult()が続く場合、次のようになります。

出力

0: uint256: 8

継承は、コントラクトの機能を拡張する方法です。Solidityは、単一継承と多重継承の両方をサポートします。以下は主要なハイライトです。

  • 派生コントラクトは、内部メソッドや状態変数を含むすべての非プライベートメンバーにアクセスできます。ただし、これを使用することは許可されていません。

  • 関数のシグネチャが同じままであれば、関数のオーバーライドが許可されます。出力パラメータが異なる場合、コンパイルは失敗します。

  • スーパーキーワードまたはスーパーコントラクト名を使用して、スーパーコントラクトの関数を呼び出すことができます。

  • 多重継承の場合、superを使用した関数呼び出しは、ほとんどの派生コントラクトを優先します。

pragma solidity ^0.5.0;

contract C {
   //private state variable
   uint private data;
   
   //public state variable
   uint public info;

   //constructor
   constructor() public {
      info = 10;
   }
   //private function
   function increment(uint a) private pure returns(uint) { return a + 1; }
   
   //public function
   function updateData(uint a) public { data = a; }
   function getData() public view returns(uint) { return data; }
   function compute(uint a, uint b) internal pure returns (uint) { return a + b; }
}
//Derived Contract
contract E is C {
   uint private result;
   C private c;
   constructor() public {
      c = new C();
   }  
   function getComputedResult() public {      
      result = compute(3, 5); 
   }
   function getResult() public view returns(uint) { return result; }
   function getData() public view returns(uint) { return c.info(); }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。コントラクトのさまざまな方法を実行します。E.getComputedResult()の後にE.getResult()が続く場合、次のようになります。

出力

0: uint256: 8

コンストラクターは、を使用して宣言された特別な関数です。 constructorキーワード。これはオプションの機能であり、コントラクトの状態変数を初期化するために使用されます。コンストラクターの主な特徴は次のとおりです。

  • コントラクトはコンストラクターを1つだけ持つことができます。

  • コンストラクタコードは、コントラクトの作成時に1回実行され、コントラクトの状態を初期化するために使用されます。

  • コンストラクターコードが実行された後、最終的なコードがブロックチェーンにデプロイされます。このコードには、パブリック関数とパブリック関数を介して到達可能なコードが含まれます。コンストラクターコードまたはコンストラクターのみが使用する内部メソッドは、最終的なコードには含まれません。

  • コンストラクターは、パブリックまたは内部のいずれかです。

  • 内部コンストラクターは、コントラクトを抽象としてマークします。

  • コンストラクターが定義されていない場合、デフォルトのコンストラクターがコントラクトに存在します。

pragma solidity ^0.5.0;

contract Test {
   constructor() public {}
}
  • 基本コントラクトに引数付きのコンストラクターがある場合、派生した各コントラクトはそれらを渡す必要があります。

  • 基本コンストラクターは、次の方法を使用して直接初期化できます-

pragma solidity ^0.5.0;

contract Base {
   uint data;
   constructor(uint _data) public {
      data = _data;   
   }
}
contract Derived is Base (5) {
   constructor() public {}
}
  • 基本コンストラクターは、次の方法を使用して間接的に初期化できます。

pragma solidity ^0.5.0;

contract Base {
   uint data;
   constructor(uint _data) public {
      data = _data;   
   }
}
contract Derived is Base {
   constructor(uint _info) Base(_info * _info) public {}
}
  • 基本コントラクトコンストラクターを初期化する直接および間接の方法は許可されていません。

  • 派生コントラクトが基本コントラクトコンストラクターに引数を渡していない場合、派生コントラクトは抽象になります。

抽象コントラクトは、実装なしで少なくとも1つの関数を含むものです。このような契約は基本契約として使用されます。一般に、抽象コントラクトには、実装された関数と抽象関数の両方が含まれます。派生コントラクトは、抽象関数を実装し、必要に応じて既存の関数を使用します。

派生コントラクトが抽象関数を実装していない場合、この派生コントラクトは抽象としてマークされます。

次のコードを試して、Solidityで抽象コントラクトがどのように機能するかを理解してください。

pragma solidity ^0.5.0;

contract Calculator {
   function getResult() public view returns(uint);
}
contract Test is Calculator {
   function getResult() public view returns(uint) {
      uint a = 1;
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

出力

0: uint256: 3

インターフェースは抽象コントラクトに似ており、を使用して作成されます interfaceキーワード。以下は、インターフェースの主な特徴です。

  • インターフェースは、実装によって機能を持つことはできません。

  • インターフェイスの機能は、外部タイプのみにすることができます。

  • インターフェイスにコンストラクタを含めることはできません。

  • インターフェイスは状態変数を持つことができません。

  • インターフェイスには、インターフェイス名のドット表記を使用してアクセスできる列挙型、構造体を含めることができます。

次のコードを試して、Solidityでインターフェイスがどのように機能するかを理解してください。

pragma solidity ^0.5.0;

interface Calculator {
   function getResult() external view returns(uint);
}
contract Test is Calculator {
   constructor() public {}
   function getResult() external view returns(uint){
      uint a = 1; 
      uint b = 2;
      uint result = a + b;
      return result;
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

Note −展開ボタンをクリックする前に、ドロップダウンから[テスト]を選択します。

出力

0: uint256: 3

ライブラリはコントラクトに似ていますが、主に再利用を目的としています。ライブラリには、他のコントラクトが呼び出すことができる関数が含まれています。Solidityには、ライブラリの使用に一定の制限があります。SolidityLibraryの主な特徴は次のとおりです。

  • ライブラリ関数は、状態を変更しない場合は直接呼び出すことができます。つまり、純粋な関数またはビュー関数は、ライブラリの外部からのみ呼び出すことができます。

  • ライブラリはステートレスであると想定されているため、破棄することはできません。

  • ライブラリに状態変数を含めることはできません。

  • ライブラリは要素を継承できません。

  • ライブラリは継承できません。

次のコードを試して、Solidityでライブラリがどのように機能するかを理解してください。

pragma solidity ^0.5.0;

library Search {
   function indexOf(uint[] storage self, uint value) public view returns (uint) {
      for (uint i = 0; i < self.length; i++) if (self[i] == value) return i;
      return uint(-1);
   }
}
contract Test {
   uint[] data;
   constructor() public {
      data.push(1);
      data.push(2);
      data.push(3);
      data.push(4);
      data.push(5);
   }
   function isValuePresent() external view returns(uint){
      uint value = 4;
      
      //search if value is present in the array using Library function
      uint index = Search.indexOf(data, value);
      return index;
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

Note −展開ボタンをクリックする前に、ドロップダウンから[テスト]を選択します。

出力

0: uint256: 3

Forの使用

ディレクティブ using A for B; ライブラリAのライブラリ関数を特定のタイプBにアタッチするために使用できます。これらの関数は、呼び出し元のタイプを最初のパラメーターとして使用します(selfを使用して識別されます)。

次のコードを試して、Solidityでライブラリがどのように機能するかを理解してください。

pragma solidity ^0.5.0;

library Search {
   function indexOf(uint[] storage self, uint value) public view returns (uint) {
      for (uint i = 0; i < self.length; i++)if (self[i] == value) return i;
      return uint(-1);
   }
}
contract Test {
   using Search for uint[];
   uint[] data;
   constructor() public {
      data.push(1);
      data.push(2);
      data.push(3);
      data.push(4);
      data.push(5);
   }
   function isValuePresent() external view returns(uint){
      uint value = 4;      
      
      //Now data is representing the Library
      uint index = data.indexOf(value);
      return index;
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

Note −展開ボタンをクリックする前に、ドロップダウンから[テスト]を選択します。

出力

0: uint256: 3

Solidityは、アセンブリ言語を使用してSolidityソースコード内にインラインアセンブリを記述するオプションを提供します。スタンドアロンのアセンブリコードを記述して、バイトコードに変換することもできます。スタンドアロンアセンブリは、Solidityコンパイラの中間言語であり、Solidityコードをスタンドアロンアセンブリに変換してからバイトコードに変換します。インラインアセンブリで使用されているのと同じ言語を使用して、スタンドアロンアセンブリでコードを記述できます。

インラインアセンブリ

インラインアセンブリコードは、Solidityコードベース内でインターリーブして、EVMをよりきめ細かく制御でき、特にライブラリ関数の作成時に使用されます。

アセンブリコードは assembly { ... } ブロック。

次のコードを試して、Solidityでライブラリがどのように機能するかを理解してください。

pragma solidity ^0.5.0;

library Sum {   
   function sumUsingInlineAssembly(uint[] memory _data) public pure returns (uint o_sum) {
      for (uint i = 0; i < _data.length; ++i) {
         assembly {
            o_sum := add(o_sum, mload(add(add(_data, 0x20), mul(i, 0x20))))
         }
      }
   }
}
contract Test {
   uint[] data;
   
   constructor() public {
      data.push(1);
      data.push(2);
      data.push(3);
      data.push(4);
      data.push(5);
   }
   function sum() external view returns(uint){      
      return Sum.sumUsingInlineAssembly(data);
   }
}

Solidity First Applicationの章に記載されている手順を使用して、上記のプログラムを実行します。

Note −展開ボタンをクリックする前に、ドロップダウンから[テスト]を選択します。

出力

0: uint256: 15

イベントは、コントラクトの継承可能なメンバーです。イベントが発行され、渡された引数がトランザクションログに保存されます。これらのログはブロックチェーンに保存され、契約がブロックチェーンに存在するまで、契約のアドレスを使用してアクセスできます。生成されたイベントは、それらを作成して発行したものでさえも、コントラクト内からアクセスできません。

イベントは、eventキーワードを使用して宣言できます。

//Declare an Event
event Deposit(address indexed _from, bytes32 indexed _id, uint _value);

//Emit an event
emit Deposit(msg.sender, _id, msg.value);

次のコードを試して、Solidityでイベントがどのように機能するかを理解してください。

まず、コントラクトを作成し、イベントを発行します。

pragma solidity ^0.5.0;

contract Test {
   event Deposit(address indexed _from, bytes32 indexed _id, uint _value);
   function deposit(bytes32 _id) public payable {      
      emit Deposit(msg.sender, _id, msg.value);
   }
}

次に、JavaScriptコードでコントラクトのイベントにアクセスします。

var abi = /* abi as generated using compiler */;
var ClientReceipt = web3.eth.contract(abi);
var clientReceiptContract = ClientReceipt.at("0x1234...ab67" /* address */);

var event = clientReceiptContract.Deposit(function(error, result) {
   if (!error)console.log(result);
});

次のような詳細を出力する必要があります-

出力

{
   "returnValues": {
      "_from": "0x1111...FFFFCCCC",
      "_id": "0x50...sd5adb20",
      "_value": "0x420042"
   },
   "raw": {
      "data": "0x7f...91385",
      "topics": ["0xfd4...b4ead7", "0x7f...1a91385"]
   }
}

Solidityは、エラー処理のためのさまざまな機能を提供します。通常、エラーが発生すると、状態は元の状態に戻ります。その他のチェックは、不正なコードアクセスを防ぐことです。以下は、エラー処理で使用される重要な方法のいくつかです-

  • assert(bool condition)−条件が満たされない場合、このメソッド呼び出しにより無効なオペコードが発生し、状態に対して行われた変更はすべて元に戻されます。このメソッドは、内部エラーに使用されます。

  • require(bool condition)−条件が満たされない場合、このメソッド呼び出しは元の状態に戻ります。-この方法は、入力または外部コンポーネントのエラーに使用されます。

  • require(bool condition, string memory message)−条件が満たされない場合、このメソッド呼び出しは元の状態に戻ります。-この方法は、入力または外部コンポーネントのエラーに使用されます。カスタムメッセージを提供するオプションを提供します。

  • revert() −このメソッドは実行を中止し、状態に対して行われた変更を元に戻します。

  • revert(string memory reason)−このメソッドは実行を中止し、状態に対して行われた変更を元に戻します。カスタムメッセージを提供するオプションを提供します。

次のコードを試して、Solidityでエラー処理がどのように機能するかを理解してください。

pragma solidity ^0.5.0;

contract Vendor {
   address public seller;
   modifier onlySeller() {
      require(
         msg.sender == seller,
         "Only seller can call this."
      );
      _;
   }
   function sell(uint amount) public payable onlySeller { 
      if (amount > msg.value / 2 ether)
         revert("Not enough Ether provided.");
      // Perform the sell operation.
   }
}

revertが呼び出されると、次のように16進データが返されます。

出力

0x08c379a0                     // Function selector for Error(string)
0x0000000000000000000000000000000000000000000000000000000000000020 // Data offset
0x000000000000000000000000000000000000000000000000000000000000001a // String length
0x4e6f7420656e6f7567682045746865722070726f76696465642e000000000000 // String data