Token Interaction
Reading and writing SRC20 token balances and allowances
Read shielded balances and write shielded state changes on SRC20 tokens using seismic-alloy.
Overview
SRC20 token interaction in Rust follows the same builder pattern as all seismic-alloy contract calls:
Transparent reads (metadata) — Use
contract.name().call()without.seismic()Signed reads (balances, allowances) — Use
contract.balanceOf(addr).seismic().call()with.seismic()(no shielded params in arguments)Shielded writes (transfers, approvals) — Use
contract.transfer(to, amount).send()directly (auto-encrypts becausesuint256is a shielded param)
Defining the Interface
use seismic_prelude::client::*;
sol! {
#[sol(rpc)]
interface ISRC20 {
function name() public view returns (string);
function symbol() public view returns (string);
function decimals() public view returns (uint8);
function totalSupply() public view returns (uint256);
function balanceOf(address account) public view returns (suint256);
function transfer(address to, suint256 amount) public returns (bool);
function approve(address spender, suint256 amount) public returns (bool);
function allowance(address owner, address spender) public view returns (suint256);
function transferFrom(address from, address to, suint256 amount) public returns (bool);
event Transfer(address indexed from, address indexed to, suint256 value);
event Approval(address indexed owner, address indexed spender, suint256 value);
}
}Reading Token Metadata
Token metadata (name, symbol, decimals, totalSupply) is not shielded. Use a plain transparent read:
Reading Shielded Balances
balanceOf() is a shielded read. The contract uses msg.sender to authenticate the caller, so you must use .seismic().call() (a signed read) on a provider created with a wallet.
balanceOf() requires a signed read via .seismic().call(). A plain .call() without .seismic() zeros out the from field, causing the contract to see the zero address as the sender and return its balance — which is almost certainly zero.
Reading Allowances
Allowance reads may also be shielded depending on the contract implementation. Use a signed read to be safe:
Shielded Writes
Transfer Tokens
Send tokens using a shielded write. The transfer amount is encrypted in calldata:
Approve a Spender
Operation Summary
Read name/symbol/decimals
contract.name().call()
No encryption
Any provider
Read balanceOf
contract.balanceOf(addr).seismic().call()
.seismic()
Signed provider
Read allowance
contract.allowance(o, s).seismic().call()
.seismic()
Signed provider
Transfer tokens
contract.transfer(to, amt).send()
Auto-encrypts
Signed provider
Approve spender
contract.approve(s, amt).send()
Auto-encrypts
Signed provider
Notes
All shielded operations require a provider built with a wallet via
SeismicProviderBuilderFunctions with shielded parameters (
suint256) liketransferandapproveauto-encrypt viaShieldedCallBuilderFor functions without shielded params (like
balanceOfandallowance), use.seismic()to opt into encryptionThe filler pipeline automatically handles encryption nonce, TEE pubkey, and AES-GCM encryption
Shielded types (
suint256) appear as their standard counterparts (uint256) in the ABI encoding
See Also
Transfers — Shielded transfer patterns and multi-step workflows
Event Decryption — Decrypting SRC20 events
Contract Interaction — General shielded and transparent call patterns
SeismicSignedProvider — Provider with signing capabilities
Last updated

