> For the complete documentation index, see [llms.txt](https://tharwa.gitbook.io/tharwa/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://tharwa.gitbook.io/tharwa/technical-architecture/sthusd-implementation.md).

# sthUSD Implementation

sthUSD transforms idle thUSD into a yield-generating asset while maintaining the liquidity and stability users expect from stablecoins. This page explains how sthUSD works under the hood, from its ERC-4626 foundation to the custom mechanisms that make it secure and efficient.

## **What Problem Does sthUSD Solve?**

Traditional stablecoins force users to choose between **yield** and **liquidity**. You can either:

* Hold USDC (liquid but no yield)
* Lock tokens in vaults (yield but no liquidity)

sthUSD eliminates this trade-off by providing **both yield and liquidity** through an intelligent vault design.

## **Contract Overview**

| Property     | Value                                                                                                               |
| ------------ | ------------------------------------------------------------------------------------------------------------------- |
| **Contract** | `sthUSD.sol`                                                                                                        |
| **Standard** | ERC-4626 Vault with Tharwa extensions                                                                               |
| **Address**  | `0xf7Af0A8079F12F19533B0DF69ce7ee6718b0C46f` (Mainnet)                                                              |
| **Audit**    | [PrismSec Report](https://github.com/tharwa-finance/contracts-v0/blob/main/audits/prismsec_tharwa_sthUSD_audit.pdf) |
| **Security** | 0 Critical, 0 High findings                                                                                         |

***

## **How sthUSD Works: Core Concepts**

### **The Share-Based Model**

sthUSD uses the **ERC-4626 vault standard**, which represents ownership through shares rather than direct token amounts. Here's why this matters:

{% tabs %}
{% tab title="Traditional Staking" %}
**Problem**: Direct 1:1 token staking

* 100 tokens in = 100 tokens out
* Yield added by minting new tokens
* Creates inflation and dilution

**Example**: Stake 1000 USDC, earn 50 USDC in new tokens
{% endtab %}

{% tab title="sthUSD Shares" %}
**Solution**: Share-based ownership

* Deposit thUSD, receive sthUSD shares
* Share value increases as yield is added
* No token inflation or dilution

**Example**: Stake 1000 thUSD for 1000 shares. Later, 1000 shares = 1050 thUSD
{% endtab %}
{% endtabs %}

### **Asset Definitions**

sthUSD manages one primary asset with sophisticated accounting:

#### **`asset()` - thUSD (The Underlying Token)**

This represents the ERC-20 token that users deposit to earn yield. In sthUSD's case, this is always thUSD (`0x76972F054aB43829064d31fF5f3AcC5Fabe57FE8`).

**Key Point**: Users deposit thUSD and receive sthUSD shares. The value of these shares grows as the vault earns yield from Tharwa's RWA portfolio.

#### **`shares` - sthUSD (The Vault Token)**

These represent proportional ownership in the vault. Unlike many DeFi protocols where shares are minted/burned arbitrarily, sthUSD shares have **real economic meaning**:

```solidity
shareValue = totalAssets() / totalSupply()
```

As `totalAssets()` grows through yield, each share becomes worth more thUSD.

### **State Variables Explained**

sthUSD tracks several key variables to maintain security and prevent manipulation:

```solidity
contract sthUSD is ERC4626, AccessControl, Pausable, ERC20Permit {
    // === CORE ACCOUNTING ===
    uint256 private _pooledAssets;      // Total thUSD we've accounted for
    uint256 private _yieldAmount;       // How much yield is currently vesting
    uint256 private _vestingEnd;        // When the current yield finishes vesting
    uint256 private _vestingPeriod;     // How long yield takes to vest (30 days)
    
    // === COOLDOWN SYSTEM ===
    uint256 private _cooldownPeriod;    // How long users must wait to withdraw
    ThUSDSilo private _silo;           // Where assets sit during cooldown
    
    // === FEE SYSTEM ===
    uint256 private _entryFeeBps;       // Fee charged on deposits (basis points)
    uint256 private _exitFeeBps;        // Fee charged on withdrawals (basis points)
    address private _feeRecipient;      // Where fees are sent
}
```

**Why These Variables Matter:**

* **`_pooledAssets`**: Prevents "donation attacks" where someone sends tokens directly to manipulate share price
* **`_yieldAmount` & `_vestingEnd`**: Ensures new depositors can't immediately claim yield that was earned before they arrived
* **`_cooldownPeriod` & `_silo`**: Provides flexibility between instant liquidity and enhanced security
* **Fee variables**: Allow protocol sustainability without hardcoded rates

***

## **Accounting Model: How Value Accrues**

### **The Core Challenge: Preventing Manipulation**

The biggest challenge in yield-bearing tokens is preventing "donation attacks" where malicious actors manipulate share prices. Here's how sthUSD solves this:

{% tabs %}
{% tab title="The Attack" %}
**Scenario**: Malicious actor exploits naive accounting

1. **Attacker deposits** 1 wei to get shares
2. **Attacker donates** 1000 thUSD directly to contract
3. **Share price skyrockets** artificially
4. **New depositors** get almost no shares for their deposits
5. **Attacker withdraws** at inflated price

**Result**: New users get rugged, attacker profits
{% endtab %}

{% tab title="sthUSD Protection" %}
**Solution**: Separate tracking of accounted vs actual assets

1. **`_pooledAssets`** tracks only legitimate deposits/yields
2. **Direct donations** don't affect `totalAssets()`
3. **Share price** based on accounted assets only
4. **Attack becomes impossible**

**Result**: Users protected, fair share pricing maintained
{% endtab %}
{% endtabs %}

### **Total Assets Calculation**

The heart of sthUSD's security is its `totalAssets()` function:

```solidity
function totalAssets() public view override returns (uint256) {
    return _pooledAssets - _unvestedAmount();
}
```

**Breaking this down:**

* **`_pooledAssets`**: Only includes legitimate deposits and properly added yield
* **`_unvestedAmount()`**: Excludes yield that hasn't finished vesting yet
* **Result**: Share price reflects only "earned" and "available" assets

### **Yield Vesting: Preventing Yield Theft**

When Tharwa adds yield to sthUSD, it doesn't become immediately available. Instead, it vests linearly over 30 days:

```solidity
function _unvestedAmount() internal view returns (uint256) {
    if (block.timestamp >= _vestingEnd) return 0;
    
    uint256 timeRemaining = _vestingEnd - block.timestamp;
    return _yieldAmount.mulDiv(timeRemaining, _vestingPeriod);
}
```

**Why Vesting Matters:**

{% hint style="info" %}
**Without Vesting**: Someone could deposit right before yield is added, immediately claim a large portion, then withdraw

**With Vesting**: Yield is distributed fairly over time to users who were actually staked when it was earned
{% endhint %}

**Example:**

* **Day 0**: 1000 thUSD yield added, starts 30-day vest
* **Day 15**: 50% of yield (500 thUSD) is vested and included in `totalAssets()`
* **Day 30**: 100% of yield (1000 thUSD) is fully vested

### **Asset Flow Tracking**

```solidity
// Deposit flow
function _deposit(address caller, address receiver, uint256 assets, uint256 shares) 
    internal override 
{
    // Calculate and deduct entry fee
    uint256 fee = assets.mulDiv(_entryFeeBps, 10_000);
    uint256 netAssets = assets - fee;
    
    // Update pooled assets
    _pooledAssets += netAssets;
    
    // Transfer fee to recipient
    if (fee > 0) {
        IERC20(asset()).safeTransferFrom(caller, _feeRecipient, fee);
    }
    
    // Continue with standard ERC4626 deposit
    super._deposit(caller, receiver, netAssets, shares);
}
```

### **Yield Vesting Mechanism**

```solidity
function addYield(uint256 amount) external onlyRole(YIELD_MANAGER_ROLE) {
    if (totalSupply() == 0) revert NoShares();
    if (_unvestedAmount() != 0) revert VestingActive();
    
    // Transfer yield into the vault
    IERC20(asset()).safeTransferFrom(msg.sender, address(this), amount);
    
    // Update accounting
    _pooledAssets += amount;
    _yieldAmount = amount;
    _vestingEnd = block.timestamp + _vestingPeriod;
    
    emit YieldAdded(amount, _vestingEnd);
}
```

***

## **Cooldown System**

### **Cooldown States**

The cooldown system provides flexibility for different withdrawal preferences:

| Cooldown Period | Behavior                         | Use Case                               |
| --------------- | -------------------------------- | -------------------------------------- |
| `0` (OFF)       | Standard ERC4626 withdraw/redeem | DeFi integrations, instant liquidity   |
| `3 days`        | Staged cooldown via silo         | Reduced exit fees, planned withdrawals |
| `7 days`        | Extended cooldown via silo       | Maximum security, institutional use    |

### **Cooldown Implementation**

```solidity
function cooldownAssets(uint256 assets) external whenNotPaused nonReentrant {
    if (_cooldownPeriod == 0) revert CooldownIsOff();
    
    address owner = msg.sender;
    uint256 shares = previewWithdraw(assets);
    
    // Calculate exit fee
    uint256 fee = assets.mulDiv(_exitFeeBps, 10_000);
    uint256 netAssets = assets - fee;
    
    // Update accounting
    _pooledAssets -= assets;
    
    // Transfer to silo for cooldown
    IERC20(asset()).safeTransfer(address(_silo), netAssets);
    _silo.deposit(owner, netAssets, block.timestamp + _cooldownPeriod);
    
    // Burn shares and handle fee
    _burn(owner, shares);
    if (fee > 0) {
        IERC20(asset()).safeTransfer(_feeRecipient, fee);
    }
    
    emit CooldownInitiated(owner, assets, netAssets, block.timestamp + _cooldownPeriod);
}
```

### **Silo Contract**

The `ThUSDSilo` contract manages assets during cooldown:

```solidity
contract ThUSDSilo {
    struct CooldownInfo {
        uint256 amount;
        uint256 cooldownEnd;
    }
    
    mapping(address => CooldownInfo) public cooldowns;
    
    function deposit(address user, uint256 amount, uint256 cooldownEnd) external onlyVault {
        cooldowns[user].amount += amount;
        cooldowns[user].cooldownEnd = cooldownEnd; // Latest cooldown resets timer
    }
    
    function withdraw(address user, address receiver) external onlyVault returns (uint256) {
        CooldownInfo storage info = cooldowns[user];
        require(block.timestamp >= info.cooldownEnd, "Cooldown active");
        
        uint256 amount = info.amount;
        delete cooldowns[user];
        
        IERC20(thUSD).safeTransfer(receiver, amount);
        return amount;
    }
}
```

***

## **Fee Structure**

### **Fee Configuration**

```solidity
function setFees(
    uint256 entryFeeBps,
    uint256 exitFeeBps,
    address feeRecipient
) external onlyRole(FEE_MANAGER_ROLE) {
    if (entryFeeBps > 1000 || exitFeeBps > 1000) revert FeeTooHigh(); // Max 10%
    if (feeRecipient == address(0)) revert RecipientZero();
    
    _entryFeeBps = entryFeeBps;
    _exitFeeBps = exitFeeBps;
    _feeRecipient = feeRecipient;
    
    emit FeesUpdated(entryFeeBps, exitFeeBps, feeRecipient);
}
```

### **Current Fee Parameters**

| Fee Type  | Current Rate | Maximum        | Applied When             |
| --------- | ------------ | -------------- | ------------------------ |
| Entry Fee | 0 bps (0%)   | 1000 bps (10%) | Deposit/Mint             |
| Exit Fee  | 0 bps (0%)   | 1000 bps (10%) | Withdraw/Redeem/Cooldown |

***

## **Access Control & Security**

### **Role-Based Permissions**

```solidity
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
bytes32 public constant YIELD_MANAGER_ROLE = keccak256("YIELD_MANAGER_ROLE");
bytes32 public constant FEE_MANAGER_ROLE = keccak256("FEE_MANAGER_ROLE");
```

| Role                 | Permissions                  | Purpose             |
| -------------------- | ---------------------------- | ------------------- |
| `DEFAULT_ADMIN_ROLE` | All administrative functions | Protocol governance |
| `PAUSER_ROLE`        | Pause/unpause contract       | Emergency response  |
| `YIELD_MANAGER_ROLE` | Add yield to vault           | Treasury operations |
| `FEE_MANAGER_ROLE`   | Configure fee parameters     | Fee optimization    |

### **Security Features**

1. **Pausable**: Emergency pause for all user-facing functions
2. **Blacklist**: Compliance-focused address restrictions
3. **Reentrancy Protection**: All external functions protected
4. **Safe Math**: OpenZeppelin's Math library for overflow protection

```solidity
function pause() external onlyRole(PAUSER_ROLE) {
    _pause();
}

function setBlacklisted(address account, bool blacklisted) 
    external onlyRole(DEFAULT_ADMIN_ROLE) 
{
    _blacklisted[account] = blacklisted;
    emit BlacklistUpdated(account, blacklisted);
}
```

***

## **Gas Optimization**

### **Efficient Storage Layout**

```solidity
// Packed struct to minimize storage slots
struct VaultState {
    uint128 pooledAssets;    // Sufficient for 3.4e38 tokens
    uint128 yieldAmount;     // Sufficient for yield amounts
    uint64 vestingEnd;       // Timestamp fits in uint64
    uint64 vestingPeriod;    // Duration fits in uint64
    uint32 cooldownPeriod;   // Duration fits in uint32
    uint16 entryFeeBps;      // Max 65535 bps (655.35%)
    uint16 exitFeeBps;       // Max 65535 bps (655.35%)
}
```

### **Gas-Efficient Operations**

| Operation | Gas Cost | Optimization             |
| --------- | -------- | ------------------------ |
| Deposit   | \~50,000 | Minimal external calls   |
| Withdraw  | \~45,000 | Direct asset transfer    |
| Cooldown  | \~60,000 | Single silo interaction  |
| Add Yield | \~40,000 | Simple accounting update |

***

## **Testing & Validation**

### **Comprehensive Test Suite**

```solidity
contract sthUSDTest is Test {
    function testDepositWithdraw() public {
        // Standard ERC4626 functionality
    }
    
    function testYieldVesting() public {
        // Yield vesting mechanics
    }
    
    function testCooldownSystem() public {
        // Cooldown and silo interactions
    }
    
    function testDonationAttackResistance() public {
        // Security against share price manipulation
    }
    
    function testFeeCalculations() public {
        // Entry and exit fee mechanics
    }
}
```

### **Invariant Testing**

Key invariants maintained throughout all operations:

1. `totalAssets() >= totalSupply() * minSharePrice`
2. `_pooledAssets >= totalAssets() + unvestedAmount()`
3. `sum(balanceOf(users)) == totalSupply()`
4. `cooldownAssets + vaultAssets == totalPooledAssets`

***

## **Integration Examples**

### **Basic Integration**

```solidity
interface ISThUSD {
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
    function totalAssets() external view returns (uint256);
    function previewDeposit(uint256 assets) external view returns (uint256 shares);
}

contract TharwaIntegration {
    IsthUSD constant sthUSD = IsthUSD(0xf7Af0A8079F12F19533B0DF69ce7ee6718b0C46f);
    IERC20 constant thUSD = IERC20(0x76972F054aB43829064d31fF5f3AcC5Fabe57FE8);
    
    function stake(uint256 amount) external {
        thUSD.transferFrom(msg.sender, address(this), amount);
        thUSD.approve(address(sthUSD), amount);
        
        uint256 shares = sthUSD.deposit(amount, msg.sender);
        emit Staked(msg.sender, amount, shares);
    }
}
```

### **Advanced Integration with Cooldown**

```solidity
contract AdvancedIntegration {
    function initiateWithdrawal(uint256 assets) external {
        if (sthUSD.cooldownPeriod() > 0) {
            // Use cooldown system
            sthUSD.cooldownAssets(assets);
        } else {
            // Direct withdrawal
            sthUSD.withdraw(assets, msg.sender, msg.sender);
        }
    }
    
    function completeWithdrawal() external {
        sthUSD.unstake(msg.sender);
    }
}
```

***

{% hint style="success" %}
**Production Ready** sthUSD is fully audited, tested, and operational on Ethereum mainnet with zero critical findings from PrismSec.
{% endhint %}

{% hint style="info" %}
**Developer Resources**

* **Source Code**: [sthUSD.sol](https://github.com/tharwa-finance/contracts-v0/blob/main/sthUSD/src/sthUSD.sol)
* **Tests**: [sthUSD.t.sol](https://github.com/tharwa-finance/contracts-v0/blob/main/sthUSD/test/sthUSD.t.sol)
* **Audit Report**: [PrismSec sthUSD Audit](https://github.com/tharwa-finance/contracts-v0/blob/main/audits/prismsec_tharwa_sthUSD_audit.pdf)
  {% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://tharwa.gitbook.io/tharwa/technical-architecture/sthusd-implementation.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
