Libro blanco / Documentación Técnica FIV LATAM COIN (FIV)

Resumen Ejecutivo

FIV LATAM COIN es un token ERC-20 avanzado diseñado para potenciar una economía descentralizada y fortalecer la adopción de pagos digitales en empresas, marketplaces y plataformas comerciales.

Gracias a su integración con negocios, FIV no solo ofrece un mecanismo de inversión, sino que también brinda descuentos exclusivos, beneficios comerciales y una red sólida de aceptación, fortaleciendo su utilidad como método de pago.

Con un enfoque claro hacia la seguridad y sostenibilidad, FIV se posiciona como una herramienta fundamental en el ecosistema de inversión y tecnología blockchain de FIV LATAM, un proyecto que busca el crecimiento y desarrollo económico digital en Latinoamérica y a nivel global.

Este documento describe la arquitectura, las funcionalidades clave y las aplicaciones prácticas del token FIV, destacando su papel en la inclusión financiera y la innovación digital.

1. Equipo

– Renzo Montero: Fundador, CEO y desarrollador de software
– Brayán Montero: Director de Operaciones
– Vianni Torrealba: Marketing Director

2. Visión y Misión

🔹 Visión: Empoderar a individuos, empresas y comunidades mediante herramientas financieras descentralizadas, transparentes y seguras.

🔹 Misión: Crear un ecosistema blockchain autosostenible con incentivos reales para sus participantes, mediante un token robusto, gobernado por su comunidad y respaldado por tecnología de última generación.

3. El Problema y la Solución

Problemas actuales:

– Falta de confianza en sistemas financieros centralizados.
– Inflación y devaluación en monedas fiat.
– Limitación para acceder a inversiones de proyectos cripto
– Limitación participación democrática en decisiones de proyectos cripto.

Soluciones de FIV LATAM COIN:

– Infraestructura propia en desarrollo, incluyendo wallet no custodia.
– Staking con recompensas justas y sostenibles.
– Adopción en comercios y plataformas digitales que aceptarán FIV como método de pago con descuentos exclusivos.                                                                                                                              – Vesting programado para asegurar una liberación gradual y sostenible de tokens a largo plazo.

4. Tecnología y Arquitectura del Token

FIV LATAM COIN es un token ERC-20 Upgradeable desarrollado con OpenZeppelin, con las siguientes características:
📅 Token estándar ERC-20 con actualizaciones sin perder datos.
🔹 Sistema de staking con recompensas ajustables.
📈 Eventos para trazabilidad de transacciones, votaciones y recompensas.
🔠 Compatible con Ethereum, BNB Chain, Polygon, Avalanche, entre otras redes EVM.

5. Economía del Token

Característica Valor
Nombre del token  FIV LATAM COIN
Símbolo FIV
Suministro inicial 100,000,000 FIV
Suministro máximo 220,000,000 FIV
Tasa de recompensas 20% APR (ajustable)
Límite total recompensas de 20,000,000 FIV
Incentivos para negocios Empresas que acepten FIV recibirán bonificaciones y descuentos

6. Sistema de Staking y Recompensas

🔹 Los holders pueden bloquear sus tokens para participar en el staking.
🔹 Las recompensas se calculan por tiempo de staking y monto bloqueado.
🔹 Límite total de recompensas: 20M FIV.
🔹 Tasa de recompensa predeterminada: 20% APR (ajustable por el owner).
🔹 Penalizaciones por retiro temprano:

  • Se debe esperar mínimo 24 horas para poder reclamar recompensas.
  • Unstake está restringido al menos 24 horas después del stake.
  • Penalización del 10% si el usuario retira antes de 7 días, monto penalizado enviado a treasuryWallet.
  • En caso de emergencia (contrato pausado), los usuarios pueden hacer unstake sin penalización ni recompensa.

7. Roadmap del Proyecto

Fase Objetivos
Q1 2025 🔹 Desarrollo del contrato inteligente
🔹 Despliegue en testnet
🔹 Revisión técnica

Q2 2025 🔄 Auditoría externa
🚀 Despliegue en mainnet
🌐 Sitio web oficial y whitepaper

Q3 2025 📢 Campañas de marketing
🧹 Integración con exchanges DEX
💬 Comunidad y votaciones

Q4 2025 🛠️ Gobernanza activa y primeras propuestas
📈 Listado en CoinMarketCap y CoinGecko

Q1 2026 🧱 Implementación de casos de uso reales (pagos, marketplace)
🤝 Alianzas estratégicas

8. Conclusiones

FIV LATAM COIN es más que un token: es una herramienta poderosa para la construcción de un sistema financiero descentralizado, justo y transparente. Con una arquitectura sólida, mecanismos de recompensas e incentivos claros, FIV busca liderar el cambio hacia una nueva economía digital en América Latina y el mundo.

9. Anexos técnicos

  • Lenguaje: Solidity 0.8.20
  • Bibliotecas usadas: OpenZeppelin (ERC20, Ownable, Pausable, ReentrancyGuard, UUPSUpgradeable)
  • Interfaz compatible: ERC20, IERC20Metadata

Eventos definidos:

  • Staked(address indexed user, uint256 amount)
  • Unstaked(address indexed user, uint256 amount, uint256 penalty)
  • RewardClaimed(address indexed user, uint256 amount)
  • TokensReleased(address indexed user, uint256 amount) (vesting)
  • TreasuryWalletUpdated(address indexed oldWallet, address indexed newWallet)

10. Información del Contrato en BNB Chain  

Para asegurar la transparencia total del proyecto, el contrato inteligente de FIV LATAM COIN ha sido desplegado bajo un patrón de upgradeabilidad UUPS.

Ambas direcciones están verificadas en BscScan y accesibles públicamente para inspección del código, historial de transacciones, eventos emitidos y cualquier interacción on-chain.

📄 Ver el código completo del Smart Contract

// SPDX-License-Identifier: MIT
/**
 * @title FIV LATAM Coin (FIV)
 * @dev ERC20 token contract featuring staking, vesting, and upgradeable mechanisms designed for the FIV ecosystem.
 *
 * Features:
 * - Staking system with reward distribution and early withdrawal penalties
 * - Annual token vesting with scheduled releases to the treasury wallet
 * - Emergency pause and unpause functionality (affects staking only)
 * - UUPS (Universal Upgradeable Proxy Standard) upgradeability support
 *
 * Key Parameters:
 * - Initial supply: 100,000,000 FIV
 * - Maximum supply cap: 220,000,000 FIV
 * - Reward pool cap: 20,000,000 FIV
 * - Maximum staking reward rate: 20% per year (accrues daily)
 *
 * Additional Functionality:
 * - Rewards accrue daily and can be claimed once every 24 hours
 * - 10% penalty applied for early unstaking (before 7 days)
 * - Minimum staking duration of 24 hours
 * - 25-year vesting schedule utilizing decreasing annual divisors
 *
 * Authorization & Security:
 * - Ownable pattern for administrative control, limited to critical operations
 * - Upgrades restricted to the owner through the UUPS pattern
 * - Treasury wallet address configurable by the owner
 *
 * Governance:
 * - Multisignature governance managed externally (via Gnosis Safe multisig)
 *
 * Developed by: Renzo Montero  
 * Website: https://fivlatam.com/  
 * Year: 2025
 */
pragma solidity ^0.8.20;

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/AddressUpgradeable.sol";

contract FIVLATAMCoin is Initializable, ERC20Upgradeable, OwnableUpgradeable, PausableUpgradeable, ReentrancyGuardUpgradeable, ERC20PermitUpgradeable, UUPSUpgradeable, ERC165Upgradeable {
    // ===================== CONSTANTS =====================
    uint256 public constant INITIAL_SUPPLY = 100000000 * 10**18;  // Minted at deployment
    uint256 public constant VESTING_SUPPLY = 100000000 * 10**18;  // Reference only: intended for 25-year vesting, released gradually
    uint256 public constant MAX_SUPPLY = 220000000 * 10**18;  // Hard cap
    uint256 public constant MAX_REWARD_RATE = 20;  // Max 20% APY
    uint256 public constant REWARD_SUPPLY_LIMIT = 20000000 * 10**18;  // Max tokens for staking rewards
    uint256 public constant RELEASE_INTERVAL = 365 days;  // Vesting release frequency
    uint256 public constant MIN_STAKE_DURATION = 1 days;  // Minimum lock period
    uint256 public constant MIN_STAKE_AMOUNT = 1e15; // Minimum stake: 0.001 FIV
    uint256 public constant EARLY_UNSTAKE_DURATION = 7 days;  // Duration within which penalty applies
    uint256 public constant EARLY_UNSTAKE_PENALTY_RATE = 10;  // 10% penalty for early unstake
    uint256 public constant CLAIM_INTERVAL = 1 days;  // Time between allowed reward claims
    uint256 public constant MAX_ACCUMULABLE_REWARD = 100000 * 1e18;  // Max pending reward per stake to avoid overflow
    uint256 public constant MAX_ACCUMULATION_DURATION = 90 days;

    // ===================== CONFIGURABLE VARIABLES =====================
    uint256 public rewardRate;
    uint256[] private releaseDivisors;
    uint256 public minReward;

    // ===================== VESTING MONITORING =====================
    uint256 public currentYear;
    uint256 public lastReleaseTime;

    // ===================== STAKING STATE =====================
    /// @dev Represents an individual stake position tied to a specific index per user.
    struct Stake  {
        uint256 amount;
        uint256 stakedAt;
        uint256 lastClaimed;
        uint256 pendingReward;
    }
    
    mapping(address => Stake[]) private userStakes;
    uint256 public totalStakedTokens;
    uint256 public rewardDistributed;

    // ===================== TOKEN VESTING =====================
    address public treasuryWallet;
    uint256 public vestingStart;

    // ===================== EVENTS =====================
    // Emitted when a user stakes tokens
    event Staked(address indexed user, uint256 amount);

    // Emitted when a user unstakes tokens and possibly receives a penalty
    event Unstaked(address indexed user, uint256 amount, uint256 penaltyAmount);

    // Emitted when a user claims staking rewards
    event RewardClaimed(address indexed user, uint256 indexed stakeIndex, uint256 amount);

    // Emitted when the reward pool has no tokens left
    event RewardPoolDepleted();

    // Emitted when tokens are released from vesting
    event TokensReleased(uint256 year, uint256 amount);

    // Emitted when the treasury wallet is updated
    event TreasuryWalletUpdated(address indexed oldWallet, address indexed newWallet);

    // Emitted when the staking reward rate is updated
    event RewardRateUpdated(uint256 oldRate, uint256 newRate);

    // Emitted when a user performs an emergency withdrawal
    event EmergencyWithdrawal(address indexed user, uint256 amount);

    // Emitted when rewards are calculated and accumulated for a user
    event RewardAccumulated(address indexed user, uint256 stakeIndex, uint256 accumulated);

    // Emitted when a user attempts to stake beyond the allowed limit
    event StakeLimitReached(address indexed user, uint256 attemptedAmount);

    // Emitted for soft validation/logging purposes, e.g., when a treasury is a contract
    event Log(string message);

    // Emitted once to mark the start of the vesting schedule
    event VestingStarted(uint256 timestamp);

    // ===================== INITIALIZATION =====================
    function initialize(address _treasuryWallet) public initializer {
        require(_treasuryWallet != address(0), "ERR1"); // ERR1: Invalid treasury wallet

        __ERC20_init("FIV LATAM COIN", "FIV");
        __Ownable_init();
        __Pausable_init();
        __ReentrancyGuard_init();
        __ERC20Permit_init("FIV LATAM COIN");
        __UUPSUpgradeable_init();

        _mint(msg.sender, INITIAL_SUPPLY);
        rewardRate = 20;
        minReward = 1e15;
        treasuryWallet = _treasuryWallet;
        vestingStart = block.timestamp;
        emit VestingStarted(vestingStart);
        lastReleaseTime = block.timestamp;
        currentYear = 0;

        releaseDivisors = [
            10, 11, 12, 13, 15,
            16, 18, 19, 21, 24,
            26, 28, 31, 34, 38,
            42, 46, 51, 56, 62,
            69, 76, 84, 93, 102
        ];
    }

    function _authorizeUpgrade(address newImplementation) internal override onlyOwner {}

    function _calculateReward(Stake memory userStake) internal view returns (uint256) {
    if (userStake.amount == 0 || userStake.lastClaimed >= block.timestamp) {
        return 0;
    }

    uint256 durationInSeconds = block.timestamp - userStake.lastClaimed;
    uint256 yearlyRate = rewardRate; // Se toma directamente del estado del contrato
    uint256 annualReward = (userStake.amount * yearlyRate) / 100;

    uint256 reward = (annualReward * durationInSeconds) / 365 days;
    return reward;
}

    uint256 public constant TIMESTAMP_TOLERANCE = 60;

    function _hasReachedTimeWithTolerance(uint256 current, uint256 target) internal pure returns (bool) {
    return current + TIMESTAMP_TOLERANCE >= target;
}

    // ===================== STAKING =====================
    function stake(uint256 amount) external whenNotPaused nonReentrant {
    // Prevent staking zero tokens
    require(amount > 0, "ERR2"); // ERR2: Cannot stake zero tokens

    // Enforce minimum stake amount
    require(amount >= MIN_STAKE_AMOUNT, "ERR3"); // ERR3: Amount below minimum stake

    // Ensure the user has sufficient balance
    require(balanceOf(msg.sender) >= amount, "ERR4"); // ERR4: Insufficient balance to stake

    // Enforce maximum number of active stakes per user
    if (userStakes[msg.sender].length >= 10) {
        emit StakeLimitReached(msg.sender, amount);
        revert("ERR5"); // ERR5: Stake limit reached
    }

    // Transfer tokens from the user to the contract
    _transfer(msg.sender, address(this), amount);

    // Record the stake details
    userStakes[msg.sender].push(Stake({
        amount: amount,
        stakedAt: block.timestamp,
        lastClaimed: block.timestamp,
        pendingReward: 0
    }));

    // Update global staking total
    totalStakedTokens += amount;

    emit Staked(msg.sender, amount);
}

    function emergencyUnstake() external whenPaused nonReentrant {
        Stake[] storage stakesLocal = userStakes[msg.sender];

        // Ensure the user has active stakes
        require(stakesLocal.length > 0, "ERR6"); // ERR6: No staked tokens to withdraw

        uint256 totalAmount = 0;

        // Sum the total amount staked by the user
        for (uint256 i = 0; i < stakesLocal.length; i++) {
            totalAmount += stakesLocal[i].amount;
        }

        // Ensure there is something to withdraw
        require(totalAmount > 0, "ERR7"); // ERR7: No staked tokens to withdraw

        // Ensure the contract has enough balance to return the funds
        require(balanceOf(address(this)) >= totalAmount, "ERR8"); // ERR8: Insufficient contract balance

        // Effects: clear the user's stake data before making external calls
        delete userStakes[msg.sender];
        totalStakedTokens -= totalAmount;

        // Interactions: transfer the tokens back to the user
        _transfer(address(this), msg.sender, totalAmount);

        emit EmergencyWithdrawal(msg.sender, totalAmount);
}

    function unstake(uint256 stakeIndex, uint256 amount) external whenNotPaused nonReentrant {
        require(stakeIndex < userStakes[msg.sender].length, "ERR9"); // ERR9: Invalid stake index
        require(amount > 0, "ERR10"); // ERR10: Cannot unstake zero tokens

        Stake storage userStake = userStakes[msg.sender][stakeIndex];
        require(userStake.amount >= amount, "ERR11"); // ERR11: Not enough staked tokens in this stake

        uint256 originalAmount = amount;
        uint256 penaltyAmount = 0;

        // Mint reward before reducing the stake, if eligible
        uint256 reward = _calculateReward(userStake);
        if (
            reward >= minReward &&
            rewardDistributed + reward <= REWARD_SUPPLY_LIMIT &&
            totalSupply() + reward <= MAX_SUPPLY
        ) {
            rewardDistributed += reward;
            userStake.lastClaimed = block.timestamp;
            _mint(msg.sender, reward);
            emit RewardClaimed(msg.sender, stakeIndex, reward);
        }

        // Check minimum staking duration with tolerance
        if (!_hasReachedTimeWithTolerance(block.timestamp, userStake.stakedAt + MIN_STAKE_DURATION)) {
            revert("ERR12"); // ERR12: Staking must be held for at least 24 hours
        } 
        // Early unstake penalty applies
        else if (block.timestamp < userStake.stakedAt + EARLY_UNSTAKE_DURATION) {
            penaltyAmount = (amount * EARLY_UNSTAKE_PENALTY_RATE) / 100;
            amount -= penaltyAmount;
            _transfer(address(this), treasuryWallet, penaltyAmount);
        }

        userStake.amount -= originalAmount;
        totalStakedTokens -= originalAmount;

        if (userStake.amount == 0) {
            // Remove the stake by swapping with last and popping to avoid gaps
            uint256 lastIndex = userStakes[msg.sender].length - 1;
            if (stakeIndex != lastIndex) {
                userStakes[msg.sender][stakeIndex] = userStakes[msg.sender][lastIndex];
            }
            userStakes[msg.sender].pop();
        }

        _transfer(address(this), msg.sender, amount);
        emit Unstaked(msg.sender, originalAmount, penaltyAmount);
}

    function claimReward() external whenNotPaused nonReentrant {
        Stake[] storage stakesLocal = userStakes[msg.sender];
        uint256 stakeLen = stakesLocal.length;

        require(stakeLen > 0, "ERR16"); // ERR16: No tokens staked

        uint256 totalReward = 0;
        uint256 dailyRate = rewardRate * 1e18;
        uint256 currentTimestamp = block.timestamp;

        for (uint256 i = 0; i < stakeLen; i++) {
            Stake storage s = stakesLocal[i];
            if (s.amount == 0) continue;

            uint256 targetTimestamp = s.lastClaimed + CLAIM_INTERVAL;

            // Check if the minimum claim interval has passed (with tolerance)
            if (!_hasReachedTimeWithTolerance(currentTimestamp, targetTimestamp)) continue;

            // Limit accumulation to avoid indefinite reward growth
            uint256 timeElapsed = currentTimestamp - s.lastClaimed;
            if (timeElapsed > MAX_ACCUMULATION_DURATION) {
                timeElapsed = MAX_ACCUMULATION_DURATION;
            }

            // Calculate proportional reward based on time elapsed
            uint256 reward = (s.amount * dailyRate * timeElapsed) / (365 * 1e18 * 1 days);
            reward += s.pendingReward;

            // If reward is below minimum, accumulate it
            if (reward < minReward) {
                uint256 newPending = s.pendingReward + reward;
                s.pendingReward = newPending > MAX_ACCUMULABLE_REWARD
                    ? MAX_ACCUMULABLE_REWARD
                    : newPending;

                emit RewardAccumulated(msg.sender, i, reward);
                continue;
            }

            // If reward is valid, update state
            totalReward += reward;
            s.pendingReward = 0;
            s.lastClaimed = currentTimestamp;

            emit RewardClaimed(msg.sender, i, reward);
        }

        require(totalReward > 0, "ERR13"); // ERR13: No rewards to claim
        require(rewardDistributed + totalReward <= REWARD_SUPPLY_LIMIT, "ERR14"); // ERR14: Reward pool exhausted
        require(totalSupply() + totalReward <= MAX_SUPPLY, "ERR15"); // ERR15: Max supply exceeded

        rewardDistributed += totalReward;
        _mint(msg.sender, totalReward);
}

    // ===================== VIEW FUNCTIONS =====================
    function getRewardAmount(address userAddress) external view returns (uint256) {
        Stake[] storage stakesLocal = userStakes[userAddress];

        if (
            stakesLocal.length == 0 ||
            rewardRate > MAX_REWARD_RATE ||
            rewardDistributed >= REWARD_SUPPLY_LIMIT
        ) {
            return 0;
        }

        uint256 totalReward = 0;
        uint256 dailyRate = rewardRate * 1e18;
        uint256 currentTimestamp = block.timestamp;

        for (uint256 i = 0; i < stakesLocal.length; i++) {
            Stake storage s = stakesLocal[i];

            if (s.amount == 0) continue;

            if (!_hasReachedTimeWithTolerance(currentTimestamp, s.lastClaimed + CLAIM_INTERVAL)) {
                continue;
            }

            uint256 timeElapsed = currentTimestamp - s.lastClaimed;
            if (timeElapsed > MAX_ACCUMULATION_DURATION) {
                timeElapsed = MAX_ACCUMULATION_DURATION;
            }

            uint256 reward = (s.amount * dailyRate * timeElapsed) / (365 * 1e18 * 1 days);
            reward += s.pendingReward;

            if (reward < minReward) {
                continue;
            }

            totalReward += reward;
        }

        uint256 remainingReward = REWARD_SUPPLY_LIMIT - rewardDistributed;
        return totalReward > remainingReward ? remainingReward : totalReward;
}

    function getStakeAmount(address user) external view returns (uint256) {
        Stake[] storage stakesLocal = userStakes[user];
        uint256 totalAmount = 0;

        for (uint256 i = 0; i < stakesLocal.length; i++) {
            totalAmount += stakesLocal[i].amount;
        }

        return totalAmount;
}

    function getUserStakeDetails(address user) external view returns (
        uint256[] memory amounts,
        uint256[] memory stakedAts,
        uint256[] memory lastClaimeds,
        uint256[] memory pendingRewards
    ) {
        Stake[] storage stakesLocal = userStakes[user];
        uint256 len = stakesLocal.length;

        amounts = new uint256[](len);
        stakedAts = new uint256[](len);
        lastClaimeds = new uint256[](len);
        pendingRewards = new uint256[](len);

        for (uint256 i = 0; i < len; i++) {
            Stake storage s = stakesLocal[i];
            amounts[i] = s.amount;
            stakedAts[i] = s.stakedAt;
            lastClaimeds[i] = s.lastClaimed;
            pendingRewards[i] = s.pendingReward;
        }
}

    function getStakes(address user) external view returns (Stake[] memory) {
        return userStakes[user];
}

    function getRemainingRewardPool() public view virtual returns (uint256) {
        return REWARD_SUPPLY_LIMIT - rewardDistributed;
}

    // ===================== TOKEN RELEASE =====================
    function releaseScheduledTokens() external onlyOwner {
        require(_hasReachedTimeWithTolerance(block.timestamp, lastReleaseTime + RELEASE_INTERVAL), "ERR17"); // ERR17: Not time to release yet
        require(currentYear < 25, "ERR18"); // ERR18: Vesting completed
        require(currentYear < releaseDivisors.length, "ERR19"); // ERR19: Invalid release year
        uint256 amount = 100000000 * 10**18 / releaseDivisors[currentYear];
        require(amount > 0, "ERR20"); // ERR20: Amount must be greater than zero
        require(totalSupply() + amount <= MAX_SUPPLY, "ERR21"); // ERR21: Exceeds max supply
        _mint(treasuryWallet, amount);
        lastReleaseTime = block.timestamp;
        currentYear++;
        emit TokensReleased(currentYear, amount);
}

    function getNextScheduledReleaseInfo() external view returns (
        uint256 nextAmount,
        uint256 nextTimestamp,
        uint256 currentYearActual
    ) {
        currentYearActual = currentYear;
        if (currentYearActual >= releaseDivisors.length) {
            return (0, 0, currentYearActual);
        }
        nextAmount = 100000000 * 10**18 / releaseDivisors[currentYearActual];
        nextTimestamp = lastReleaseTime + RELEASE_INTERVAL;
}

    // ===================== OWNER FUNCTIONS =====================
    /// @notice Ownership renouncement is intentionally disabled.
    /// @dev This function exists only to satisfy the Ownable interface.
    ///      In this contract, ownership is expected to be managed externally (e.g., via Gnosis Safe).
    ///      Ownership should not be renounced to retain upgradeability and governance control.
    function renounceOwnership() public view override onlyOwner {
        revert("ERR22"); // ERR22: Renouncing ownership is disabled
}

    function setTreasuryWallet(address newWallet) external onlyOwner {
        require(newWallet != address(0), "ERR23"); // ERR23: Invalid address
        address oldWallet = treasuryWallet;
        treasuryWallet = newWallet;
        emit TreasuryWalletUpdated(oldWallet, newWallet);

        // Soft check: emits a log if the new treasury is a contract (likely a multisig wallet)
        if (AddressUpgradeable.isContract(newWallet)) {
            emit Log("New treasuryWallet is a contract, likely a multisig");
        }
}

    function setRewardRate(uint256 newRate) external onlyOwner {
        require(newRate > 0 && newRate <= MAX_REWARD_RATE, "ERR24"); // ERR24: Invalid reward rate
        uint256 oldRate = rewardRate;
        rewardRate = newRate;
        emit RewardRateUpdated(oldRate, newRate);
}

    function pause() external onlyOwner {
        _pause();
}

    function unpause() external onlyOwner {
        _unpause();
}

    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return super.supportsInterface(interfaceId);
}

    // ===================== UPGRADE GAP =====================
    // Reserve storage space to allow for layout changes in the future
        uint256[50] private __gap;
}
  

Un universo de oportunidades. A tu alcance.

X