Skip to main content

SAVITRI-20: Fungible Token Standard

SAVITRI-20 is the fungible token standard for the Savitri Network, equivalent to Ethereum's ERC-20. It enables creation and management of fungible tokens with transfer, approval, and allowance mechanics.

Interface

FunctionParamsReturnsDescription
initializeowner, name, symbol, initial_supply-Deploy and initialize a new token
total_supply-u128Total token supply
balance_ofaddressu128Token balance of an address
transferto, amountResultTransfer tokens to recipient
approvespender, amountResultApprove spender allowance
transfer_fromfrom, to, amountResultTransfer using allowance
allowanceowner, spenderu128Check approved allowance
mintto, amountResultMint new tokens (owner only)
burnfrom, amountResultBurn tokens

Storage Layout

Slot RangePurpose
0-99BaseContract (reserved)
100Total supply
101+Balances mapping: keccak256(address || 101)
200+Allowances mapping: keccak256(spender || keccak256(owner || 200))
300+Token name (string storage)
400+Token symbol (string storage)

All values are stored as 32-byte arrays with u128 values in little-endian format.

Deploying a SAVITRI-20 Token

Via Contract Runtime (Rust)

use savitri_contracts::contracts::standards::savitri20::SAVITRI20;

// Initialize a new token
SAVITRI20::initialize(
&mut contract_storage,
&storage,
&owner_address, // [u8; 32]
"My Token", // name
"MTK", // symbol
1_000_000_000_000_000_000_000_000, // 1M tokens (18 decimals)
Some(&mut gas_meter),
)?;

Via SDK

use savitri_sdk::{TransactionBuilder, Wallet};

let wallet = Wallet::from_private_key_hex("your_key")?;

// Deploy token contract
let deploy_tx = TransactionBuilder::new()
.data(savitri20_bytecode)
.value(0)
.nonce(nonce)
.fee(5_000_000_000_000_000)
.build_and_sign(&wallet)?;

Token Operations

Transfer

// Direct transfer
SAVITRI20::transfer(
&mut contract_storage,
&storage,
&sender, // [u8; 32]
&recipient, // [u8; 32]
1000_000_000_000_000_000, // 1000 tokens
&mut event_system,
Some(&mut gas_meter),
)?;

Approve and TransferFrom

// Approve spender
SAVITRI20::approve(
&mut contract_storage,
&storage,
&owner,
&spender,
5000_000_000_000_000_000, // 5000 token allowance
&mut event_system,
Some(&mut gas_meter),
)?;

// Transfer using allowance
SAVITRI20::transfer_from(
&mut contract_storage,
&storage,
&spender, // caller (must have allowance)
&owner, // from
&recipient, // to
1000_000_000_000_000_000,
&mut event_system,
Some(&mut gas_meter),
)?;

Query Balances

let balance = SAVITRI20::balance_of(&contract_storage, &storage, &address)?;
let supply = SAVITRI20::total_supply(&contract_storage, &storage)?;
let allowed = SAVITRI20::allowance(&contract_storage, &storage, &owner, &spender)?;

Events

EventFieldsDescription
Transferfrom, to, amountToken transfer
Approvalowner, spender, amountAllowance approval
Mintto, amountNew tokens minted
Burnfrom, amountTokens burned

Security

  • Non-zero address check: Transfers to/from the zero address are rejected
  • Balance validation: Transfers fail if sender has insufficient balance
  • Allowance check: transfer_from verifies and decrements allowance atomically
  • Overflow protection: All arithmetic uses checked operations
  • Keccak256 slot hashing: Prevents storage slot collisions between different mappings