Download
Download
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol";
import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
interface IPancakeRouter01 {
function factory() external pure returns (address);
function WETH() external pure returns (address);
function addLiquidity(
address tokenA,
address tokenB,
uint amountADesired,
uint amountBDesired,
uint amountAMin,
uint amountBMin,
address to,
uint deadline
) external returns (uint amountA, uint amountB, uint liquidity);
function addLiquidityETH(
address token,
uint amountTokenDesired,
uint amountTokenMin,
uint amountETHMin,
address to,
uint deadline
) external payable returns (uint amountToken, uint amountETH, uint liquidity);
function swapExactTokensForTokens(
uint amountIn,
uint amountOutMin,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapTokensForExactTokens(
uint amountOut,
uint amountInMax,
address[] calldata path,
address to,
uint deadline
) external returns (uint[] memory amounts);
function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to,
uint deadline)
external
payable
returns (uint[] memory amounts);
function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata
path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata
path, address to, uint deadline)
external
returns (uint[] memory amounts);
function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint
deadline)
external
payable
returns (uint[] memory amounts);
}
/**
* @title VexoraProperties
* @dev ERC20 token with features specialized for tokenized real estate investments:
* - Fractional ownership of properties
* - Property registry
* - Distribution of rental income
* - Property governance
* - Integration with BSC/PancakeSwap
*/
contract VexoraProperties is ERC20Burnable, ERC20Pausable, AccessControl,
ReentrancyGuard {
using SafeMath for uint256;
// Roles
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant PROPERTY_MANAGER_ROLE =
keccak256("PROPERTY_MANAGER_ROLE");
bytes32 public constant COMPLIANCE_ROLE = keccak256("COMPLIANCE_ROLE");
// PancakeSwap
IPancakeRouter02 public pancakeRouter;
address public pancakeswapPair;
// Token Configuration
uint256 public constant MAX_SUPPLY = 1_000_000_000 * 1e18; // 1 billion tokens
// Token Distribution
uint256 public constant PROPERTY_ACQUISITION_ALLOCATION = 500_000_000 * 1e18; //
50%
uint256 public constant LIQUIDITY_ALLOCATION = 200_000_000 * 1e18; // 20%
uint256 public constant TEAM_ALLOCATION = 100_000_000 * 1e18; // 10%
uint256 public constant MARKETING_ALLOCATION = 100_000_000 * 1e18; // 10%
uint256 public constant RESERVE_FUND_ALLOCATION = 100_000_000 * 1e18; // 10%
// Addresses
address public treasuryWallet;
address public marketingWallet;
address public teamWallet;
address public reserveFundWallet;
// Property Management
struct Property {
uint256 id;
string name;
string location;
uint256 value; // Property value in USD cents
uint256 tokenAllocation; // Amount of tokens representing this property
bool isActive;
uint256 rentalIncome; // Accumulated rental income in stable coins (e.g., BUSD, USDT)
address paymentToken; // Token used for rental payments (e.g., BUSD)
uint256 lastDistributionTime;
}
struct PropertyInvestor {
uint256 propertyId;
address investor;
uint256 tokenAmount;
}
// Compliance
mapping(address => bool) public isWhitelisted;
bool public whitelistEnabled = true;
uint256 public maxTransactionAmount;
// Treasury
uint256 public treasuryBalance;
// Events
event PropertyAdded(uint256 indexed propertyId, string name, string location, uint256 value,
uint256 tokenAllocation);
event PropertyInvestmentMade(uint256 indexed propertyId, address indexed investor,
uint256 tokenAmount);
event PropertyInvestmentWithdrawn(uint256 indexed propertyId, address indexed investor,
uint256 tokenAmount);
event RentalIncomeReceived(uint256 indexed propertyId, uint256 amount);
event DividendDistributed(uint256 indexed propertyId, uint256 totalAmount);
event DividendClaimed(address indexed investor, uint256 amount);
event WhitelistStatusChanged(address indexed account, bool status);
event WhitelistEnabledChanged(bool enabled);
event PropertyStatusChanged(uint256 indexed propertyId, bool isActive);
// Modifiers
modifier onlyPropertyManager() {
require(hasRole(PROPERTY_MANAGER_ROLE, msg.sender), "Caller is not a property
manager");
_;
}
modifier onlyAdmin() {
require(hasRole(ADMIN_ROLE, msg.sender), "Caller is not an admin");
_;
}
modifier onlyCompliance() {
require(hasRole(COMPLIANCE_ROLE, msg.sender), "Caller is not compliance");
_;
}
/**
* @dev Constructor sets up roles and initializes the token
* @param _treasuryWallet Address of the treasury wallet
* @param _marketingWallet Address of the marketing wallet
* @param _teamWallet Address of the team wallet
* @param _reserveFundWallet Address of the reserve fund wallet
* @param _pancakeRouterAddress Address of the PancakeSwap router
*/
constructor(
address _treasuryWallet,
address _marketingWallet,
address _teamWallet,
address _reserveFundWallet,
address _pancakeRouterAddress
) ERC20("Vexora Properties Token", "VXP") {
require(_treasuryWallet != address(0), "Treasury wallet cannot be the zero address");
require(_marketingWallet != address(0), "Marketing wallet cannot be the zero address");
require(_teamWallet != address(0), "Team wallet cannot be the zero address");
require(_reserveFundWallet != address(0), "Reserve fund wallet cannot be the zero
address");
require(_pancakeRouterAddress != address(0), "PancakeSwap router cannot be the zero
address");
// Set wallets
treasuryWallet = _treasuryWallet;
marketingWallet = _marketingWallet;
teamWallet = _teamWallet;
reserveFundWallet = _reserveFundWallet;
// Set PancakeSwap
pancakeRouter = IPancakeRouter02(_pancakeRouterAddress);
/**
* @dev Hook that is called before any transfer of tokens
* Implements compliance checks and transaction limits
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal override(ERC20, ERC20Pausable) {
super._beforeTokenTransfer(from, to, amount);
/**
* @dev Add a new property to the registry
* @param _name Name of the property
* @param _location Location of the property
* @param _value Value of the property in USD cents
* @param _tokenAllocation Amount of tokens allocated to this property
* @param _paymentToken Token used for rental payments
*/
function addProperty(
string memory _name,
string memory _location,
uint256 _value,
uint256 _tokenAllocation,
address _paymentToken
) external onlyPropertyManager {
require(_tokenAllocation <= PROPERTY_ACQUISITION_ALLOCATION, "Token allocation
exceeds available tokens");
require(bytes(_name).length > 0, "Name cannot be empty");
require(bytes(_location).length > 0, "Location cannot be empty");
require(_value > 0, "Value must be greater than 0");
require(_paymentToken != address(0), "Payment token cannot be zero address");
propertyCount++;
uint256 propertyId = propertyCount;
properties[propertyId] = Property({
id: propertyId,
name: _name,
location: _location,
value: _value,
tokenAllocation: _tokenAllocation,
isActive: true,
rentalIncome: 0,
paymentToken: _paymentToken,
lastDistributionTime: block.timestamp
});
/**
* @dev Invest tokens in a specific property
* @param _propertyId ID of the property
* @param _amount Amount of tokens to invest
*/
function investInProperty(uint256 _propertyId, uint256 _amount) external nonReentrant {
require(_propertyId <= propertyCount && _propertyId > 0, "Invalid property ID");
require(properties[_propertyId].isActive, "Property is not active");
require(_amount > 0, "Amount must be greater than 0");
require(balanceOf(msg.sender) >= _amount, "Insufficient balance");
// Ensure the total investment doesn't exceed the property's token allocation
uint256 newTotalInvestment = propertyTotalInvestment[_propertyId].add(_amount);
require(newTotalInvestment <= property.tokenAllocation, "Investment would exceed
property allocation");
if (!propertyFound) {
investorProperties[msg.sender].push(_propertyId);
}
/**
* @dev Withdraw investment from a property
* @param _propertyId ID of the property
* @param _amount Amount of tokens to withdraw
*/
function withdrawInvestment(uint256 _propertyId, uint256 _amount) external nonReentrant {
require(_propertyId <= propertyCount && _propertyId > 0, "Invalid property ID");
require(_amount > 0, "Amount must be greater than 0");
require(propertyInvestments[_propertyId][msg.sender] >= _amount, "Insufficient
investment");
/**
* @dev Set the active status of a property
* @param _propertyId ID of the property
* @param _isActive Whether the property is active
*/
function setPropertyStatus(uint256 _propertyId, bool _isActive) external onlyPropertyManager
{
require(_propertyId <= propertyCount && _propertyId > 0, "Invalid property ID");
properties[_propertyId].isActive = _isActive;
/**
* @dev Get all properties invested in by an address
* @param _investor Address of the investor
* @return Array of property IDs
*/
function getInvestorProperties(address _investor) external view returns (uint256[] memory) {
return investorProperties[_investor];
}
/**
* @dev Get investment amount in a specific property
* @param _propertyId ID of the property
* @param _investor Address of the investor
* @return Amount of tokens invested
*/
function getPropertyInvestment(uint256 _propertyId, address _investor) external view returns
(uint256) {
return propertyInvestments[_propertyId][_investor];
}
/**
* @dev Record rental income for a property (paid in stable tokens)
* @param _propertyId ID of the property
* @param _amount Amount of stable tokens received as rent
*/
function recordRentalIncome(uint256 _propertyId, uint256 _amount) external
onlyPropertyManager {
require(_propertyId <= propertyCount && _propertyId > 0, "Invalid property ID");
require(_amount > 0, "Amount must be greater than 0");
require(properties[_propertyId].isActive, "Property is not active");
properties[_propertyId].rentalIncome = properties[_propertyId].rentalIncome.add(_amount);
/**
* @dev Distribute rental income among property investors
* @param _propertyId ID of the property
*/
function distributeRentalIncome(uint256 _propertyId) external onlyPropertyManager
nonReentrant {
require(_propertyId <= propertyCount && _propertyId > 0, "Invalid property ID");
// Transfer income from manager to contract (assumes manager received the rental income
as stable tokens)
// Implementation depends on the stable token used for payments
/**
* @dev Claim accumulated dividends for an investor
*/
function claimDividends() external nonReentrant {
uint256 amount = unclaimedDividends[msg.sender];
require(amount > 0, "No dividends to claim");
unclaimedDividends[msg.sender] = 0;
/**
* @dev Get unclaimed dividends for an investor
* @param _investor Address of the investor
* @return Amount of unclaimed dividends
*/
function getUnclaimedDividends(address _investor) external view returns (uint256) {
return unclaimedDividends[_investor];
}
/**
* @dev Add or remove an address from the whitelist
* @param _account Address to update
* @param _status Whitelist status
*/
function updateWhitelist(address _account, bool _status) external onlyCompliance {
isWhitelisted[_account] = _status;
emit WhitelistStatusChanged(_account, _status);
}
/**
* @dev Batch update whitelist for multiple addresses
* @param _accounts Array of addresses to update
* @param _status Whitelist status for all addresses
*/
function batchUpdateWhitelist(address[] calldata _accounts, bool _status) external
onlyCompliance {
for (uint256 i = 0; i < _accounts.length; i++) {
isWhitelisted[_accounts[i]] = _status;
emit WhitelistStatusChanged(_accounts[i], _status);
}
}
/**
* @dev Enable or disable the whitelist requirement
* @param _enabled Whether the whitelist is enabled
*/
function setWhitelistEnabled(bool _enabled) external onlyCompliance {
whitelistEnabled = _enabled;
emit WhitelistEnabledChanged(_enabled);
}
/**
* @dev Set the maximum transaction amount
* @param _maxAmount New maximum transaction amount
*/
function setMaxTransactionAmount(uint256 _maxAmount) external onlyAdmin {
require(_maxAmount > 0, "Max transaction amount must be greater than 0");
maxTransactionAmount = _maxAmount;
}
/**
* @dev Pause all token transfers
*/
function pause() external onlyAdmin {
_pause();
}
/**
* @dev Unpause all token transfers
*/
function unpause() external onlyAdmin {
_unpause();
}
/**
* @dev Add liquidity to PancakeSwap
* @param _tokenAmount Amount of VXP tokens to add to liquidity
* @param _router PancakeSwap router address
*/
function addLiquidityETH(uint256 _tokenAmount, address _router) external payable
onlyAdmin {
require(_tokenAmount > 0, "Token amount must be greater than 0");
require(msg.value > 0, "BNB amount must be greater than 0");
// Add liquidity
IPancakeRouter02(_router).addLiquidityETH{value: msg.value}(
address(this),
_tokenAmount,
0, // slippage is unavoidable
0, // slippage is unavoidable
treasuryWallet, // LP tokens sent to treasury
block.timestamp + 300 // deadline: 5 minutes
);
}
/**
* @dev Update treasury wallet address
* @param _newWallet New treasury wallet address
*/
function updateTreasuryWallet(address _newWallet) external onlyAdmin {
require(_newWallet != address(0), "New wallet cannot be the zero address");
treasuryWallet = _newWallet;
isWhitelisted[_newWallet] = true;
}
/**
* @dev Update marketing wallet address
* @param _newWallet New marketing wallet address
*/
function updateMarketingWallet(address _newWallet) external onlyAdmin {
require(_newWallet != address(0), "New wallet cannot be the zero address");
marketingWallet = _newWallet;
isWhitelisted[_newWallet] = true;
}
/**
* @dev Update team wallet address
* @param _newWallet New team wallet address
*/
function updateTeamWallet(address _newWallet) external onlyAdmin {
require(_newWallet != address(0), "New wallet cannot be the zero address");
teamWallet = _newWallet;
isWhitelisted[_newWallet] = true;
}
/**
* @dev Update reserve fund wallet address
* @param _newWallet New reserve fund wallet address
*/
function updateReserveFundWallet(address _newWallet) external onlyAdmin {
require(_newWallet != address(0), "New wallet cannot be the zero address");
reserveFundWallet = _newWallet;
isWhitelisted[_newWallet] = true;
}
/**
* @dev Get property details
* @param _propertyId ID of the property
* @return Property details
*/
function getPropertyDetails(uint256 _propertyId) external view returns (
string memory name,
string memory location,
uint256 value,
uint256 tokenAllocation,
bool isActive,
uint256 rentalIncome,
address paymentToken,
uint256 lastDistributionTime,
uint256 totalInvestment
){
require(_propertyId <= propertyCount && _propertyId > 0, "Invalid property ID");
return (
property.name,
property.location,
property.value,
property.tokenAllocation,
property.isActive,
property.rentalIncome,
property.paymentToken,
property.lastDistributionTime,
propertyTotalInvestment[_propertyId]
);
}
/**
* @dev Get total number of properties
* @return Number of properties
*/
function getPropertyCount() external view returns (uint256) {
return propertyCount;
}
/**
* @dev Get total number of properties an investor has invested in
* @param _investor Address of the investor
* @return Number of properties
*/
function getInvestorPropertyCount(address _investor) external view returns (uint256) {
return investorProperties[_investor].length;
}
/**
* @dev Function to receive ETH that will be used for adding liquidity
*/
receive() external payable {}
}