스마트 컨트랙트
Savitri Network는 내장 토큰 표준, 거버넌스, 오라클 지원을 갖춘 네이티브 스마트 컨트랙트 런타임을 제공합니다.
컨트랙트 런타임
컨트랙트는 다음을 갖춘 격리된 환경(샌드박스)에서 실행됩니다:
- 가스 계량: 각 연산은 무한 루프 방지를 위해 가스를 소비합니다
- 스토리지 슬롯: Keccak256 해시 슬롯 주소를 통한 키-값 저장소
- 이벤트 시스템: 컨트랙트가 외부 소비자를 위한 이벤트를 방출합니다
- 메모리 모니터링: 런타임 메모리 제한으로 DoS 공격을 방지합니다
- 업그레이드 지원: 거버넌스를 통해 컨트랙트를 업그레이드할 수 있습니다
토큰 표준
| 표준 | 유형 | 동등 | 설명 |
|---|---|---|---|
| SAVITRI-20 | 대체 가능 | ERC-20 | 토큰 전송, 승인, 허용량 |
| SAVITRI-721 | 대체 불가능 (NFT) | ERC-721 | 고유 토큰 소유권, 전송, 메타데이터 |
| SAVITRI-1155 | 멀티 에셋 | ERC-1155 | 배치 작업, 혼합 대체/비대체 가능 |
내장 컨트랙트
거버넌스
온체인 제안 및 투표 시스템:
- 제목, 설명, 투표 기간으로 제안 생성
- 찬성/반대 투표
- 승인된 제안 실행
- FL (Federated Learning) 제안 특수화
자세한 내용은 거버넌스를 참조하세요.
오라클
외부 데이터 피드 시스템:
- 오라클 제공자 등록
- 데이터 요청 및 제출
- 증명 검증
- 스키마 검증
자세한 내용은 오라클 시스템을 참조하세요.
SDK를 통한 컨트랙트 상호작용
ContractClient 사용
use savitri_sdk::{ContractClient, Wallet, RpcClient};
let wallet = Wallet::from_private_key_hex("your_key")?;
let contract = ContractClient::from_url_and_wallet("http://localhost:8545", wallet)?;
// 컨트랙트 함수 호출
let tx_hash = contract.call_contract(
&"contract_address".to_string(),
b"transfer",
&encoded_args,
Some(0),
).await?;
TransactionBuilder 사용
use savitri_sdk::TransactionBuilder;
let tx = TransactionBuilder::new()
.to("contract_address")
.data(call_data) // function selector + encoded args
.value(0)
.nonce(nonce)
.fee(5_000_000_000_000_000) // 0.005 SAVT contract fee
.build_and_sign(&wallet)?;
스토리지 모델
컨트랙트는 Ethereum과 유사한 슬롯 기반 스토리지 모델을 사용합니다:
- 슬롯 0-99:
BaseContract용 예약 (소유자, 메타데이터) - 슬롯 100+: 컨트랙트 특정 스토리지
충돌을 방지하기 위해 Keccak256 해시를 통해 슬롯 주소가 파생됩니다:
balance_slot = keccak256(address || SLOT_BALANCES_BASE)
allowance_slot = keccak256(spender || keccak256(owner || SLOT_ALLOWANCES_BASE))
모든 값은 32바이트 배열로 저장됩니다 (숫자 유형은 little-endian).
가스 비용
| 연산 | 가스 비용 | 수수료 (SAVT) |
|---|---|---|
| 컨트랙트 배포 | 가변 | 기본 0.005 |
| 컨트랙트 호출 | 가변 | 기본 0.005 |
| 스토리지 쓰기 (SSTORE) | 20,000 | 기본 포함 |
| 스토리지 읽기 (SLOAD) | 200 | 기본 포함 |
| 토큰 전송 | ~50,000 | 0.005 |
| NFT 민트 | ~80,000 | 0.005 |
| 거버넌스 투표 | ~30,000 | 0.005 |
컨트랙트 배포
컨트랙트는 to = None이고 data 필드에 컨트랙트 바이트코드가 있는 특수 트랜잭션을 통해 배포됩니다:
let deploy_tx = TransactionBuilder::new()
// 없는 .to() -- 컨트랙트 배포를 나타냄
.data(contract_bytecode)
.value(0)
.nonce(nonce)
.fee(5_000_000_000_000_000)
.build_and_sign(&wallet)?;