Page cover

bring-forwardDocumentación técnica del contrato Optipool

Para la transparencia y la automomia de BNBFund, Presenta el Smart Contract Documentado que hace cada Funcion dentro del codigo.

/**
 *Submitted for verification at BscScan.com on 2026-02-11
*/

/**
 *Submitted for verification at testnet.bscscan.com on 2026-02-11
*/

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

interface IBEP20 {
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    function transfer(address to, uint256 amount) external returns (bool);
    function balanceOf(address owner) external view returns (uint256);
    function decimals() external view returns (uint8);
}

contract Optipool {

    struct Participant {
        uint256 referrerId;
    }

    struct Investment {
        uint256 amount;
        uint256 startTimestamp;
        uint256 duration;
        uint256 reward;
        bool active;
        uint8 poolId;
    }

    address public immutable owner;
    IBEP20 public immutable usdt;
    uint8  public immutable usdtDecimals;

    // 👉 NUEVO
    address public immutable marketplaceWallet;

    uint256 public nextId = 2;

    mapping(address => uint256) public walletToId;
    mapping(uint256 => address) public idToWallet;
    mapping(address => Participant) public participants;

    mapping(address => uint256) public userReferrer;
    mapping(address => mapping(uint256 => address)) public directReferrals;
    mapping(address => uint256) public directReferralCount;

    uint8 public constant POOLS = 4;

    struct PoolConfig {
        uint256 duration;
        uint16 userRewardBP;
    }

    mapping(uint8 => PoolConfig) public pools;

    uint256 public investmentCounter;

    // usuario => investmentId => data
    mapping(address => mapping(uint256 => Investment)) public userInvestments;

    // capital activo por usuario
    mapping(address => uint256) public totalActiveDeposits;

    bool private locked;

    event Deposit(
        address indexed user,
        uint256 indexed investmentId,
        uint256 amount,
        uint8 poolId,
        uint256 reward,
        uint256 referrerId
    );

    event Withdraw(
        address indexed user,
        uint256 indexed investmentId,
        uint256 amountWithdrawn
    );

    event ReferralBonus(
        address indexed referrer,
        address indexed referee,
        uint256 amount,
        uint8 poolId
    );

    // 👉 opcional, solo informativo para frontend
    event MarketplaceFunded(uint256 amount);

    modifier nonReentrant() {
        require(!locked, "Reentrancy");
        locked = true;
        _;
        locked = false;
    }

    // 👉 constructor con wallet del marketplace
    constructor(address _usdt, address _marketplaceWallet) {
        require(_usdt != address(0), "USDT required");
        require(_marketplaceWallet != address(0), "MARKETPLACE required");

        owner = msg.sender;
        usdt = IBEP20(_usdt);
        usdtDecimals = IBEP20(_usdt).decimals();
        marketplaceWallet = _marketplaceWallet;

        walletToId[msg.sender] = 1;
        idToWallet[1] = msg.sender;
        participants[msg.sender] = Participant(1);

        pools[0] = PoolConfig(7 days, 200);
        pools[1] = PoolConfig(14 days, 400);
        pools[2] = PoolConfig(21 days, 700);
        pools[3] = PoolConfig(30 days, 1000);
    }

    function _safeTransferFrom(address token, address from, address to, uint256 amount) private {
        (bool success, bytes memory data) =
            token.call(abi.encodeWithSelector(IBEP20.transferFrom.selector, from, to, amount));
        require(success && (data.length == 0 || abi.decode(data,(bool))), "transferFrom failed");
    }

    function _safeTransfer(address token, address to, uint256 amount) private {
        (bool success, bytes memory data) =
            token.call(abi.encodeWithSelector(IBEP20.transfer.selector, to, amount));
        require(success && (data.length == 0 || abi.decode(data,(bool))), "transfer failed");
    }

    function _register(address user, uint256 referrerId) internal returns(uint256,uint256) {

        if(walletToId[user] != 0){
            return (walletToId[user], participants[user].referrerId);
        }

        uint256 refId = referrerId;

        if(refId == 0 || idToWallet[refId] == address(0) || refId == nextId){
            refId = 1;
        }

        address refAddr = idToWallet[refId];
        if(refAddr == user){
            refId = 1;
            refAddr = idToWallet[1];
        }

        uint256 newId = nextId++;

        walletToId[user] = newId;
        idToWallet[newId] = user;
        participants[user] = Participant(refId);
        userReferrer[user] = refId;

        uint256 idx = directReferralCount[refAddr];
        directReferrals[refAddr][idx] = user;
        directReferralCount[refAddr] = idx + 1;

        return (newId, refId);
    }

    function _getReferralPercent(address referrer) internal view returns(uint16) {

        uint256 total = totalActiveDeposits[referrer];
        uint256 d = 10 ** usdtDecimals;

        if (total >= 5000 * d) return 1000;
        if (total >= 2500 * d) return 800;
        if (total >= 1000 * d) return 600;
        if (total >= 500  * d) return 400;
        if (total >= 250  * d) return 300;
        if (total >= 50   * d) return 200;

        return 0;
    }

    function deposit(uint256 amount, uint8 poolId, uint256 referrerId) external nonReentrant {

        require(poolId < POOLS, "Invalid pool");

        uint256 d = 10 ** usdtDecimals;

        require(amount >= 1 * d, "Min 1 USDT");
        require(amount <= 7000 * d, "Max 7000");
        require(totalActiveDeposits[msg.sender] + amount <= 7000 * d, "User limit");

        (, uint256 assignedRefId) = _register(msg.sender, referrerId);

        PoolConfig memory pool = pools[poolId];
        uint256 userReward = amount * pool.userRewardBP / 10000;

        address referrer = idToWallet[assignedRefId];
        uint16 refBP = _getReferralPercent(referrer);
        uint256 refBonus = amount * refBP / 10000;

        _safeTransferFrom(address(usdt), msg.sender, address(this), amount);

        investmentCounter++;
        uint256 invId = investmentCounter;

        userInvestments[msg.sender][invId] = Investment({
            amount: amount,
            startTimestamp: block.timestamp,
            duration: pool.duration,
            reward: userReward,
            active: true,
            poolId: poolId
        });

        totalActiveDeposits[msg.sender] += amount;

        if(refBonus > 0){
            require(usdt.balanceOf(address(this)) >= refBonus, "No referral funds");
            _safeTransfer(address(usdt), referrer, refBonus);
            emit ReferralBonus(referrer, msg.sender, refBonus, poolId);
        }

        emit Deposit(
            msg.sender,
            invId,
            amount,
            poolId,
            userReward,
            assignedRefId
        );
    }

    function withdraw(uint256 investmentId) external nonReentrant {

        Investment storage inv = userInvestments[msg.sender][investmentId];

        require(inv.active, "Not active");
        require(block.timestamp >= inv.startTimestamp + inv.duration, "Locked");

        uint256 totalOut = inv.amount + inv.reward;

        require(usdt.balanceOf(address(this)) >= totalOut, "No balance");

        inv.active = false;
        totalActiveDeposits[msg.sender] -= inv.amount;

        _safeTransfer(address(usdt), msg.sender, totalOut);

        emit Withdraw(msg.sender, investmentId, totalOut);
    }

    /* -------------------------------------------------
       👉 FUNCIÓN PARA EL MARKETPLACE
       La wallet autorizada transfiere USDT al contrato
       (no altera participaciones ni rewards fijos)
    ------------------------------------------------- */
    function fundFromMarketplace(uint256 amount) external nonReentrant {

        require(msg.sender == marketplaceWallet, "ONLY_MARKETPLACE");
        require(amount > 0, "ZERO_AMOUNT");

        _safeTransferFrom(address(usdt), msg.sender, address(this), amount);

        emit MarketplaceFunded(amount);
    }

    function getTimeToUnlock(address user, uint256 investmentId) external view returns(uint256){
        Investment memory inv = userInvestments[user][investmentId];
        if(!inv.active) return 0;
        if(block.timestamp >= inv.startTimestamp + inv.duration) return 0;
        return (inv.startTimestamp + inv.duration) - block.timestamp;
    }

    function getCurrentReferralBonusPercent(address referrer) external view returns(uint16){
        return _getReferralPercent(referrer);
    }

    function getTotalContractBalance() external view returns(uint256){
        return usdt.balanceOf(address(this));
    }

    receive() external payable { revert(); }
    fallback() external payable { revert(); }
}

Interface IBEP20

Interfaz mínima para interactuar con un token tipo BEP-20 (USDT en BSC).

Se utiliza para:

  • recibir depósitos

  • pagar retiros

  • pagar referidos

  • leer decimales


2️⃣ Structs


🔹 Participant

Representa un usuario registrado.

Campos:

  • referrerId → ID del sponsor

  • active → solo indica que existe / está activo

  • currentPoolsIndex → último pool usado

  • poolsAtCurrentDeposit → actualmente no se usa en ninguna lógica


🔹 Investment

Representa una inversión individual.

Campos:

  • monto

  • momento de inicio

  • duración

  • reward fijo ya calculado

  • estado activo

  • pool usado


3️⃣ Sistema de IDs y referidos

Sistema interno para asignar un ID incremental a cada wallet.


Sistema de:

  • sponsor directo

  • listado de referidos directos


⚠️ No existe multilevel, solo nivel 1.


4️⃣ Variables globales principales


Contador de IDs.


Owner del contrato.


Token utilizado para todo el sistema.


5️⃣ Pools


Cantidad de pools.


Configuración de cada pool.

⚠️ contractBP no se usa en ninguna parte.


Configuración real de pools.

Se inicializa en el constructor.


6️⃣ Inversiones


Cada usuario guarda sus inversiones usando un ID global.


Guarda solo el último ID de inversión del usuario.


Contador global de inversiones.


7️⃣ Reentrancy guard

Usado por el modifier:


8️⃣ Gestión de wallets de liquidez


Define qué wallets están autorizadas a inyectar fondos del marketplace.


9️⃣ Acumulador de depósitos activos

Registra cuánto capital activo tiene cada usuario.

Se usa exclusivamente para:

  • límite de 7,000 USDT por usuario

  • cálculo dinámico de referidos


🔟 Eventos

Eventos para:

  • depósitos

  • retiros

  • pago de referidos

  • autorización de wallets

  • depósitos del marketplace


1️⃣1️⃣ Constructor

Acciones:

  • fija el owner

  • fija el token USDT

  • asigna al owner el ID 1

  • inicializa pools


⚠️ Los pools son inmutables después del deploy.


1️⃣2️⃣ Modifiers


onlyOwner

Solo el owner puede ejecutar la función.


nonReentrant

Protección básica contra reentradas.


onlyLiquidityWallet

Solo wallets autorizadas.


1️⃣3️⃣ Funciones internas de transferencia


Funciones seguras de transferencia usando call.


1️⃣4️⃣ Administración de wallets de liquidez


authorizeLiquidityAddress

Permite al owner autorizar wallets del marketplace.


revokeLiquidityAddress

Revoca wallets autorizadas.


⚠️ No mueve fondos.


1️⃣5️⃣ Registro de usuarios


Función interna que:

  • asigna ID si el usuario no existe

  • valida el referrer

  • si el referrer es inválido → asigna ID 1

  • registra relación directa


Reglas importantes:

  • no permite contratos

  • no permite autorreferidos

  • no permite referrers inexistentes


1️⃣6️⃣ Funciones de lectura

  • getParticipantId

  • getAddressById

  • getDirectReferral

  • getDirectReferralCount

Solo lecturas.


1️⃣7️⃣ Porcentaje dinámico de referidos


Calcula el % en base a:

Rangos:

  • 2% a 10%


1️⃣8️⃣ Depósito de usuarios


Pasos reales:

  1. prohíbe que sea wallet de liquidez

  2. valida pool

  3. valida que sea EOA

  4. valida mínimo 1 USDT

  5. valida máximo 7,000 USDT

  6. valida límite total por usuario

  7. registra usuario

  8. calcula reward fijo

  9. calcula bono de referido

  10. transfiere USDT al contrato

  11. paga bono de referido inmediatamente

  12. crea la inversión

  13. actualiza contadores

  14. emite evento


Puntos clave:

  • el reward queda fijo desde el inicio

  • el bono de referido se paga en el momento del depósito


1️⃣9️⃣ Inyección de fondos del marketplace


Solo wallets autorizadas.

Hace:

  1. valida pool

  2. valida montos

  3. transfiere USDT al contrato

  4. registra una inversión con reward = 0


Notas:

  • no aumenta totalActiveDeposits

  • esas inversiones no generan ningún beneficio

  • esas wallets no pueden retirar


2️⃣0️⃣ Retiro de usuarios


Reglas:

  1. wallets de liquidez no pueden retirar

  2. la inversión debe estar activa

  3. debe haber vencido el tiempo

  4. el contrato debe tener balance suficiente

  5. paga:

  1. marca la inversión como inactiva

  2. reduce totalActiveDeposits


No existe retiro parcial.


2️⃣1️⃣ Funciones de consulta


getActiveInvestment

Devuelve solo la última inversión registrada.


getTotalContractBalance

Devuelve balance del token USDT en el contrato.


getTimeToUnlock

Devuelve tiempo restante de una inversión.


getCurrentReferralBonusPercent

Devuelve el % actual de un usuario.


2️⃣2️⃣ receive / fallback

Rechaza cualquier envío de BNB.



❓ ¿El owner puede drenar los fondos del contrato?

👉 Respuesta directa y honesta:

NO. En este contrato el owner NO tiene ninguna función que permita retirar fondos.


Verificación técnica

No existe ninguna función como:

  • withdrawOwner

  • emergencyWithdraw

  • rescueTokens

  • transferBalance

  • sweep

  • claim

  • recover


El owner únicamente puede:


Ambas funciones:

  • solo modifican un mapping

  • no mueven tokens

  • no interactúan con el balance


¿Puede el owner drenar los fondos indirectamente?

🔴 No.

Porque:

  • el owner no puede llamar withdraw si no tiene inversiones

  • aunque tenga inversiones, solo puede retirar sus propias inversiones

  • no existe ninguna función administrativa de pago


¿Puede el owner crear una wallet de liquidez y luego retirar?

🔴 Tampoco.

Las wallets de liquidez:

en withdraw.

Por lo tanto:

una wallet autorizada nunca puede retirar.


⚠️ Única forma en la que los fondos salen del contrato

Los fondos solo salen por:

  1. withdraw() → usuarios normales

  2. _safeTransfer() en pago de referidos

No existe ninguna otra salida.


✅ Conclusión final

Desde el punto de vista del código que has mostrado:

✔ El owner NO puede drenar los fondos.

✔ No existe función de emergencia para retirar.

✔ No existe puerta trasera administrativa.

✔ No existe privilegio de extracción.

Last updated