Skip to main content

Smart Contracts

Savitri Network provides a native smart contract runtime with built-in token standards, governance, and oracle support.

Contract Runtime

Contracts run in a sandboxed environment with:

  • Gas metering: Every operation consumes gas to prevent infinite loops
  • Storage slots: Key-value storage using Keccak256-hashed slot addresses
  • Event system: Contracts emit events for external consumers
  • Memory monitoring: Runtime memory limits prevent DoS
  • Upgrade support: Contracts can be upgraded via governance

Token Standards

StandardTypeEquivalentDescription
SAVITRI-20FungibleERC-20Token transfers, approvals, allowances
SAVITRI-721Non-Fungible (NFT)ERC-721Unique token ownership, transfers, metadata
SAVITRI-1155Multi-AssetERC-1155Batch operations, mixed fungible/non-fungible

Built-in Contracts

Governance

On-chain proposal and voting system:

  • Create proposals with title, description, and voting period
  • Vote with support/oppose
  • Execute approved proposals
  • FL (Federated Learning) proposal specialization

See Governance for details.

Oracle

External data feed system:

  • Register oracle providers
  • Request and submit data
  • Proof verification
  • Schema validation

See Oracle System for details.

Contract Interaction via SDK

Using 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)?;

// Call a contract function
let tx_hash = contract.call_contract(
&"contract_address".to_string(),
b"transfer",
&encoded_args,
Some(0),
).await?;

Using 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)?;

Storage Model

Contracts use a slot-based storage model similar to Ethereum:

  • Slots 0-99: Reserved for BaseContract (owner, metadata)
  • Slot 100+: Contract-specific storage

Slot addresses are derived using Keccak256 hashing to prevent collisions:

balance_slot = keccak256(address || SLOT_BALANCES_BASE)
allowance_slot = keccak256(spender || keccak256(owner || SLOT_ALLOWANCES_BASE))

All values are stored as 32-byte arrays (little-endian for numeric types).

Gas Costs

OperationGas CostFee (SAVT)
Contract deployVariable0.005 base
Contract callVariable0.005 base
Storage write (SSTORE)20,000Included in base
Storage read (SLOAD)200Included in base
Token transfer~50,0000.005
NFT mint~80,0000.005
Governance vote~30,0000.005

Contract Deployment

Contracts are deployed via special transactions with to = None and the contract bytecode in the data field:

let deploy_tx = TransactionBuilder::new()
// No .to() -- indicates contract deployment
.data(contract_bytecode)
.value(0)
.nonce(nonce)
.fee(5_000_000_000_000_000)
.build_and_sign(&wallet)?;