Production Smart Contracts

Verified Solidity Source Code

Production-hardened ERC-20 stablecoin + governance contracts. OpenZeppelin base, audited patterns, Sepolia-deployed.

SmartUSD.sol — $SUSD Stablecoin

ERC-20 · v3.1.0
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title SmartUSD (\$SUSD)
 * @notice AI-powered algorithmic stablecoin — production v3.1.0
 * @dev    OpenZeppelin ERC20 + AccessControl + ReentrancyGuard + Pausable
 *         Dual-token model: collateral (USDC) → $SUSD + SGOV
 */

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";

interface ISGov {
    function mintReward(address to, uint256 amount) external;
    function burnFrom(address from, uint256 amount) external;
}

contract SmartUSD is ERC20, AccessControl, ReentrancyGuard, Pausable {
    using SafeERC20 for IERC20;

    bytes32 public constant MINTER_ROLE      = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE      = keccak256("PAUSER_ROLE");
    bytes32 public constant CONTROLLER_ROLE  = keccak256("CONTROLLER_ROLE");

    // ── State ──────────────────────────────────────────────────────────
    IERC20  public immutable collateralToken; // USDC (6 decimals)
    ISGov   public sgovToken;

    uint256 public collateralRatio = 115_00; // 115.00% in bps (÷100)
    uint256 public mintFee         = 10;     // 0.10% in bps
    uint256 public burnFee         = 30;     // 0.30% in bps
    uint256 public mintCap         = 50_000_000e18;  // 50M SUSD hard cap
    uint256 public singleMintMax   = 1_000_000e18;   // per-tx guard
    uint256 public totalCollateral;

    // SGOV reward rate: 1% of locked collateral (in bps)
    uint256 public sgovRewardRate = 100; // 1.00%

    // ── Events ─────────────────────────────────────────────────────────
    event Minted(address indexed user, uint256 susdAmount, uint256 collateral, uint256 sgovRewarded, uint256 fee);
    event Burned(address indexed user, uint256 susdAmount, uint256 usdcReturned, uint256 sgovBurned, uint256 fee);
    event CollateralRatioUpdated(uint256 oldCR, uint256 newCR);
    event MintFeeUpdated(uint256 newFee);
    event BurnFeeUpdated(uint256 newFee);
    event SGovTokenSet(address indexed sgov);
    event MintCapUpdated(uint256 newCap);

    // ── Constructor ────────────────────────────────────────────────────
    constructor(address _collateralToken, address admin) ERC20("Smart USD", "SUSD") {
        require(_collateralToken != address(0), "SUSD: zero collateral");
        require(admin != address(0), "SUSD: zero admin");

        collateralToken = IERC20(_collateralToken);

        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(MINTER_ROLE,       admin);
        _grantRole(PAUSER_ROLE,       admin);
        _grantRole(CONTROLLER_ROLE,   admin);
    }

    // ── Admin ──────────────────────────────────────────────────────────
    function setSGovToken(address _sgov) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(_sgov != address(0), "SUSD: zero sgov");
        sgovToken = ISGov(_sgov);
        emit SGovTokenSet(_sgov);
    }

    function setCollateralRatio(uint256 newCR) external onlyRole(CONTROLLER_ROLE) {
        require(newCR >= 105_00 && newCR <= 200_00, "SUSD: CR out of range");
        emit CollateralRatioUpdated(collateralRatio, newCR);
        collateralRatio = newCR;
    }

    function setMintFee(uint256 newFee) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(newFee <= 200, "SUSD: fee > 2%");
        mintFee = newFee;
        emit MintFeeUpdated(newFee);
    }

    function setBurnFee(uint256 newFee) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(newFee <= 200, "SUSD: fee > 2%");
        burnFee = newFee;
        emit BurnFeeUpdated(newFee);
    }

    function setMintCap(uint256 newCap) external onlyRole(DEFAULT_ADMIN_ROLE) {
        require(newCap >= totalSupply(), "SUSD: cap below supply");
        mintCap = newCap;
        emit MintCapUpdated(newCap);
    }

    function pause()   external onlyRole(PAUSER_ROLE) { _pause(); }
    function unpause() external onlyRole(PAUSER_ROLE) { _unpause(); }

    // ── Core: Mint ─────────────────────────────────────────────────────
    /**
     * @notice Deposit USDC collateral → receive $SUSD + SGOV reward
     * @param usdcAmount   Amount of USDC to deposit (6-decimal)
     */
    function mint(uint256 usdcAmount) external nonReentrant whenNotPaused {
        require(usdcAmount > 0, "SUSD: zero amount");

        // Normalise USDC (6 dec) to 18 dec
        uint256 susdAmount = usdcAmount * 1e12;
        require(susdAmount <= singleMintMax, "SUSD: exceeds single-tx cap");
        require(totalSupply() + susdAmount <= mintCap, "SUSD: exceeds protocol cap");

        // Collateral required (115% of $SUSD face value, back in USDC 6-dec)
        uint256 collateralRequired = (usdcAmount * collateralRatio) / 100_00;

        // Pull collateral
        collateralToken.safeTransferFrom(msg.sender, address(this), collateralRequired);
        totalCollateral += collateralRequired;

        // Protocol fee (deducted from SUSD received)
        uint256 fee      = (susdAmount * mintFee) / 10_000;
        uint256 netSusd  = susdAmount - fee;

        // Mint $SUSD to user
        _mint(msg.sender, netSusd);

        // Mint SGOV reward (1% of locked collateral)
        uint256 sgovReward = 0;
        if (address(sgovToken) != address(0)) {
            sgovReward = (collateralRequired * 1e12 * sgovRewardRate) / 10_000;
            sgovToken.mintReward(msg.sender, sgovReward);
        }

        emit Minted(msg.sender, netSusd, collateralRequired, sgovReward, fee);
    }

    // ── Core: Burn ─────────────────────────────────────────────────────
    /**
     * @notice Burn $SUSD → redeem USDC collateral
     * @param susdAmount   Amount of $SUSD to burn (18-decimal)
     */
    function burn(uint256 susdAmount) external nonReentrant whenNotPaused {
        require(susdAmount > 0, "SUSD: zero amount");
        require(balanceOf(msg.sender) >= susdAmount, "SUSD: insufficient balance");

        // Burn $SUSD from user
        _burn(msg.sender, susdAmount);

        // USDC to return (face value ÷ CR, minus redemption fee), back to 6 dec
        uint256 usdcGross = (susdAmount / 1e12) * 100_00 / collateralRatio;
        uint256 fee       = (usdcGross * burnFee) / 10_000;
        uint256 usdcNet   = usdcGross - fee;

        require(totalCollateral >= usdcGross, "SUSD: insufficient collateral");
        totalCollateral -= usdcGross;

        // Burn SGOV if held (1% of susdAmount)
        uint256 sgovBurned = 0;
        if (address(sgovToken) != address(0)) {
            sgovBurned = (susdAmount * sgovRewardRate) / 10_000;
            // Best-effort burn — don't revert if user has none
            try sgovToken.burnFrom(msg.sender, sgovBurned) {} catch { sgovBurned = 0; }
        }

        collateralToken.safeTransfer(msg.sender, usdcNet);

        emit Burned(msg.sender, susdAmount, usdcNet, sgovBurned, fee);
    }

    // ── View ───────────────────────────────────────────────────────────
    function collateralBalance() external view returns (uint256) {
        return collateralToken.balanceOf(address(this));
    }

    function healthRatio() external view returns (uint256) {
        uint256 supply = totalSupply();
        if (supply == 0) return type(uint256).max;
        // Returns value * 100 (e.g., 11500 = 115.00%)
        return (totalCollateral * 1e12 * 100_00) / supply;
    }
}

SGov.sol — Governance Token

ERC-20 · v3.1.0
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title SGov
 * @notice SMART USD Governance & Volatility Absorber Token — v3.1.0
 * @dev    ERC20 + staking + yield distribution + governance power
 */

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

contract SGov is ERC20, AccessControl, ReentrancyGuard {
    using SafeERC20 for IERC20;

    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant BURNER_ROLE = keccak256("BURNER_ROLE");

    // ── Staking State ──────────────────────────────────────────────────
    struct StakeInfo {
        uint256 amount;
        uint256 rewardDebt;   // snapshot of rewardPerToken at stake time
        uint256 pendingYield; // accrued but not yet claimed
        uint256 stakedAt;
    }

    mapping(address => StakeInfo) public stakes;
    uint256 public totalStaked;
    uint256 public rewardPerTokenStored;  // scaled by 1e18
    uint256 public lastRewardTimestamp;
    uint256 public rewardRate;            // wei per second distributed to stakers

    uint256 private constant PRECISION = 1e18;

    // ── Supply Cap ─────────────────────────────────────────────────────
    uint256 public constant MAX_SUPPLY = 100_000_000e18; // 100M SGOV

    // ── Events ─────────────────────────────────────────────────────────
    event Staked(address indexed user, uint256 amount);
    event Unstaked(address indexed user, uint256 amount);
    event YieldClaimed(address indexed user, uint256 amount);
    event RewardRateUpdated(uint256 newRate);

    constructor(address admin) ERC20("Smart Governance", "SGOV") {
        require(admin != address(0), "SGOV: zero admin");
        _grantRole(DEFAULT_ADMIN_ROLE, admin);
        _grantRole(MINTER_ROLE, admin);
        _grantRole(BURNER_ROLE, admin);
        lastRewardTimestamp = block.timestamp;
    }

    // ── Minting / Burning ──────────────────────────────────────────────
    function mintReward(address to, uint256 amount) external onlyRole(MINTER_ROLE) {
        require(totalSupply() + amount <= MAX_SUPPLY, "SGOV: supply cap");
        _mint(to, amount);
    }

    function burnFrom(address from, uint256 amount) external onlyRole(BURNER_ROLE) {
        _burn(from, amount);
    }

    // ── Reward Accounting ──────────────────────────────────────────────
    function _updateReward(address user) internal {
        rewardPerTokenStored = _rewardPerToken();
        lastRewardTimestamp  = block.timestamp;
        if (user != address(0)) {
            stakes[user].pendingYield += _earned(user);
            stakes[user].rewardDebt   = rewardPerTokenStored;
        }
    }

    function _rewardPerToken() internal view returns (uint256) {
        if (totalStaked == 0) return rewardPerTokenStored;
        return rewardPerTokenStored
            + ((block.timestamp - lastRewardTimestamp) * rewardRate * PRECISION)
            / totalStaked;
    }

    function _earned(address user) internal view returns (uint256) {
        return (stakes[user].amount
            * (_rewardPerToken() - stakes[user].rewardDebt))
            / PRECISION;
    }

    // ── Stake ──────────────────────────────────────────────────────────
    function stake(uint256 amount) external nonReentrant {
        require(amount > 0, "SGOV: zero amount");
        require(balanceOf(msg.sender) >= amount, "SGOV: insufficient balance");

        _updateReward(msg.sender);

        _transfer(msg.sender, address(this), amount);
        stakes[msg.sender].amount  += amount;
        stakes[msg.sender].stakedAt = block.timestamp;
        totalStaked += amount;

        emit Staked(msg.sender, amount);
    }

    // ── Unstake ────────────────────────────────────────────────────────
    function unstake(uint256 amount) external nonReentrant {
        require(amount > 0, "SGOV: zero amount");
        require(stakes[msg.sender].amount >= amount, "SGOV: insufficient staked");

        _updateReward(msg.sender);

        stakes[msg.sender].amount -= amount;
        totalStaked -= amount;
        _transfer(address(this), msg.sender, amount);

        emit Unstaked(msg.sender, amount);
    }

    // ── Claim Yield ────────────────────────────────────────────────────
    function claimYield() external nonReentrant {
        _updateReward(msg.sender);
        uint256 yield = stakes[msg.sender].pendingYield;
        require(yield > 0, "SGOV: no yield");
        stakes[msg.sender].pendingYield = 0;

        // Yield is minted as new SGOV (protocol revenue share)
        require(totalSupply() + yield <= MAX_SUPPLY, "SGOV: supply cap");
        _mint(msg.sender, yield);

        emit YieldClaimed(msg.sender, yield);
    }

    // ── View ───────────────────────────────────────────────────────────
    function stakedBalance(address user) external view returns (uint256) {
        return stakes[user].amount;
    }

    function pendingYield(address user) external view returns (uint256) {
        return stakes[user].pendingYield + _earned(user);
    }

    function votingPower(address user) external view returns (uint256) {
        // Voting power = liquid + staked balance
        return balanceOf(user) + stakes[user].amount;
    }

    // ── Admin ──────────────────────────────────────────────────────────
    function setRewardRate(uint256 newRate) external onlyRole(DEFAULT_ADMIN_ROLE) {
        _updateReward(address(0));
        rewardRate = newRate;
        emit RewardRateUpdated(newRate);
    }
}

Airdrop.sol — Testnet Faucet

Testnet Only
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title SmartUSD Testnet Airdrop Faucet
 * @notice One-time faucet: 500 SUSD + 50 SGOV + 1000 MockUSDC per wallet
 * @dev    Deploy on Sepolia only. Disable before mainnet launch.
 */

import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";

interface IMintable {
    function mintReward(address to, uint256 amount) external;
    function mint(address to, uint256 amount) external;      // MockUSDC
}

contract SmartUSDAirdrop is Ownable, ReentrancyGuard {

    IMintable public susdToken;
    IMintable public sgovToken;
    IMintable public mockUSDC;

    uint256 public susdAmount  = 500e18;   // 500 SUSD
    uint256 public sgovAmount  = 50e18;    // 50 SGOV
    uint256 public usdcAmount  = 1_000e6;  // 1000 USDC (6 dec)

    mapping(address => bool) public claimed;
    uint256 public totalClaimed;
    bool    public active = true;

    // ── Events ─────────────────────────────────────────────────────────
    event AirdropClaimed(address indexed user, uint256 susd, uint256 sgov, uint256 usdc);
    event AirdropDeactivated();

    constructor(
        address _susd,
        address _sgov,
        address _usdc,
        address _owner
    ) Ownable(_owner) {
        require(_susd != address(0) && _sgov != address(0) && _usdc != address(0), "Zero address");
        susdToken = IMintable(_susd);
        sgovToken = IMintable(_sgov);
        mockUSDC  = IMintable(_usdc);
    }

    // ── Claim ──────────────────────────────────────────────────────────
    function claim() external nonReentrant {
        require(active,             "Airdrop: not active");
        require(!claimed[msg.sender], "Airdrop: already claimed");

        claimed[msg.sender] = true;
        totalClaimed++;

        // Mint all three tokens to caller
        susdToken.mintReward(msg.sender, susdAmount);
        sgovToken.mintReward(msg.sender, sgovAmount);
        mockUSDC.mint(msg.sender,  usdcAmount);

        emit AirdropClaimed(msg.sender, susdAmount, sgovAmount, usdcAmount);
    }

    // ── View ───────────────────────────────────────────────────────────
    function isEligible(address user) external view returns (bool) {
        return active && !claimed[user];
    }

    // ── Admin ──────────────────────────────────────────────────────────
    function setAmounts(uint256 _susd, uint256 _sgov, uint256 _usdc) external onlyOwner {
        susdAmount = _susd;
        sgovAmount = _sgov;
        usdcAmount = _usdc;
    }

    function deactivate() external onlyOwner {
        active = false;
        emit AirdropDeactivated();
    }
}
← Back to App 🚀 Deploy Guide →