solidity コントラクトとクラス

【ブロックチェーン プログラミング】solidity contract クラスと同じと思え!

  • 2023年7月22日
  • IT
IT solidity コントラクトとクラス

今回はcontractの作成から継承までを解説

すこしでもプログラミングに触れている人なら

「クラスと一緒で、C言語に近い継承を持っている」

と伝えると、だいたい言語仕様が理解できるでしょう。

ではcontract(クラス)について理解しましょう。


Version Pragma


ちょっとcontractの話の前に

Version Pragmaについて理解をしておこう

Version Pragmaは

コンパイラを指定するわけではないです。

あくまで、これは注釈であり

また、指定してもコンパイラを変更できません。

Pragmaを指定することで、記入したバージョンでのチェックが動きます。

現在想定したコンパイラで実装し、それが、シンタックスなどの内容が間違っていないかを

チェックしてくれます。

下記を例にすると

pragma solidity >= 0.4.0 < 0.7.0;
	contract testContract { 
}

Solidity の 0.4.0 以上、0.7.0 未満のバージョンとの互換性があるソースである

Solidity の 0.4.0 以上、0.7.0 未満のバージョンでのコーディングでのエラーが

ないかチェックせよ・・・という意味になる

コンパイル指定ではない、ことを理解しておこう


インスタンス生成


solidityにおいてのインスタンス(contract)生成方法は

各種言語のクラス生成と同じで

newキーワードを利用してインスタンスを作成(デプロイ)します

コントラクト contract(インスタンス)生成 new キーワード

先にも述べたが

コントラクト(インスタンス)の生成には

newキーワードを使って生成する

このコンストラクト作成には

GASが必要になるのだが・・・その内容はまた別の機会で説明する


solidity:コンストラクタ


コンストラクタとは

インスタンスが生成される場合に

初回だけ実施されるメソッドである

また、コンストラクタが宣言されていない場合には

自動でデフォルトコンストラクタが作成される

通常の言語(javaなど)ではコンストラクタは

インスタンスの生成時に毎回実行されるが

solidityは他の言語と違い、コンストラクタの動きが違うことを覚えておこう

solidityのコンストラクタは、デプロイ時に一度だけ実行される

solidityのコンストラクタは、インスタンス生成時に「毎回実行されない」


一般的なインスタンスの生成の違いについて理解する


contract インスタンス作成での違い

solidityでのインスタンス作成は

通常の言語とは動きが違う

これにはブロックチェーンが関わってくる

そもそもsolidityはブロックチェーンかつ、スマートコントラクトの言語である

いままでのプログラミングであるオブジェクト思考でのプログラミングと概念が違う

コントラクトの作成=インスタンス生成には、デプロイといった動きが必ず発生することを理解して欲しい

いままでの言語のように、メモリ空間上にインスタンス化し再利用する物とは違う

インスタンスの作成にはブロックチェーンにデプロイするため

GASも発生する


solidity:継承 is


solidityでも他の言語同様に継承ができる

is キーワードを使って継承する

継承では

  • 単一継承
  • 多重継承

の両方が可能

単一継承

下記のような単一継承が可能

contract objectA {
  // 実装内容・・・
}
contract objectB is objectA {
  // 実装内容・・・
}

これは単純な継承です

contract objectA {
  // 実装内容・・・
}
contract objectB is objectA {
  // 実装内容・・・
}
contract objectC is objectA {
  // 実装内容・・・
}

これはobjectAを基底contractとして、派生したobjectB、objectCの

contractを作成しています。

contract objectA {
  // 実装内容・・・
}
contract objectB is objectA {
  // 実装内容・・・
}
contract objectC is objectB {
  // 実装内容・・・
}

こちらの継承もできるが

少し複雑になるため

あまり、使わないけど、こういうことも可能です。

多重継承

contract objectA {
  // 実装内容・・・
}
contract objectB is objectA {
  // 実装内容・・・
}
contract objectC is objectA {
  // 実装内容・・・
}
contract objectD is objectA, objectB, objectC {
  // 実装内容・・・
}

javaを主に使っていた私からすると

あまり利用したくない機能です

javaは単一継承しか許されていない・・・

注意点として、

objectDはobjectAも継承しているところですかね

私はあまり慣れないです・・・・


solidity:抽象コントラクト abstract


抽象コントラクト(contract)とは、抽象であるため

実態を持たない(インスタンスの作成ができない)

抽象コントラクトを利用するには

利用するコントラクト側で継承する必要がある

また、抽象コントラクトで指定した

メソッドは、継承側のコントラクトで

メソッドの実装が強制される

現在のsolidityのバージョンではabstractのキーワードの指定は

必要なく

実装を持たない、メソッドのみのcontractを作成すれば良い

pragma solidity >=0.4.16 <0.9.0;

//抽象contractでは、メソッドの型のみ宣言する
contract AbstractContract {
   function getValue() public view returns(uint);
}

//実態側で抽象contractを継承し、指定されたメソッドの実態を作成する
contract Calculator_implementation is AbstractContract {
 
   function getValue() public view returns(uint) {
      uint calcObjA = 10;
      uint calcObjB = 20;
      uint retValue = calcObjA * calcObjB;
      return retValue;
   }
}

solidity:インターフェース interface


抽象コントラクトと似ているが

変数を持つことができず

メソッドの宣言のみのコントラクト


利用には利用側のコントラクトで継承する

利用側のコントラクトでは

メソッドの実態の作成が強制される


特徴として

  • 関数を実装することはできない
  • 他のインターフェースから継承できる
  • 宣言されたメソッドはすべてexternalでないといけない
  • コンストラクターを作成できない
  • 状態変数(ステート変数)を保持できない

コントラクト内でで宣言されているが、どのメソッドからも利用されていない変数のこと
javaでのstaticなグローバル変数みたいな物です。
ただし、storegeというブロックチェーン上に格納され、ブロックチェーン上で利用することから、GASも高くなる要因の一つ

pragma solidity ^0.8.0;

interface Calculator {
   function getResult() external view returns(uint);
}
//インターフェースを継承
contract Calculator_implementation is Calculator {
//インターフェースで宣言されたメソッドの実態を実装する
   function getResult() external view returns(uint){

      uint calcObjA = 100; 
      uint calcObjB = 5;
      uint result = a % b;
      return result;

   }
}

抽象コントラクト(contract またはクラス)とインターフェースの違いとは?


ちょっと、プログラミングで理解できていない人もいるため

抽象コントラクト(contract またはクラス)とインターフェースの違いに触れよう

抽象コントラクト

抽象コントラクトは

is-aの関係の物を示すために使います。

「is-aの関係」といっても

ピンとこないですね

抽象的なものを表現することから抽象コントラクトなのですが

人も、犬も、猫も、動物で表せます。

トラック、セダン、RVは車で表せますね

言い換えると

人、犬、猫 → 動物 である(is-a)

抽象として表した場合に「動物」の一括りで表せます

トラック、セダン、RV → 車 である(is-a)

抽象として表した場合に「車」の一括りで表せます

インターフェース

インターフェースとは、振る舞いのみを宣言したコントラクトであり

抽象コントラクトと対比して話をすると

異なった物の振る舞いのみでグルーピングすることが可能になる

元々は違った振る舞いをグループにまとめ

多重継承のためにインターフェースを使う場合が多い


solidity:ライブラリ library


solidityには再利用可能であるが、状態を持たない、contractがある

libraryキーワードで宣言する

javaでいうstaticクラス的なものですね

ちょうどいい例、いや、ライブラリがあります

StringUtil.solです

こちらのソースを確認すればわかりますが

文字列関連の各種処理をライブラリとして作成しています。

//https://github.com/Arachnid/solidity-stringutils/blob/master/src/strings.sol のソースから
//抜粋
pragma solidity ^0.8.0;

//libraryキーワードを使用して、ライブラリであることを宣言

library strings {
    struct slice {
        uint _len;
        uint _ptr;
    }

    function memcpy(uint dest, uint src, uint len) private pure {
        // Copy word-length chunks while possible
        for(; len >= 32; len -= 32) {
            assembly {
                mstore(dest, mload(src))
            }
            dest += 32;
            src += 32;
        }

        // Copy remaining bytes
        uint mask = type(uint).max;
        if (len > 0) {
            mask = 256 ** (32 - len) - 1;
        }
        assembly {
            let srcpart := and(mload(src), not(mask))
            let destpart := and(mload(dest), mask)
            mstore(dest, or(destpart, srcpart))
        }
    }
//以下省略

使い方は

using キーワードを使い使いたいデータに対して指定する

pragma solidity ^0.8.0;

import 'ds-test/test.sol';
//ライブラリをインポート
import './strings.sol';

contract StringsTest is DSTest {
//利用したいデータに対して、指定する、今回は全て
	using strings for *;


    function abs(int x) private pure returns (int) {
        if(x < 0)
            return -x;
        return x;
    }

    function sign(int x) private pure returns (int) {
        return x == 0 ? int(0) : (x < 0 ? -1 : int(1));
    }

    function assertEq0(string memory a, string memory b) internal {
        assertEq0(bytes(a), bytes(b));
    }

    function assertEq0(strings.slice memory a, strings.slice memory b) internal {
    	assertEq0(a.toString(), b.toString());
    }

    function assertEq0(strings.slice memory a, string memory b) internal {
        assertEq0(a.toString(), b);
    }

	function testSliceToString() public {
		string memory test = "Hello, world!";
		assertEq0(test, test.toSlice().toString());
	}

    function testBytes32Len() public {
        bytes32 test;
        for(uint i = 0; i <= 32; i++) {
            assertEq(i, test.len());
            test = bytes32((uint(test) / 0x100) | 0x2000000000000000000000000000000000000000000000000000000000000000);
        }
    }
//以下省略

まとめ


solidtyのcontractは他の言語のクラスに近いと説明した

しかし、他の言語と大きく違うのは

ブロックチェーン上での管理されるもの、また、メモリ上に利用する物

インスタンス化にはブロックチェーン上に

デプロイが伴うことなど

いくつかの違いがあり

WEB3.0  = ブロックチェーン

を理解する上での、難しいところであり

いままでの言語とは少し違うことを理解しないといけない

また、現在最新の書籍を見つけることは難しく

WEBでの情報が全てなので

その点でも理解しにくい部分が多い

それ以外にも、GASの計算も必要になるため

何かと初心者には難しいが

だからこそ、今後のWEB3.0の技術者が必要になった場合

(確実に足りなくなると思うが・・・)

少しでも役立てて欲しい。

わたしもまだ勉強中ではあるが

できるだけ詳しく説明できれば(初心者にも)

と思う

solidity コントラクトとクラス
最新情報をチェックしよう!