SPUSD Wrapper System
The SPUSD wrapper system is a critical innovation that bridges the security of soul-bound tokens with the liquidity needs of DeFi users. This document provides a technical overview of how the wrapper mechanism works.
Architecture Overview
The wrapper system consists of two main components:
1. StUSDCWrapper Contract
The wrapper contract handles all conversions between sUSDC and SPUSD:
- Holds sUSDC tokens during the wrap period
- Mints/burns SPUSD based on deposits/withdrawals
- Manages locked token accounting
- Enforces role-based access for special operations
2. SPUSD Token Contract
The SPUSD token is a standard ERC20 with additional features:
- Lock-aware transfers (checks vesting status)
- Restricted minting (only wrapper can mint)
- Burn mechanism for unwrapping
- Standard ERC20 compatibility for DEX trading
Core Mechanisms
Wrapping Process
function wrap(uint256 amount) external nonReentrant {
require(amount > 0, "Wrapper: Zero amount");
// Transfer sUSDC from user to wrapper
require(
sUSDC.transferFrom(msg.sender, address(this), amount),
"Wrapper: Transfer failed"
);
// Mint equivalent SPUSD to user
spusd.mint(msg.sender, amount);
emit Wrapped(msg.sender, amount);
}
Flow:
- User approves wrapper for sUSDC
- User calls
wrap(amount)
- sUSDC transferred to wrapper
- SPUSD minted to user
- User can now trade SPUSD
Unwrapping Process
function unwrap(uint256 amount) external nonReentrant {
require(amount > 0, "Wrapper: Zero amount");
// Check if user has locked tokens
LockInfo memory lock = lockedBalances[msg.sender];
if (lock.amount > 0 && block.timestamp < lock.unlockTime) {
require(
spusd.balanceOf(msg.sender) - amount >= lock.amount,
"Wrapper: Tokens locked"
);
}
// Burn SPUSD from user
spusd.burnFrom(msg.sender, amount);
// Transfer sUSDC back to user
require(
sUSDC.transfer(msg.sender, amount),
"Wrapper: Transfer failed"
);
emit Unwrapped(msg.sender, amount);
}
Flow:
- User holds SPUSD tokens
- User calls
unwrap(amount)
- Lock status checked
- SPUSD burned from user
- sUSDC returned to user
Lock Management
Investor Distribution
For team members and investors with vesting schedules:
function wrapAndLock(
address to,
uint256 amount,
uint256 unlockTime
) external nonReentrant {
require(
owner() == msg.sender || hasRole(OPERATOR_ROLE, msg.sender),
"Wrapper: Not authorized"
);
// Transfer sUSDC from sender to wrapper
require(
sUSDC.transferFrom(msg.sender, address(this), amount),
"Wrapper: Transfer failed"
);
// Update lock info
if (unlockTime > lockedBalances[to].unlockTime) {
lockedBalances[to].unlockTime = unlockTime;
}
lockedBalances[to].amount += amount;
// Mint SPUSD to recipient
spusd.mint(to, amount);
emit LockedTokensMinted(to, amount, unlockTime);
}
Lock Checking in SPUSD
The SPUSD token checks locks on every transfer:
function _update(address from, address to, uint256 amount) internal override {
// Skip check for minting/burning
if (from == address(0) || to == address(0)) {
super._update(from, to, amount);
return;
}
// Check if sender has locked tokens
(uint256 locked, uint256 unlockTime) = IStUSDCWrapper(wrapper).getLockInfo(from);
if (locked > 0 && block.timestamp < unlockTime) {
// Ensure user maintains minimum locked balance
uint256 balance = balanceOf(from);
require(balance - amount >= locked, "SPUSD: Tokens locked");
}
super._update(from, to, amount);
}
Security Features
Access Control
- Only wrapper contract can mint SPUSD
- Only wrapper contract can burn SPUSD
- Owner/Operator roles for
wrapAndLock
- No admin functions to drain funds
Economic Security
- Strict 1:1 backing ratio enforced
- No fractional reserve possible
- All sUSDC held in wrapper contract
- Transparent on-chain verification
Technical Security
- Reentrancy guards on all state-changing functions
- No external calls except to trusted contracts
- Comprehensive input validation
- Event emission for all operations
Integration Points
For DEXs
SPUSD appears as a standard ERC20 token:
transfer()
andtransferFrom()
work normallyapprove()
andallowance()
standardbalanceOf()
returns total balance (including locked)- Lock checking happens transparently
For Lending Protocols
SPUSD can be used as collateral:
- Standard ERC20 interface
- No special integration required
- Lock status doesn't affect collateral value
- Liquidations work normally
For Analytics
Key metrics to track:
- Total SPUSD supply = Total sUSDC locked in wrapper
- Circulating supply = Total supply - locked amounts
- Wrapper sUSDC balance = Backing ratio verification
- Lock statistics = Vesting schedule tracking
Gas Optimization
The wrapper system is optimized for gas efficiency:
- Single storage slot for lock info
- Minimal external calls
- Efficient balance checks
- Batching not required (already optimal)
Typical gas costs:
- Wrap: ~120,000 gas
- Unwrap: ~100,000 gas
- Transfer: ~65,000 gas (with lock check)
Emergency Procedures
Wrapper Issues
If the wrapper has issues:
- No new wrapping/unwrapping possible
- Existing SPUSD remains tradeable
- sUSDC remains safe in wrapper
- Can deploy new wrapper if needed
SPUSD Issues
If SPUSD has issues:
- Wrapper holds all backing sUSDC
- Can snapshot balances
- Deploy new SPUSD token
- Migrate via wrapper update
Future Enhancements
Planned Features
- Batch operations for gas savings
- Permit support for gasless transactions
- Cross-chain bridge integration
- Advanced lock types (linear vesting)
Backward Compatibility
All enhancements maintain:
- Existing SPUSD remains valid
- No changes to core wrap/unwrap
- Additional features are optional
- Full backward compatibility
Conclusion
The SPUSD wrapper system elegantly solves the liquidity problem of soul-bound tokens while maintaining security and simplicity. By separating concerns between the soul-bound layer (sUSDC) and liquidity layer (SPUSD), users get the best of both worlds: security and flexibility.