Certify

Implementation of the certification process into your admin workf

The library can be downloaded here: {{ link }}

Full working example can be downloaded here: {{ link }}

This template require http protocol to work properly or you may get a CORS Policy error. To test locally, a server (wamp, node server etc) is required to access the index.html through http(s):// and not file:///C:/...

First, you need to create an html page and include the library call:

<script src="assets/js/signchain-signer.min.js"></script>

EXAMPLE OF THE HTML PART

Create your own simple html to allow you to connect with your Waves Account, select the file you want to certify and then register the hash of this file on the Blockchain using Waves Signer.

<!DOCTYPE html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Sign signer</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="assets/css/bs.css">
    <link rel="stylesheet" href="assets/css/style.css">
  </head>
  <body>
    <div class="container">
      <div class="row">

        <div class="col-lg-12 mt-4">
          
          <div class="mb-4">
            <button id="login" class="btn btn-primary">SIGN IN</button> <button id="logout" class="hide btn btn-primary">LOGOUT</button>
          </div>

          <div id="logged_wrap">

            <div id="account_details" class="wrapper mt-4">
              <h2>Account details</h2>
              <div id="userInfos" class="mt-2"></div>
              <div id="userCredit" class="mt-2"></div>
              <div id="userBalance" class="mt-2"></div>
              <div id="certificationCost" class="mt-2"></div>
            </div>
            
            <div id="certify_details" class="wrapper mt-4 mb-4">
              <h2>Certify</h2>
              <div class="mb-4">
                <input type="file" id="selectFile" />
                <div id="fileInfos" class="mt-2"></div>
              </div>
              <button id="certify" class="btn btn-primary">CERTIFY</button>
              <div id="result" class="mt-2"></div>
            </div>


          </div>

        </div>
      </div>
  </div>
  <script src="assets/js/signchain-signer.min.js"></script>
  <script src="assets/js/site.js"></script>
  </body>
</html>

This library includes three methods that allows you to retrieve a connected user's SIGN balance, his SIGN Certification credit if any as well as the current Certification Cost in SIGN Utility Tokens, this information is displayed in our template as #userCredit, #userBalance and #certificationCost

Then we have the #login button to allow users to log in with Waves Signer, followed by the #userInfos block that will receive Waves Signer data once connected (address and publicKey)

The #selectFile input field will allow file selection on the user's computer, once the file is selected, the hash will be calculated locally client side (files never go online during the hashing process) and then the #fileInfos block will receive the file details (file name and file hash in our example)

Finally, the #certify button once clicked will call the invoke() method of Waves Signer with the appropriate data and ask the user to confirm the transaction. Once confirmed, the block #result will receive the transaction ID.

At the bottom you need to include the two javascript files, the library first then your custom javascript.

EXAMPLE OF THE JAVASCRIPT PART

All you need to do is to create your different listener and call the appropriate methods of the library; here an examples of each listener associated to our example HTML before we go into detail:

let network = "T" // W for mainnet, T for testnet
let explorerHost = network == "T" ? "https://testnet.wavesexplorer.com" : "https://wavesexplorer.com"
let verifyAddress = "3MvujUZTtoANRzrKYKzx99Vtb9TUh6VPCDm"
let Signchain = new window.signchain(network)
let storeData = null;
let storeDataVerify = null
let userData = null;

// LOGIN WITH SIGNER
document.getElementById("login").addEventListener("click", async function (e) {

  try{
    userData = await Signchain.login();
    if(login.toString().includes("User rejection!")){return}
    document.getElementById("logged_wrap").classList.add("show")
    document.getElementById("userInfos").innerHTML = "Address: " + userData.address + "<br>Public Key: " + userData.publicKey
    document.getElementById("login").classList.add("hide")
    document.getElementById("logout").classList.remove("hide")
    getCredit();
    getBalance();
    getPrice()
  }catch(err){
    console.log(err)
  }
});

// LOGIN WITH SIGNER
document.getElementById("logout").addEventListener("click", async function (e) {
  try{
    await Signchain.logout();
    document.getElementById("logged_wrap").classList.remove("show")
    document.getElementById("userInfos").innerHTML = ""
    document.getElementById("userBalance").innerHTML = ""
    document.getElementById("userCredit").innerHTML = "";
    document.getElementById("login").classList.remove("hide")
    document.getElementById("logout").classList.add("hide")
    storeData = null;
    userData = null;
  }catch(err){
    console.log(err)
  }
});

// ADD THE LISTENER TO YOUR CERTIFY INPUT FIELD
document.getElementById("selectFile").addEventListener("change", async function (event) {
  if(event.target.files[0]){
    let file = event.target.files[0]
    await Signchain.hash(file, function (res) {
      storeData = res;
      document.getElementById("fileInfos").innerHTML = "Title: " + storeData.title + "<br>Sha256 file hash: " + storeData.hash
    });
  }else{
    document.getElementById("fileInfos").innerHTML = ""
    storeData = null;
  }
});

// ADD THE LISTENER TO YOUR VERIFY INPUT FIELD
document.getElementById("selectFileVerify").addEventListener("change", async function (event) {
  if(event.target.files[0]){
    let file = event.target.files[0]
    await Signchain.hash(file, function (res) {
      storeDataVerify = res;
      document.getElementById("fileInfosVerify").innerHTML = "Hash sha256: " + storeDataVerify.hash
    });
  }else{
    document.getElementById("fileInfosVerify").innerHTML = ""
    storeDataVerify = null;
  }
});

document.getElementById("verify").addEventListener("click", async function (e) {
  document.getElementById("resultVerify").innerHTML = ""
  let checkData = await Signchain.verify(storeDataVerify.hash, verifyAddress)
  // is file certified by correct address
  if(checkData.valid){
    let certificationDate = JSON.parse(checkData.data[0].value).timestamp
    console.log(certificationDate)
    document.getElementById("resultVerify").innerHTML = '<strong class="valid">Certification valid</strong> and certified on '+new Date(certificationDate);
  }else{
    document.getElementById("resultVerify").innerHTML = '<strong class="invalid">Certification not found</strong>'
  }
})

// ON CLICK, RETRIEVE STORED DATA AND CALL THE FILE CERTIFICATION DAPP WITH SIGNER
document.getElementById("certify").addEventListener("click", async function (e) { 
  let useCredit = false
  document.getElementById("result").innerHTML = "Transaction processing... ";
  try{
    let credit = await Signchain.checkUserCredit(userData.address);
    if(credit > 0){
      useCredit = true
    }
    if(storeData == null) {
      document.getElementById("result").innerHTML = "File cannot be empty... "; 
      return
    }
  }catch(err){
    document.getElementById("result").innerHTML = err;
    console.log(err)
  }

  try{
    let certify = await Signchain.certify(storeData, userData.publicKey, useCredit);

    if(certify.id){ 
      document.getElementById("result").innerHTML = "Transaction ID: <a href=\""+explorerHost+"/tx/" + certify.id + "\" target=\"_blank\">"+certify.id+"</a>";
    }else{
      document.getElementById("result").innerHTML = "Something went wront: " + certify;
    }
    setTimeout(getCredit(userData.address), 3000);
  }catch(err){
    document.getElementById("result").innerHTML = err;
    console.log(err)
  }
});

// GET CERTIFICATION COST ON LOAD
let getPrice = async function(){
  try{
    let cost = await Signchain.getCertificationFee();
    document.getElementById("certificationCost").innerHTML = "Cost per certification: " + cost + " SIGN"
  }catch(err){
    console.log(err)
  }
}


// GET USER SIGN BALANCE
let getBalance = async function(){
  try{
    let balance = await Signchain.getUserBalance(userData.address);
    let balanceDecimal = balance / Math.pow(10, 8);
    document.getElementById("userBalance").innerHTML = "This account have: " + balanceDecimal + " SIGN Token(s)"
  }catch(err){
    console.log(err)
  }
}

// CHECK IF USER HAVE CREDIT
let getCredit = async function(){
  try{
    let credit = await Signchain.checkUserCredit(userData.address);
    credit = credit == undefined ? 0 : credit
    document.getElementById("userCredit").innerHTML = "This account have: " + credit + " Credit(s)"
  }catch(err){
    console.log(err)
  }
}

INIT THE LIB

let network = "T" // W for mainnet, T for testnet
let explorerHost = network == "T" ? "https://testnet.wavesexplorer.com" : "https://wavesexplorer.com"
let verifyAddress = "3MvujUZTtoANRzrKYKzx99Vtb9TUh6VPCDm"
let Signchain = new window.signchain(network)

The important initialisation part is:

let Signchain = new window.signchain(network)

You can pass the network parameter (W for mainnet or T for testnet) or leave it empty for mainnet by default.

LOGIN Signchain.login()

document.getElementById("login").addEventListener("click", async function (e) {
  try{
    userData = await Signchain.login();
    if(login.toString().includes("User rejection!")){return}
    document.getElementById("logged_wrap").classList.add("show")
    document.getElementById("userInfos").innerHTML = "Address: " + userData.address + "<br>Public Key: " + userData.publicKey
    document.getElementById("login").classList.add("hide")
    document.getElementById("logout").classList.remove("hide")
    getCredit();
    getBalance();
    getPrice()
  }catch(err){
    console.log(err)
  }
});

This calls Waves Signer login() method, once logged in, the data (address and publicKey are stored in a userData variable, this information is displayed in the page and we call it the getCredit(), getBalance() and getPrice() methods we will define later

LOGOUT Signchain.logout()

document.getElementById("logout").addEventListener("click", async function (e) {
  try{
    await Signchain.logout();
    document.getElementById("logged_wrap").classList.remove("show")
    document.getElementById("userInfos").innerHTML = ""
    document.getElementById("userBalance").innerHTML = ""
    document.getElementById("userCredit").innerHTML = "";
    document.getElementById("login").classList.remove("hide")
    document.getElementById("logout").classList.add("hide")
    storeData = null;
    userData = null;
  }catch(err){
    console.log(err)
  }
});

HASH FILE Signchain.hash()

document.getElementById("selectFile").addEventListener("change", async function (event) {
  if(event.target.files[0]){
    let file = event.target.files[0]
    await Signchain.hash(file, function (res) {
      storeData = res;
      document.getElementById("fileInfos").innerHTML = "Title: " + storeData.title + "<br>Sha256 file hash: " + storeData.hash
    });
  }else{
    document.getElementById("fileInfos").innerHTML = ""
    storeData = null;
  }
});

This listener listens to any changes on our input file field and calls the hash method from the signchain library, the response object will include the title, the hash and the file in byteArray if the file is under 10Mb or null for bigger file (at the moment we limitate IPFS upload at 10Mb), this data is stored in a storeData object and the title and hash are displayed on the page

CERTIFY THE FILE Signchain.certify(data: object, pubKey: string, credit: boolean)

document.getElementById("certify").addEventListener("click", async function (e) { 
  let useCredit = false
  document.getElementById("result").innerHTML = "Transaction processing... ";
  try{
    let credit = await Signchain.checkUserCredit(userData.address);
    if(credit > 0){
      useCredit = true
    }
    if(storeData == null) {
      document.getElementById("result").innerHTML = "File cannot be empty... "; 
      return
    }
  }catch(err){
    document.getElementById("result").innerHTML = err;
    console.log(err)
  }

  try{
    let certify = await Signchain.certify(storeData, userData.publicKey, useCredit);

    if(certify.id){ 
      document.getElementById("result").innerHTML = "Transaction ID: <a href=\""+explorerHost+"/tx/" + certify.id + "\" target=\"_blank\">"+certify.id+"</a>";
    }else{
      document.getElementById("result").innerHTML = "Something went wront: " + certify;
    }
    setTimeout(getCredit(userData.address), 3000);
  }catch(err){
    document.getElementById("result").innerHTML = err;
    console.log(err)
  }
});

After clicking on the #certify button, the certify() method is called with the storeData object (which includes the result from the hashing function), the public key of the connected account and a boolean to use credits or tokens as parameters. In this example we'll first check if the Certification Credit balance of the user is superior to 0, if so, we use credit, if not, we use SIGN's Utility token as payment.

storeData structure { buffer: ArrayBuffer(98093) {}, hash: "788ce1b308d265efad63373183ce563e6b90b2364a978cbfdb948314a72ac8f8", index: "a6049a50-a259-11ea-b11c-6d336bf17b27", title: "Send_Curl_Commands_Online.png" }

userData structure { address: "3MsG6jPNCrVJUtYB7XJBxS7utWsXAf4n9Vp", publicKey: "CqbpJxfYeagkaT7sXQpxsfJXS1ZPxjM1Giw94n3y4Tp5" }

GET THE CERTIFICATION PRICE Signchain.getCertificationFee()

let getPrice = async function(){
  try{
    let cost = await Signchain.getCertificationFee();
    document.getElementById("certificationCost").innerHTML = "Cost per certification: " + cost + " SIGN"
  }catch(err){
    console.log(err)
  }
}

GET THE USER SIGN TOKEN BALANCE Signchain.getUserBalance(address: string)

let getBalance = async function(){
  try{
    let balance = await Signchain.getUserBalance(userData.address);
    let balanceDecimal = balance / Math.pow(10, 8);
    document.getElementById("userBalance").innerHTML = "This account have: " + balanceDecimal + " SIGN Token(s)"
  }catch(err){
    console.log(err)
  }
}

GET THE USER CERTIFICATION CREDIT BALANCE Signchain.checkUserCredit(address: string)

let getCredit = async function(){
  try{
    let credit = await Signchain.checkUserCredit(userData.address);
    credit = credit == undefined ? 0 : credit
    document.getElementById("userCredit").innerHTML = "This account have: " + credit + " Credit(s)"
  }catch(err){
    console.log(err)
  }
}

Last updated