Using NFTs to build a CryptoGift market

CryptoGift is an ERC721 NFT Token and Marketplace to buy and give away an Ethereum based Collectible Gift.

Vittorio Minacori
7 min readNov 25, 2018

Visit CryptoGift.

Have you never heard about Crypto Collectibles, Non-Fungible Tokens (NFTs) or, technically, ERC721 Ethereum Tokens?

Basically they are unique and distinguishable tokens which have individual traits and identities.

As per official definition “NFTs can represent ownership over digital or physical assets” like:

  • Physical property (houses, unique artwork)
  • Virtual collectables (unique pictures of kittens, collectable cards)
  • “Negative value” assets (loans, burdens and other responsibilities)

How ERC721 works?

As defined in the official ERC721 proposal, they have a set of functions to handle transfers and token ownership.

Below the standard token and receiver interface. A wallet/broker/auction application MUST implement the aboveERC721TokenReceiver interface if it will accept safe transfers.

ERC721 application examples:

CryptoKitties is a digital collectible game based around cats that are breedable and tradeable.
Cryptokitties demonstrated the immediate utility of ERC721 tokens with tracking digital assets. Each crypto kitty represents an ERC721 token and has properties distinguishing it graphically and mathematically from every other crypto kitty. Just as Cryptokitties can breed and produce other unique Cryptokitties, ERC721 tokens can be combined to produce unique ERC721 token offspring. The methodology and ultimate result of combining ERC721 tokens depend on how developers write their smart contracts, but the capability is built into the ERC721 protocol.
Now imagine that instead of graphical avatars in the Cryptokitties world, we decide to track real assets including documents like wills or real estate deeds with verified signatures. Virtual products and real-world products could become ERC721 trackable and unique with embedded chips or imprints.
Art pieces could apply steganography to embed ERC721 token information that clarifies and verifies the origin and authenticity of the art piece. Every valuable asset could be tracked using ERC721 tokens because each token represents a one-off version.
ERC 721 tokens are also a natural fit for identifying populations of any sort including human beings in trust networks or online. The reason that the ERC721 tokens apply so well for online or trust networks when individuals may not know each other directly, is that each token’s uniqueness works without revealing the owner of a token. This allows for enforcing reputations without having to break privacy.

What is a CryptoGift?

A CryptoGift is an ERC721 Collectible or NFT Token to store a message into the Ethereum Blockchain.
Send Ethereum to a friend for birthday or Christmas, or send a love message. Encrypt and make it eternal.

Choose your and your receiver name, write an awesome message, decide a date and how many ETH do you want to Gift (also zero) and sign your CryptoGift using MetaMask.
Copy and share CryptoGift link and Encryption Key with your receiver. Only who hold the key can decrypt your message.

Make your Gift unique on the Blockchain!

A CryptoGift has:

  • sender: who is sending the gift
  • receiver: who is receiving the gift
  • message: a message for the receiver
  • amount: the value of ETH sent to the receiver
  • style: a number representing the message style
  • date: the date after which the gift is visible

You can create yours by visiting this page: https://vittominacori.github.io/cryptogift/send.html

After filling the form you can Send your CryptoGift or view how it will appear by clicking the Preview button.

You will need MetaMask and to Connect your account to start using it on CryptoGift. And then you can confirm your transaction.

NOTE: do not refresh the page during transaction.
IMPORTANT: do not lose your Encryption Key. It cannot be recovered if you lose it. It allows receiver to decrypt your message and it cannot be decrypted without it.

At this point you have a link and a QR Code that can be shared with your receiver. You can also print and send it by email.
Receiver has already received your ETH Gift but he can read your message only after the date you have inserted before and only by using the Encryption Key you provide.

View it using thebros as Encryption Key.

The Code

Smart Contracts

CryptoGiftToken has been built using OpenZeppelin, an open source library for secure smart contract development. It provides implementations of standards like ERC20 and ERC721 which you can deploy as-is or extend to suit your needs, as well as Solidity components to build custom contracts and more complex decentralized systems.

Smart Contracts have been compiled with solc v0.4.24+commit.e67f0147, using Truffle, a development environment, testing framework and asset pipeline for Ethereum, aiming to make life as an Ethereum developer easier.

You can view build here and test coverage here.
Code is verified on Etherscan: view Token and Marketplace.

CryptoGiftToken.sol is the Token Smart Contract and it is extended from ERC721Full with MinterRole.

It has a struct defining gift details and a mapping from token ID to the structure.

struct GiftStructure {    
uint256 amount;
address purchaser;
string content; // this is encoded with your encryption key
uint256 date;
uint256 style;
}
// Mapping from token ID to the structures
mapping(uint256 => GiftStructure) private _structureIndex;

Token has a function that allows to create new Gift and it can be called only by users with MINTER role (it will be the Market Smart Contract as described below).

function newGift(
uint256 amount,
address purchaser,
address beneficiary,
string content,
uint256 date,
uint256 style
)
external
canGenerate
onlyMinter
returns (uint256)
{
require(date > 0);
require(style <= _styles);
uint256 tokenId = _progressiveId.add(1);
_mint(beneficiary, tokenId);
_structureIndex[tokenId] = GiftStructure(
amount,
purchaser,
content,
date,
style
);
_progressiveId = tokenId;
return tokenId;
}

As described above, Gift will be visible only after the provided date. To check visibility, Token has a function that will return a boolean indicating if it is visible or not and the date too. If date is zero it means that Token does not exist.

function isVisible (
uint256 tokenId
)
external
view
returns (bool visible, uint256 date)
{
if (_exists(tokenId)) {
GiftStructure storage gift = _structureIndex[tokenId];
visible = block.timestamp >= gift.date;
date = gift.date;
} else {
visible = false;
date = 0;
}
}

It also has a function that will return a struct of values describing the token.

function getGift (uint256 tokenId)
external
view
returns (
uint256 amount,
address purchaser,
address beneficiary,
string content,
uint256 date,
uint256 style
)
{
require(_exists(tokenId));
GiftStructure storage gift = _structureIndex[tokenId]; require(block.timestamp >= gift.date); amount = gift.amount;
purchaser = gift.purchaser;
beneficiary = ownerOf(tokenId);
content = gift.content;
date = gift.date;
style = gift.style;
}

CryptoGiftMarketplace.sol is a Crowdsale Smart Contract (like an ICO Smart Contract) that has Minter role on Token contract. So it will be able to mint new tokens after a payment received.

To do so it has a payable function that accepts values defining the Gift and a value of ETH.
A CryptoGift has a price of 0.01 ETH (just like a coffe) that will be taken from the ETH amount and used to support the platform development :)

function buyToken(
address beneficiary,
string content,
uint256 date,
uint256 style
)
external
payable
{
uint256 weiAmount = msg.value;
_preValidatePurchase(beneficiary, weiAmount);
uint256 giftValue = msg.value.sub(_price); uint256 lastTokenId = _processPurchase(
giftValue,
beneficiary,
content,
date,
style
);
emit TokenPurchase(
msg.sender,
beneficiary,
giftValue,
lastTokenId
);
_forwardFunds(giftValue, beneficiary);
}

The _preValidatePurchase checks that beneficiary is not the ZERO address and that user is sending more than price.

function _preValidatePurchase(
address beneficiary,
uint256 weiAmount
)
internal
view
{
require(beneficiary != address(0));
require(weiAmount >= _price);
}

The _processPurchase function creates the token by calling newGift as described above.

function _processPurchase(
uint256 amount,
address beneficiary,
string content,
uint256 date,
uint256 style
)
internal
returns (uint256)
{
return _token.newGift(
amount,
msg.sender,
beneficiary,
content,
date,
style
);
}

Finally the _forwardFunds function will split the funds by transferring “price” to CryptoGift wallet and “giftValue” to beneficiary.

function _forwardFunds(
uint256 giftValue,
address beneficiary
)
internal
{
if (_price > 0) {
_wallet.transfer(_price);
}
if (giftValue > 0) {
beneficiary.transfer(giftValue);
}
}

View Smart Contracts project and discover how to install and test it.

Dapp — Frontend

The client is an app hosted on GitHub Pages and built with VuePress, a minimalistic site generator with Vue component based layout system.

The core Ethereum stuff are defined into a mixin dapp.js that handle web3.js library configuration and Smart Contracts ABI loading.

The initWeb3 function check if MetaMask is installed and if it is using ethereum provider like the EIP-1102 introduced.

...
if (checkWeb3 && (typeof ethereum !== 'undefined' || typeof web3 !== 'undefined')) {
if (ethereum) {
console.log('injected web3');
this.web3Provider = ethereum;
} else {
console.log('injected web3 (legacy)');
this.web3Provider = web3.currentProvider;
this.legacy = true
}
this.web3 = new Web3(this.web3Provider);
this.metamask.installed = true;
this.web3.version.getNetwork(async (err, netId) => {
if (err) {
console.log(err);
}
this.metamask.netId = netId;
if (netId !== this.network.list[network].id) {
this.network.current = this.network.list[this.network.map[netId]];
await this.initWeb3(network, false);
}
resolve();
});
} else {
console.log('provided web3');
this.network.current = this.network.list[network];
this.web3Provider = new Web3.providers.HttpProvider(this.network.list[network].web3Provider);
this.web3 = new Web3(this.web3Provider);
resolve();
}
...

The initContracts function instead loads the ABIs and then calls a “ready” function that can be overridden.

initContracts () {
this.contracts.token = this.web3.eth.contract(TokenArtifact.abi);
this.instances.token = this.contracts.token.at(__TOKEN_ADDRESS__);
this.contracts.market = this.web3.eth.contract(MarketArtifact.abi);
this.instances.market = this.contracts.market.at(__MARKET_ADDRESS__);
this.ready();
}

Encryption of CryptoGift content is handled in the encryption.js mixin.

...
encrypt (text, encryptionKey) {
return CryptoJS.AES.encrypt(JSON.stringify(text), encryptionKey).toString();
},
decrypt (text, encryptionKey) {
const bytes = CryptoJS.AES.decrypt(text.toString(), encryptionKey);
return bytes.toString();
},
...

View DApp project and discover how to install and test it.

Try building your CryptoGift https://vittominacori.github.io/cryptogift/

For any thought about it please comment here, open issue on repository or contact me on Twitter or LinkedIn.

--

--

Vittorio Minacori

Software Engineer. Blockchain & Web3. Author of ERC-1363.