Soroban은 Stellar
Rust로 만들어진 Smart Contract 플랫폼이다.
WASM과 Rust기반으로 전송 분야에 특화되어 있는 스텔라 블록체인에서 최근에 발표된 프로젝트다.
Solidity
와 다르게 어떤 식으로 컨트랙트를 작성하는지 비교해보고자 한다.
rustup target add wasm32-unknown-unknown
cargo install --locked --version 0.9.4 soroban-cli
soroban
$ soroban
Build, deploy, & interact with contracts; set identities to sign with; configure networks; generate keys; and more.
Intro: https://soroban.stellar.org
CLI Reference: https://github.com/stellar/soroban-tools/tree/main/docs/soroban-cli-full-docs.md
Usage: soroban [OPTIONS] <COMMAND>
Commands:
contract Tools for smart contract developers
config Read and update config
events Watch the network for contract events
lab Experiment with early features and expert tools
version Print version information
completion Print shell completion code for the specified shell
Options:
--global Use global config
-f, --filter-logs <FILTER_LOGS> Filter logs output. To turn on "soroban_cli::log::footprint=debug" or off "=off". Can also use env var `RUST_LOG`
-q, --quiet Do not write logs to stderr including `INFO`
-v, --verbose Log DEBUG events
--very-verbose Log DEBUG and TRACE events
--list List installed plugins. E.g. `soroban-hello`
-h, --help Print help (see more with '--help')
-V, --version Print version
TESTING_OPTIONS:
--config-dir <CONFIG_DIR>
cargo new --lib 프로젝트명
// Cargo.toml
...
[lib]
crate-type = ["cdylib"]
[features]
testutils = ["soroban-sdk/testutils"]
[dependencies]
soroban-sdk = "0.9.2"
[dev_dependencies]
soroban-sdk = { version = "0.9.2", features = ["testutils"] }
[profile.release]
opt-level = "z"
overflow-checks = true
debug = 0
strip = "symbols"
debug-assertions = false
panic = "abort"
codegen-units = 1
lto = true
[profile.release-with-logs]
inherits = "release"
debug-assertions = true
profile.release
는 contract build를 최적화 하기 위해 생성한다. soroban의 contract 최대 크기는 256KB이지만, 위 설정이 없는 Rust 프로그램은 거의 이 크기를 초과한다고 한다.
// lib.rs
#![no_std]
Rust std가 Build에 포함되지 않도록 설정해준다. std의 크기가 크기때문에 블록체인상에 배포되는 것과 같은 상황에는 적합하지 않다고 한다.
use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Symbol, Vec};
크레이트에서 필요한 macro와 soroban-sdk를 가져와야 한다.
또한 Rust에서 처럼 std::vec::Vec
과 같은 힙 할당자와 메모리가 없기때문에 사용할 수 없고 자체적으로 네이티브 기능을 제공하는 Vec
, Map
, Bytes
, Symbol
과 같은 타입들을 제공한다.
Floats와 부동 소수점 연산은 지원되지 않는다고 한다.
#[contract]
pub struct Contract;
#[contractimpl]
impl Contract {
pub fn hello(env: Env, to: Symbol) -> Vec<Symbol> {
todo!()
}
}
#[contract]
속성은 Contract 구조체를 계약 기능이 연결된 유형으로 지정한다. impl
이 들어갔으니 유추 가능하겠지만, #[contractimpl]
은 계약 구조체에 계약 기능을 구현하는 부분이다.
또한 함수가 외부에서 호출되도록 의도적으로 pub
키워드로 만들어야 한다.
Env
는 soroban 환경에 대한 엑세스를 허용하는 유형이다.
#![no_std]
use soroban_sdk::{contract, contractimpl, symbol_short, vec, Env, Symbol, Vec};
#[contract]
pub struct Contract;
#[contractimpl]
impl Contract {
pub fn greet(env: Env, to: Symbol) -> Vec<Symbol> {
vec![&env, symbol_short!("Hello"), to]
}
}
Symbol
은 짧은 문자열을 표시한다. 유효한 문자는 a-z, A-Z, 0-9까지 이며 최대 길이는 32자이다.
또한 함수명이나 struct, enum 식별자에 사용되어 길이가 제한되었다고 한다. 최대가 32자이나 9자 미만일 경우 런타임에 더 효율적이며 컴파일 타임에도 차이가 있다고 한다.
test.rs 파일을 생성해준다.
// test.rs
use crate::{Contract, ContractClient};
use soroban_sdk::{symbol_short, vec, Env};
#[test]
fn greet() {
let env = Env::default();
let contract_id = env.register_contract(None, Contract);
let client = ContractClient::new(&env, &contract_id);
let words = client.greet(&symbol_short!("Dev"));
assert_eq!(
words,
vec![&env, symbol_short!("Hello"), symbol_short!("Dev"),]
);
}
모든 테스트에서 필요한 것은 컨트랙트 내부에서 실행되는 soroban 환경인 Env
를 만들어줘야 한다.
let env = Env::default();
컨트랙트는 컨트랙트 타입을 이용해 테스트 환경에 적용하는데, 컨트랙트의 register_contract
의 첫 번째 인자로 계약자의 ID를 명시해주거나 None
을 넣을 수 있다. None
이 들어가면 ID가 새로 생성된다.
let contract_id = env.register_contract(None, Contract);
lib.rs에서 impl
로 구현한 컨트랙트의 이름은 Contract
였다. client를 생성할때컨트랙트명client:new()
를 사용하면 될 것 같다.
let client = ContractClient::new(&env, &contract_id);
let words = client.hello(&symbol_short!("Dev"));
cargo test
soroban contract build // equal, cargo build --target wasm32-unknown-unknwon --release
빌드가 완료되면 파일명과 동일한 wasm
파일이 생성된다.
soroban contract invoke \
--wasm target/wasm32-unknown-unknown/release/[project-name].wasm \
--id 1 \
-- \
greet \
--to friend
to
의 인자로 friend를 보내주면 vec!에 "Hello"와 "friend"가 담겨서 출력된다.
Solidity에서는 우선 작성하는 Solidity의 버전을 명시해줘야 한다.
pragma solidity ^0.8.21;
contract 작성을 위해서는 contract
라는 키워드를 사용한다.
contract Greet {
function greet() public pure returns (string memory) {
return "Hello Solidity!";
}
}
public
키워드를 통해 외부 컨트랙에서도 함수를 호출할 수 있게 해주고, pure를 통해 외부 변수들과의 관계없이 내부요소들만 사용한다고 명시한다. 이후에 이 함수가 리턴할 반환 유형을 명시하는데 string memory
라고 작성하는 이유는 함수 실행 동안만 유효하며, 함수 호출이 완료되면 데이터가 삭제된다. 함수 내부에서 생성되고 사용되는 임시 데이터를 나타낸다.
#[contract]
pub struct Contract;
#[contractimpl]
impl Contract {
pub fn greet(env: Env, to: Symbol) -> Vec<Symbol> {
vec![&env, symbol_short!("Hello"), to]
}
}
soroban은 스텔라 재단에서 만들었으며 출시된지 얼마 되지 않은듯 해보인다. Rust로 smart contract을 작성할 수 있는게 큰 매력인것 같다.