SolidarityBasket tam Solidity kaynak kodu. Adres: 0x555400DE5f287bB7E2a7F1C1E26fEa9cDCdB2877 · MIT · ^0.8.20
Tüm dosyayı SolidarityBasket.sol olarak kopyalayın.
// 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");
}
}