S

Solidarity Basket

All articles

Smart Contract / English

English

Contract Source Code

This page publishes the full Solidity source of the SolidarityBasket vault. Anyone can read it, copy it, and compare it with the bytecode on BNB Smart Chain.

Deployed address: 0x555400DE5f287bB7E2a7F1C1E26fEa9cDCdB2877 · License: MIT · Solidity ^0.8.20

Use the button below to copy the entire file into SolidarityBasket.sol.

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

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

/**
 * @title SolidarityBasket
 * @notice "الخزنة الآمنة" - العقد الذكي الوحيد المسؤول عن استقبال وسحب الأموال.
 *
 * مبدأ التصميم المتعمّد:
 * - هذا العقد لا يعرف شيئاً عن "الدولار". كل تحويل (USD -> BNB أو USD -> USDT)
 *   يحدث في الواجهة الأمامية (Off-chain) قبل إرسال المعاملة.
 * - العقد يستقبل فقط الرقم النهائي الجاهز (Wei أو أصغر وحدة USDT) ويسجّله.
 * - لا يوجد Oracle داخل العقد لتقليل التعقيد وتقليل مساحة الهجوم (attack surface).
 */
contract SolidarityBasket is Ownable, ReentrancyGuard {
    using SafeERC20 for IERC20;

    // عنوان عقد USDT على BSC (BEP-20) - يُمرَّر عند النشر، وليس مُثبَّتاً بالكود
    // حتى نقدر نستخدم نفس العقد على Testnet بعنوان USDT تجريبي مختلف.
    IERC20 public immutable usdtToken;

    // رصيد كل مستفيد (صاحب صفحة) منفصل داخل نفس الخزنة
    // هذا يمهّد لمرحلة "تعدد الصفحات" المستقبلية دون تعديل العقد لاحقاً
    mapping(address => uint256) public bnbBalanceOf;
    mapping(address => uint256) public usdtBalanceOf;

    event BNBDeposited(address indexed donor, address indexed beneficiary, uint256 amountWei, uint256 timestamp);
    event USDTDeposited(address indexed donor, address indexed beneficiary, uint256 amountRaw, uint256 timestamp);
    event BNBWithdrawn(address indexed beneficiary, address indexed to, uint256 amountWei);
    event USDTWithdrawn(address indexed beneficiary, address indexed to, uint256 amountRaw);

    error ZeroAmount();
    error InsufficientBalance();
    error ZeroAddress();

    constructor(address _usdtToken, address _initialOwner) Ownable(_initialOwner) {
        if (_usdtToken == address(0)) revert ZeroAddress();
        usdtToken = IERC20(_usdtToken);
    }

    /**
     * @notice إيداع BNB لصالح مستفيد معيّن (صاحب صفحة).
     * @param beneficiary عنوان محفظة المستفيد الذي سيُحسب له الرصيد.
     * @dev المبلغ يُحدَّد بـ msg.value (Wei) الذي حسبته الواجهة الأمامية مسبقاً
     *      من السعر اللحظي BNB/USD.
     */
    function depositBNB(address beneficiary) external payable nonReentrant {
        if (beneficiary == address(0)) revert ZeroAddress();
        if (msg.value == 0) revert ZeroAmount();

        bnbBalanceOf[beneficiary] += msg.value;
        emit BNBDeposited(msg.sender, beneficiary, msg.value, block.timestamp);
    }

    /**
     * @notice إيداع USDT لصالح مستفيد معيّن.
     * @param beneficiary عنوان محفظة المستفيد.
     * @param amount الكمية بأصغر وحدة (raw units) - تنبيه: USDT على BSC = 18 decimals.
     * @dev يجب أن يكون المتبرع قد نفّذ usdtToken.approve(address(this), amount) مسبقاً.
     */
    function depositUSDT(address beneficiary, uint256 amount) external nonReentrant {
        if (beneficiary == address(0)) revert ZeroAddress();
        if (amount == 0) revert ZeroAmount();

        // نمط Checks-Effects-Interactions: نحدّث الحالة قبل نقل التوكن
        usdtBalanceOf[beneficiary] += amount;

        usdtToken.safeTransferFrom(msg.sender, address(this), amount);
        emit USDTDeposited(msg.sender, beneficiary, amount, block.timestamp);
    }

    /**
     * @notice سحب BNB - المستفيد يسحب رصيده الخاص فقط.
     * @dev لاحظ: هذه ليست onlyOwner، بل كل مستفيد يسحب رصيده هو فقط.
     *      هذا أكثر أماناً من جعل "مدير واحد" يسحب أموال الجميع.
     */
    function withdrawBNB(address payable to, uint256 amount) external nonReentrant {
        if (to == address(0)) revert ZeroAddress();
        if (amount == 0) revert ZeroAmount();
        if (bnbBalanceOf[msg.sender] < amount) revert InsufficientBalance();

        bnbBalanceOf[msg.sender] -= amount;

        (bool success, ) = to.call{value: amount}("");
        require(success, "BNB transfer failed");

        emit BNBWithdrawn(msg.sender, to, amount);
    }

    /**
     * @notice سحب USDT - المستفيد يسحب رصيده الخاص فقط.
     */
    function withdrawUSDT(address to, uint256 amount) external nonReentrant {
        if (to == address(0)) revert ZeroAddress();
        if (amount == 0) revert ZeroAmount();
        if (usdtBalanceOf[msg.sender] < amount) revert InsufficientBalance();

        usdtBalanceOf[msg.sender] -= amount;
        usdtToken.safeTransfer(to, amount);

        emit USDTWithdrawn(msg.sender, to, amount);
    }

    /**
     * @notice دالة طوارئ فقط لمالك العقد (لحالات نادرة: خطأ في عنوان مستفيد، إلخ)
     *      لا تُستخدم للسحب الروتيني - السحب الروتيني يكون عبر withdrawBNB/withdrawUSDT
     *      من قبل المستفيد نفسه فقط.
     */
    function emergencyWithdrawStuckBNB(address payable to, uint256 amount) external onlyOwner nonReentrant {
        if (to == address(0)) revert ZeroAddress();
        require(amount <= address(this).balance, "Exceeds contract balance");
        (bool success, ) = to.call{value: amount}("");
        require(success, "Transfer failed");
    }
}