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
Ownable
contract 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
tokenizeProperty
function 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
setForSale
function allows the owner to mark a property as available for sale and set its valuation.The
buyProperty
function 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
CoOwner
struct allows multiple addresses to own a share of the property. TheaddCoOwner
function assigns shares to different co-owners.The contract manages rental income distribution through
distributeRentalIncome
and allows co-owners to withdraw their share of the rental income usingwithdrawDividend
.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
placeBid
andacceptBid
.Events like
DividendWithdrawn
,BidPlaced
, andBidAccepted
provide 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