Introduction
Welcome to this tutorial. In this tutorial, we’re going to dive into a fascinating topic - the management of intellectual property (IP) using blockchain technology. The Celo network provides a rich infrastructure for creating and managing unique, non-fungible tokens (NFTs) that can represent a wide variety of real-world assets. One innovative application of this technology is in the world of IP rights, where each unique token can represent a unique piece of intellectual property. This tutorial will guide you through the inner workings of such a smart contract, explaining each function in detail.
You can find the repository for this tutorial Here
Prerequisites
To follow this tutorial, you will need the following:
- Basic understanding of Solidity and smart contracts.
- A Development Environment Like Remix.
- The Celo Extension Wallet.
Contract Developement
The complete code contract look like this
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract IPManagement is ERC721URIStorage, Ownable {
uint private counter;
enum Category { Patent, Copyright, Trademark }
struct IP {
string ipDescription;
uint256 price;
Category category;
address payable owner;
bool purchased;
}
mapping (uint => IP) public ips;
event IPRegistered (
uint tokenId,
string ipDescription,
uint256 price,
address payable owner,
Category category
);
event IPPurchased (
uint tokenId,
string ipDescription,
uint256 price,
address payable owner,
Category category
);
constructor() ERC721("IPManagement", "IPM") {}
function registerIP(string memory _description, uint256 _price, Category _category, string memory tokenURI) public onlyOwner {
counter++;
uint tokenId = counter;
_mint(msg.sender, tokenId);
_setTokenURI(tokenId, tokenURI);
ips[tokenId] = IP(_description, _price, _category, payable(msg.sender), false);
emit IPRegistered(tokenId, _description, _price, payable(msg.sender), _category);
}
function purchaseIP(uint tokenId) public payable {
IP memory _ip = ips[tokenId];
require(!_ip.purchased, "IP already purchased");
require(msg.value == _ip.price, "Incorrect value sent");
_ip.owner.transfer(msg.value);
_ip.purchased = true;
ips[tokenId] = _ip;
_transfer(_ip.owner, msg.sender, tokenId);
emit IPPurchased(tokenId, _ip.ipDescription, msg.value, _ip.owner, _ip.category);
}
function updateIP(uint tokenId, string memory _description, uint256 _price, Category _category) public {
IP memory _ip = ips[tokenId];
require(_ip.owner == msg.sender, "Only owner can update the IP");
_ip.ipDescription = _description;
_ip.price = _price;
_ip.category = _category;
ips[tokenId] = _ip;
}
function deleteIP(uint tokenId) public {
IP memory _ip = ips[tokenId];
require(_ip.owner == msg.sender, "Only owner can delete the IP");
delete ips[tokenId];
_burn(tokenId);
}
}
Code Breakdown
Let’s break it down piece by piece:
Imports and Inheritance
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract IPManagement is ERC721URIStorage, Ownable {
This contract imports two contracts from OpenZeppelin: ERC721URIStorage
and Ownable
.
ERC721URIStorage
is an extension of the ERC721 standard, which provides basic functionality to create unique, non-fungible tokens. It also includes a URI for metadata that might be used to identify the token externally.Ownable
provides basic authorization control functions, simplifying the implementation of user permissions. It sets the originalowner
of the contract to the sender(account deploying the contract) and restricts certain functions to be accessible only by theowner
.
Contract Variables and Types
uint private counter;
enum Category { Patent, Copyright, Trademark }
struct IP {
string ipDescription;
uint256 price;
Category category;
address payable owner;
bool purchased;
}
mapping (uint => IP) public ips;
counter
is a private variable that keeps track of the number of registered IPs.
Category
is an enumerated type (enum
) that specifies three types of intellectual property: Patent, Copyright, and Trademark.
IP
is a struct, a custom defined type that groups together related data of varying types. This struct represents an intellectual property (IP), and includes a description, a price, a category (from the Category
enum), an owner’s address, and a flag indicating whether the IP has been purchased.
ips
is a public mapping that associates each tokenId
(an unsigned integer) with an IP
struct.
Events
event IPRegistered (
uint tokenId,
string ipDescription,
uint256 price,
address payable owner,
Category category
);
event IPPurchased (
uint tokenId,
string ipDescription,
uint256 price,
address payable owner,
Category category
);
Events are a way for smart contracts to emit information that external consumers (like web3 applications) can listen for and react to.
IPRegistered
event is emitted when a new IP is registered.IPPurchased
event is emitted when an IP is purchased.
Constructor
constructor() ERC721("IPManagement", "IPM") {}
The constructor handles the name and symbol of our IP Management ERC721
token.
RegisterIP Function
function registerIP(string memory _description, uint256 _price, Category _category, string memory tokenURI) public onlyOwner {
counter++;
uint tokenId = counter;
_mint(msg.sender, tokenId);
_setTokenURI(tokenId, tokenURI);
ips[tokenId] = IP(_description, _price, _category, payable(msg.sender), false);
emit IPRegistered(tokenId, _description, _price, payable(msg.sender), _category);
}
The registerIP()
function allows the contract owner to register a new IP. It increments the counter
, mints a new ERC721 token, and sets the token’s URI. It also creates a new IP
struct and stores it in the ips
mapping, then emits an IPRegistered
event.
PurchaseIP Function
function purchaseIP(uint tokenId) public payable {
IP memory _ip = ips[tokenId];
require(!_ip.purchased, "IP already purchased");
require(msg.value == _ip.price, "Incorrect value sent");
_ip.owner.transfer(msg.value);
_ip.purchased = true;
ips[tokenId] = _ip;
_transfer(_ip.owner, msg.sender, tokenId);
emit IPPurchased(tokenId, _ip.ipDescription, msg.value, _ip.owner, _ip.category);
}
The purchaseIP()
function allows a user to purchase an IP, provided it hasn’t been purchased already and the correct amount of Celo is sent. The purchase amount is transferred to the previous owner, and ownership of the token (and thus, the IP) is transferred to the buyer. Then it updates the purchased flag in the ips mapping and emits an IPPurchased event.
Update IP Function
function updateIP(uint tokenId, string memory _description, uint256 _price, Category _category) public {
IP memory _ip = ips[tokenId];
require(_ip.owner == msg.sender, "Only owner can update the IP");
_ip.ipDescription = _description;
_ip.price = _price;
_ip.category = _category;
ips[tokenId] = _ip;
}
The updateIP()
function allows the owner of an IP to update its details, such as the description, price, and category. Before making these changes, it checks that the sender is the current owner of the IP.
Delete IP Function
function deleteIP(uint tokenId) public {
IP memory _ip = ips[tokenId];
require(_ip.owner == msg.sender, "Only owner can delete the IP");
delete ips[tokenId];
_burn(tokenId);
}
The deleteIP()
function allows the owner of an IP to delete it. Before deletion, it checks that the sender is the current owner of the IP. Then, it removes the IP
struct from the ips
mapping and burns the associated token, effectively removing it from circulation.
Deployment
To deploy our smart contract successfully, we need the celo extention wallet which can be downloaded from here
Next, we need to fund our newly created wallet which can done using the celo alfojares faucet Here
Now, click on the plugin logo at the bottom left corner and search for celo plugin.
Install the plugin and click on the celo logo which will show in the side tab after the plugin is installed.
Next connect your Celo wallet, select the contract you want to deploy and finally click on deploy to deploy your contract.
Conclusion
Congratulations! You’ve reached the end of this tutorial. I hope this detailed walkthrough of the IPManagement
smart contract gave you a clear understanding of how blockchain technology can be used to manage intellectual property rights. By now, you should have a solid grasp on how the contract functions, how ERC721 tokens are used to represent unique intellectual properties, and how ownership and transfers of these tokens map to the ownership and sales of IPs.
Next Steps
I hope you learned a lot from this tutorial. Here are some relevant links that would aid your learning further.
About the author
I’m Jonathan Iheme, A full stack block-chain Developer from Nigeria.