Blockmason is excited to launch our second blockchain product called Link. Link allows developers to use smart contracts and the power of blockchain in their web or mobile applications with very little to no prior blockchain experience. Link creates classic, conventional, web-based APIs for any smart contract written on a programmable blockchain (e.g. Ethereum).
In this activity, we will use Link to record ownership of assets (in this example, collectible digital stamps) on the blockchain.
With Link and its built-in private blockchain, there is no need to use common blockchain development tools such as Infura or acquire an underlying token such as ETH from an exchange to pay for gas fees - it just works!
A simple front-end template is provided and Parcel will be used as the web application bundler and server.
The key steps of this activity are:
- Setup project and install dependencies
- Deploy a basic Ownership smart contract to the blockchain using Link
- Configure a front-end JavaScript file
- Run your decentralized application (DApp)
You will need to setup the following for this activity:
Install
Node
andnpm
: https://nodejs.org/en/download/ (note - you can also useyarn
)
Clone the Github repo: https://github.com/blockmason/simple-ownership-contract-demo into a new folder.
In the new folder, run
npm install
which will install the following key dependencies:
-
@blockmason/link-sdk
- https://www.npmjs.com/package/@blockmason/link-sdk - a simple SDK for interacting with the Link project. -
parcel-bundler
- https://parceljs.org/getting_started.html - for bundling and running the web application
Create a Blockmason Link account - register at https://mason.link/sign-up and then setup your demo organization.
The Ownership.sol
file contains a very simple Ownership Smart Contract:
pragma solidity ^0.5.8;
contract Ownership {
mapping(string => address) public ownerOf;
address public authority;
constructor() public {
authority = msg.sender;
}
function setOwner(string memory asset, address owner) public {
ownerOf[asset] = owner;
}
}
-
Ownership is recorded in a mapping called
ownerOf
between an asset name (some string) and an Ethereum address (a 20 byte value), which looks something like0xca14563Ce2585B6026b7691f264ac2173CdEC530
for example. -
Using the keyword
public
for theownerOf
mapping object automatically provides us with a getter for that object. -
The
authority
in this case will be a Link managed Ethereum account.
Sign into your Link account and copy and paste the
Ownership.sol
contract code into the Link IDE. We'll call this projectOwnership
.
Now click on the
API
button on theCode/API
toggle and you will see API endpoints for all the Ownership smart contract functions and attributes!
That's it! Our Ownership smart contract is automatically deployed to the Link private blockchain and we are ready to use our web API endpoints in our front-end DApp.
Taking a look inside the src/
folder, we see that it is a very basic JavaScript app with data pulled in from stamps.json
. We also make use of jQuery and Bootstrap in our code.
Take a look at
index.html
andjs/app.js
code templates, which is where we will focus our efforts.
We see that the html template loads each of the stamps with data from stamps.json
including an image, and an input field for setting an owners address. When a user presses the Own
button, the intent is for the user-specified address to be recorded as the stamp's owner.
The template code has been provided and we just need to fill in the details.
const stampData = require('../stamps.json');
const { link } = require('@blockmason/link-sdk');
const ownershipProject = link({
clientId: '',
clientSecret: ''
});
We import the stamp data and the @blockmason/link-sdk
package. We then need to provide the clientId
and clientSecret
from Link in order to use the .get
and .post
methods provided by the link
object.
Copy and paste your specific
clientId
andclientSecret
from the bottom of the Link IDE screen:
App = {
init: function() {
// Load stamps.
const stampsRow = $('#stampsRow');
const stampTemplate = $('#stampTemplate');
for (i = 0; i < stampData.length; i ++) {
stampTemplate.find('.panel-title').text(stampData[i].name);
stampTemplate.find('img').attr('src', stampData[i].picture);
stampTemplate.find('.stamp-location').text(stampData[i].location);
stampTemplate.find('.btn-own').attr('data-id', stampData[i].id);
stampsRow.append(stampTemplate.html());
App.markOwned(i, stampData[i].id);
}
return App.bindEvents();
},
bindEvents: function() {
$(document).on('click', '.btn-own', App.setOwnership);
},
The above code:
-
Loads all the stamp data as part of our
stampTemplate
into ourstampRow
element. -
Calls
App.markOwned(..)
which will check the blockchain for ownership records and mark an asset with its corresponding owner on the front-end. -
Returns a button 'click' event listener
markOwned: async function(index, name) {
// Mark stamp ownership
},
Here, we will call the GET /ownerOf
API endpoint to retrive the owner of an asset value we pass.
Note the following:
- We pass an attribute called
value
which is a string - We get a response object called
result
which is an address
The index
function argument is used to identify which .panel-stamp
element is being referenced during the for loop in App.init()
. Our completed code looks like the following:
markOwned: async function(index, name) {
const asset = {
"value": name
};
const { result } = await ownershipProject.get('/ownerOf', asset);
if (result !== '0x0000000000000000000000000000000000000000') {
$('.panel-stamp').eq(index).find('#ownerAddress').empty();
$('.panel-stamp').eq(index).find('#ownerAddress').append('Owner: ' + result).css({ wordWrap: "break-word" });
}
},
Lastly, we need to complete the setOwnership
function:
setOwnership: async function(event) {
event.preventDefault();
if (confirm("Confirm ownership of this stamp, which can take a few seconds to record on the blockchain")) {
const stampId = $(event.target).data('id');
const owner = $(event.target).closest("div.owner-address").find("input[name='owner']").val();
$(event.target).text("Processing").attr('disabled', true);
// Set Ownership code
}
}
Here, we will call the POST /setOwner
API endpoint to set the owner of an asset with an address.
Note the following:
- We pass an attribute called
asset
which is a string, andowner
which is an address. - We don't get a response object
That last point is true if the POST request is successful. Otherwise, an error
object is returned containing the error details. Our completed code looks like the following:
setOwnership: async function(event) {
event.preventDefault();
if (confirm("Confirm ownership of this stamp, which can take a few seconds to record on the blockchain")) {
const stampId = $(event.target).data('id');
const owner = $(event.target).closest("div.owner-address").find("input[name='owner']").val();
$(event.target).text("Processing").attr('disabled', true);
const reqBody = {
"asset": stampId,
"owner": owner
};
const response = await ownershipProject.post('/setOwner', reqBody);
if(response.errors) {
alert(response.errors[0].detail);
$(event.target).text("Own").attr('disabled', false);
}
else {
console.log('Post request successful');
$(event.target).text("Own").attr('disabled', false);
$(event.target).closest("div.owner-address").find("input[name='owner']").val('');
$(event.target).parents(".panel-stamp").find("#ownerAddress").text('Owner: ' + owner);
}
}
}
Find the complete code in app-complete.js
.
Note - we didn't use any complex or large libraries like web3.js
, which requires an instance of the Ownership
contract to be created before the contract function methods can be called. Except for our confirm message, there is nothing in the code to even indicate that blockchains are involved!
Run the application from the project root folder with:
npm start
See the full command this executes under scripts
in package.json
.
Note the following:
-
By default, the DApp will run at https://localhost:1234 . You can specify the
-p
flag in the scripts command inpackage.json
if you want to use a specific port. -
Parcel
will create the following folders in your project folder:.cache/
anddist/
. If you run into any errors while running your DApp, delete these folders and runnpm start
again.
Copy and paste in an Ethereum wallet address (for example 0xca14563Ce2585B6026b7691f264ac2173CdEC530
) and try to own one of the Collectible Stamps. Note: if you do not enter in a valid address, you will see the following error alert pop up:
When running, your DApp should look similar to the following:
Congrats on getting your first DApp running from scratch using Link!
Throughout this example, we touched on Link concepts including deploying a smart contract, using the Link IDE, using Link API endpoints, and more.
To learn more about Blockmason Link, check out the official Link website at blockmason.link or our weekly blog posts at blockmason.link/blog.
- Twitter: https://twitter.com/BlockmasonLink
- Telegram: https://t.me/blockmasonlink
- Reddit: https://www.reddit.com/r/blockmason/