Example: Real Estate Tokenization Library
To illustrate how Evire's Asset Tokenization libraries can be utilized in a smart contract, we'll provide an example library that facilitates the tokenization of real-world assets. This example will focus on a basic implementation for tokenizing real estate assets.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
contract RealEstateToken is ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;
    struct Property {
        uint256 id;
        string location;
        uint256 valuation;
        bool forSale;
    }
    mapping(uint256 => Property) public properties;
    event PropertyTokenized(uint256 tokenId, string location, uint256 valuation, address owner);
    event PropertyForSale(uint256 tokenId, uint256 valuation);
    event PropertySold(uint256 tokenId, address newOwner, uint256 salePrice);
    constructor() ERC721("RealEstateToken", "RET") {}
    function tokenizeProperty(string memory _location, uint256 _valuation) public onlyOwner returns (uint256) {
        _tokenIds.increment();
        uint256 newPropertyId = _tokenIds.current();
        _mint(msg.sender, newPropertyId);
        _setTokenURI(newPropertyId, _location);
        properties[newPropertyId] = Property({
            id: newPropertyId,
            location: _location,
            valuation: _valuation,
            forSale: false
        });
        emit PropertyTokenized(newPropertyId, _location, _valuation, msg.sender);
        return newPropertyId;
    }
    function setForSale(uint256 _tokenId, uint256 _valuation) public onlyOwnerOf(_tokenId) {
        Property storage property = properties[_tokenId];
        property.forSale = true;
        property.valuation = _valuation;
        emit PropertyForSale(_tokenId, _valuation);
    }
    function buyProperty(uint256 _tokenId) public payable {
        Property storage property = properties[_tokenId];
        require(property.forSale, "Property not for sale");
        require(msg.value >= property.valuation, "Insufficient funds");
        address previousOwner = ownerOf(_tokenId);
        _transfer(previousOwner, msg.sender, _tokenId);
        property.forSale = false;
        payable(previousOwner).transfer(msg.value);
        emit PropertySold(_tokenId, msg.sender, msg.value);
    }
    modifier onlyOwnerOf(uint256 _tokenId) {
        require(ownerOf(_tokenId) == msg.sender, "Not the owner");
        _;
    }
}Explanation
- We use OpenZeppelin's ERC721 standard to represent the real estate as non-fungible tokens (NFTs). The - Ownablecontract ensures that only the contract owner can perform certain actions.
- This struct holds details about each property, including its unique ID, location, valuation, and sale status. 
- The - tokenizePropertyfunction allows the owner to create a new property token. This function mints a new ERC721 token, sets its metadata (location), and stores the property details.
- The - setForSalefunction allows the owner to mark a property as available for sale and set its valuation.
- The - buyPropertyfunction allows someone to purchase a property that is for sale by sending the appropriate amount of Ether. The ownership of the token is transferred, and the previous owner is paid.
Example of Usage:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./RWAFW/RealEstateToken.sol";
contract ExtendedRealEstateToken is RealEstateToken {
    struct CoOwner {
        address owner;
        uint256 share; // Percentage of ownership
    }
    mapping(uint256 => CoOwner[]) public coOwners;
    mapping(uint256 => uint256) public rentalIncome;
    mapping(uint256 => mapping(address => uint256)) public withdrawnDividends;
    event DividendWithdrawn(uint256 tokenId, address coOwner, uint256 amount);
    event BidPlaced(uint256 tokenId, address bidder, uint256 bidAmount);
    event BidAccepted(uint256 tokenId, address previousOwner, address newOwner, uint256 bidAmount);
    function addCoOwner(uint256 _tokenId, address _coOwner, uint256 _share) public onlyOwnerOf(_tokenId) {
        coOwners[_tokenId].push(CoOwner({
            owner: _coOwner,
            share: _share
        }));
    }
    function distributeRentalIncome(uint256 _tokenId, uint256 _amount) public onlyOwnerOf(_tokenId) {
        rentalIncome[_tokenId] += _amount;
    }
    function withdrawDividend(uint256 _tokenId) public {
        uint256 totalIncome = rentalIncome[_tokenId];
        require(totalIncome > 0, "No income to withdraw");
        uint256 ownerShare;
        for (uint256 i = 0; i < coOwners[_tokenId].length; i++) {
            if (coOwners[_tokenId][i].owner == msg.sender) {
                ownerShare = coOwners[_tokenId][i].share;
                break;
            }
        }
        uint256 withdrawableAmount = (totalIncome * ownerShare / 100) - withdrawnDividends[_tokenId][msg.sender];
        require(withdrawableAmount > 0, "No dividends to withdraw");
        withdrawnDividends[_tokenId][msg.sender] += withdrawableAmount;
        payable(msg.sender).transfer(withdrawableAmount);
        emit DividendWithdrawn(_tokenId, msg.sender, withdrawableAmount);
    }
    function placeBid(uint256 _tokenId) public payable {
        require(properties[_tokenId].forSale, "Property not for sale");
        require(msg.value > properties[_tokenId].valuation, "Bid is too low");
        emit BidPlaced(_tokenId, msg.sender, msg.value);
    }
    function acceptBid(uint256 _tokenId, address _bidder, uint256 _bidAmount) public onlyOwnerOf(_tokenId) {
        require(properties[_tokenId].forSale, "Property not for sale");
        require(_bidAmount > properties[_tokenId].valuation, "Bid amount is too low");
        address previousOwner = ownerOf(_tokenId);
        _transfer(previousOwner, _bidder, _tokenId);
        properties[_tokenId].forSale = false;
        payable(previousOwner).transfer(_bidAmount);
        emit BidAccepted(_tokenId, previousOwner, _bidder, _bidAmount);
    }
    function getCoOwners(uint256 _tokenId) public view returns (CoOwner[] memory) {
        return coOwners[_tokenId];
    }
}Explanation
- The - CoOwnerstruct allows multiple addresses to own a share of the property. The- addCoOwnerfunction assigns shares to different co-owners.
- The contract manages rental income distribution through - distributeRentalIncomeand allows co-owners to withdraw their share of the rental income using- withdrawDividend.
- The contract supports a bidding mechanism where potential buyers can place bids on properties that are for sale, and owners can accept these bids through - placeBidand- acceptBid.
- Events like - DividendWithdrawn,- BidPlaced, and- BidAcceptedprovide a way to track important actions within the contract.
This example demonstrates how to build a real estate management system using Solidity and the RealEstateToken.sol library. It covers tokenization, co-ownership, rental income distribution and a bidding system, showcasing the versatility and power of Solidity for creating advanced blockchain applications.
Last updated
