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:
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.
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.
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
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)
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.