Solang (Solidity)で始めるSolanaスマートコントラクト開発
SolangはSolidity v0.8互換のSolana、Polkadot向けに拡張されたコンパイラです。
こちらを使うことでSolidityを使ってSolanaのプログラム(スマートコントラクト)の生成を行えるため、EVMでの開発に慣れている方は比較的容易にSolanaの開発をできるようになっています。
Solanaのプログラム向けにコンパイルするさいに、通常のSolidityと違う点がいくつかあるため詳細については上記を参照してください。
インストール
$ brew install hyperledger/solang/solang
$ solang --help
Solang Solidity Compiler
Usage: solang <COMMAND>
Commands:
compile Compile Solidity source files
doc Generate documention for contracts using doc comments
shell-complete Print shell completion for various shells to STDOUT
language-server Start LSP language server on stdin/stdout
idl Generate Solidity interface files from Anchor IDL files
new Create a new Solang project
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
-V, --version Print version
SolangはMacOS環境ではbrew
を使ってインストール可能です。
他環境向けのインストールではリンク先を参照してください。
Solana公式サイトのGetting Startedではsolana-cli、anchorのインストールが求められますが、これはanchorのSolangサポートを利用しているためです。
プロジェクトの作成
$ solang new --target solana my_project
$ tree -r
.
└── my_project
├── flipper.sol
└── solang.toml
2 directories, 2 files
solang new
コマンドで新しいプロジェクトを作成できます。
[package]
version = "0.1.0"
# Source files to be compiled.
input_files = ["flipper.sol"]
# Contracts to be compiled.
# If no contracts are specified, solang will compile all non-virtual contracts.
contracts = ["flipper"]
# Specify required import paths.
import_path = []
# Define any importmaps.
# import_map = { "@openzeppelin" = "/home/user/libraries/openzeppelin-contracts/" }
import_map = {}
[target]
name = "solana"
[debug-features]
# Log debug prints to the environment.
prints = true
# Log runtime errors to the environment.
log-runtime-errors = true
# Add debug info to the generated llvm IR.
generate-debug-info = false
[optimizations]
dead-storage = true
constant-folding = true
strength-reduce = true
vector-to-slice = true
common-subexpression-elimination = true
# Valid LLVM optimization levels are: none, less, default, aggressive
llvm-IR-optimization-level = "aggressive"
[compiler-output]
verbose = false
# Emit compiler state at early stages. Valid options are: ast-dot, cfg, llvm-ir, llvm-bc, object, asm
# emit = "llvm-ir"
# Output directory for binary artifacts.
# output_directory = "path/to/dir"
# Output directory for the metadata.
# output_meta = "path/to/dir"
# Output everything in a JSON format on STDOUT instead of writing output files.
std_json_output = false
contract flipper {
bool private value;
/// Constructor that initializes the `bool` value to the given `init_value`.
@payer(payer)
constructor(bool initvalue) {
value = initvalue;
}
/// A message that can be called on instantiated contracts.
/// This one flips the value of the stored `bool` from `true`
/// to `false` and vice versa.
function flip() public {
value = !value;
}
/// Simply returns the current value of our `bool`.
function get() public view returns (bool) {
return value;
}
}
Anchor IDLからのSolidityコードの生成
SolangではanchorのIDLからのSolidityコードの生成もサポートされています。
$ solang idl account_data.json
account_data.json: info: creating 'account_data.sol'
struct AddressInfo {
string name;
uint8 houseNumber;
string street;
string city;
}
@program_id("F1ipperKF9EfD821ZbbYjS319LXYiBmjhzkkf5a26rC")
interface account_data {
@selector([0x87,0x2c,0xcd,0xc6,0x19,0x01,0x48,0xbc])
function initialize(uint16 space,string name,uint8 housenumber,string street,string city) external;
@selector([0xa1,0xe0,0x32,0x3d,0x05,0xd2,0x7a,0xd8])
function get() view external returns (AddressInfo);
@selector([0xa5,0x09,0x3b,0xf0,0x30,0x77,0xd7,0x73])
function getAddressInfoSize() view external returns (uint256);
}
IDLの元は下記のコードになります。
コンパイル
コンパイルを実行するとsolanaのプログラムファイルである.so
と、anchorのIDLが生成されます。
$ solang compile
$ ls -la
total 240
drwxr-xr-x 6 ab staff 192 10 29 12:06 .
drwxr-xr-x 7 ab staff 224 10 29 11:55 ..
-rw-r--r-- 1 ab staff 1494 10 29 12:06 flipper.json
-rw-r--r-- 1 ab staff 109312 10 29 12:06 flipper.so
-rw-r--r-- 1 ab staff 513 10 29 11:46 flipper.sol
-rw-r--r-- 1 ab staff 1310 10 29 11:46 solang.toml
{
"version": "0.1.0",
"name": "flipper",
"instructions": [
{
"name": "new",
"docs": [
"notice: Constructor that initializes the `bool` value to the given `init_value`."
],
"accounts": [
{
"name": "dataAccount",
"isMut": true,
"isSigner": true,
"isOptional": false
},
{
"name": "payer",
"isMut": true,
"isSigner": true,
"isOptional": false
},
{
"name": "systemProgram",
"isMut": false,
"isSigner": false,
"isOptional": false
}
],
"args": [
{
"name": "initvalue",
"type": "bool"
}
]
},
{
"name": "flip",
"docs": [
"notice: A message that can be called on instantiated contracts.\nThis one flips the value of the stored `bool` from `true`\nto `false` and vice versa."
],
"accounts": [
{
"name": "dataAccount",
"isMut": true,
"isSigner": false,
"isOptional": false
}
],
"args": []
},
{
"name": "get",
"docs": [
"notice: Simply returns the current value of our `bool`."
],
"accounts": [
{
"name": "dataAccount",
"isMut": false,
"isSigner": false,
"isOptional": false
}
],
"args": [],
"returns": "bool"
}
]
}
コンパイルで出来た成果物からsolana-cliを使用してプログラムをデプロイしたり、IDLからクライアントコードの生成を行います。
anchorを使うとanchor
コマンドで統合して扱うことができます。開発・運用スタイルに合わせてanchorを採用するか、コマンドを組み合わせて使うか検討することをお勧めします。
おわりに
Solidity自体はシンプルな言語なので、Solangを使うことでSolanaのプログラム開発の敷居の高さはかなり低くなります。
特に日本でのブロックチェーン開発ではEVM系が一般的でSolidityユーザーは多いので、Solangを採用してSolanaのプログラムの開発をするのは悪くない選択肢だと思います。
では、従来のNative (SPL Style)/Anchorを使った開発はしない方がいいかというと、そうでもありません。
特に私のようにRustに慣れているがブロックチェーン開発には慣れていないユーザーからすると、Nativeでの開発なんかは読めばわかる。テストなんかもRustで記述できるというのはとてもやりすいです。
(Solana開発で試したてないし、試した話も見かけないですがFormal Methods系のツールもあったりするし)
そのため、Solangを採用するかは開発チームの状況に合わせて決定するのが良いと思います。
Discussion