Using smart contracts to efficiently store information on the blockchain
We recently completed a project in the IP protection and copyright space. The core idea of the product was to use blockchain as a ledger to record information on ownership of artwork and it's metadata. Artwork here, is used in the more broader sense covering various media formats like images, videos, text, code, audio etc.
The product was a quasi-social-media application with options to like, share and collaborate on an artwork. As far as tech is concerned, we developed a mobile application with Flutter as a frontend. The backend was NodeJS with AdonisJS on top. Adonis provides features like ORM (Lucid ORM), request validators, authentication and database migration tool.
The Problem
The foundations of the product lay in the idea of being able to prove ownership of a piece of artwork. Along with that we also wanted to record interactions like viewing, liking, sharing and collaborating on the artwork. This requirement was particularly challenging since we had to record the interaction data (which is generated frequently) in the blockchain. Writing to the blockchain can take time depending on the traffic and also get expensive. So we had to find a fast yet cost effective way to store information on the blockchain even when the application scales to hundreds of thousands of users.
The Solution
We decided to use a tool, Metakeep, rather than interacting with the blockchain directly. Metakeep provides a Node SDK along with the REST endpoints to interact with the polygon blockchain. For recording ownership of an artwork we decided to use NFTs. All artwork uploaded to the platform would be minted as an NFT using Metakeep along with the owner and collaborators (if any) wallet addresses as metadata information. One thing we observed is Metakeep lists the minted NFTs in the OpenSea marketplace. There was no way atleast from the API to tweak this. Since the platform demanded exclusivity we replaced the actual artwork with a placeholder/default image and recorded the file hash along with other metadata information.
To mint an NFT we used the mint
endpoint made available by Metakeep. The pseudocode would look like below
export default function mint({auth, request, response}) {
const payload = request.validate(MintValidator)
let file_hash = FileHelper.getFileHash(payload.file)
axios.post({
url: MINT_URL,
headers: {
'accept': 'application/json',
'Idempotency-Key': `some-random-string`,
'content-type': 'application/json',
'x-api-key': METAKEEP_API_KEY,
},
data: {
nft: { collection: COLLECTION_ADDRESS },
to: { email: EMAIL },
metadata: {
name,
description,
image
attributes: {
collaborators: ['wallet-address-1','wallet-address-2']
creators: ['wallet-address-3','wallet-address-4']
file_hash
}
}
}
})
}
With this we were successfully able to mint an NFT and see it appear in the blockchain. For the next part ie. storing interaction information in the blockchain, required us to write a smart contract and then deploy it to Metakeep's infrastructure. We used Solidity to write a smart contract and Hardhat to compile it. The abi
from the compilation step was then used to deploy it to the Metakeep infrastructure.
Intially we decided to create a state variable and then store the interaction information in it. But we quickly realised that as the application scales, storing information in the blockchain will get expensive and slow. Events to the rescue! Rather than storing the information on the chain we decided to emit that information using an event. The data emitted by an event is not stored in the state of a smart contract. It is recorded in a transaction receipt and stored in the block. This can then be accessed and queried using Metakeep API or blockchain explorers like polyscan.
To implement this we first had to write a simple smart contract
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "metakeep-lambda/ethereum/contracts/MetaKeepLambda.sol";
contract InteractionsData is MetaKeepLambda {
constructor(address lambdaOwner, string memory lambdaName) MetaKeepLambda(lambdaOwner, lambdaName) {}
event SaveInteractionData(string interactionData);
function logInteractionData(string calldata eventData) public {
emit SaveInteractionData(eventData);
}
}
To compile this smart contract we setup a hardhat project and placed this under contracts
folder. To compile we ran,
npx hardhat compile
The abi from the compiled contract generated in the artifacts/contracts
folder was then passed to Metakeep for creating a lambda.
To register a lambda we used the lambda/create
endpoint,
async function registerLambda() {
const { ethAddress } = await getDeveloperWalletAddress()
const body = {
constructor: { args: [ethAddress, "StoreArkData"] },
abi: contractData["abi"],
bytecode: contractData["bytecode"]
}
const res = await fetch(`${METAKEEP_LAMBDA_BASE_URL}/create`, {
method: "POST",
headers: headers,
body: JSON.stringify(body)
})
const data = await res.json()
return data
}
To trigger a lambda we used lambda/invoke
endpoint,
async function triggerLambda(resData: string, transaction_type: string) {
const body = {
lambda: `${process.env.METAKEEP_LAMBDA_ADDRESS}`,
function: { name: "triggerEvent", args: [resData] },
description: { text: `Store data in blockchain for ${transaction_type}` }
}
const res = await fetch(`${METAKEEP_LAMBDA_BASE_URL}/invoke`, {
method: "POST",
headers: headers,
body: JSON.stringify(body)
})
const data = await res.json()
}
With this we were able to store the artwork in the blockhain and the interaction data in the blockchain with fast and cost-effective way.
Key Takeaways
- Storing data in a state variable in a smart contract is not cost effective. Storing it in transaction receipt at fraction of the cost is a better alternative.
- Metakeep provides user friendly apis and abstracts away the complexity of dealing with blockchains.
- Since Truffle is being sunset and will no longer receive updates, Hardhat serves as a strong alternative.
Conclusion
We were not only able to meet the client's requirements but also helped them save money in the long run. The client was happy with our implementation and the platform is now live for artists and creators.