Building a Fully Decentralized Blog on Arweave using WeaveDB and 4EVERLAND
In this tutorial, you will learn how to build a fully decentralized blog using WeaveDB and 4EVERLAND.
What is WeaveDB
WeaveDB is a NoSQL database powered by Warp Contracts (SmartWeave) on the Arweave blockchain.
The query APIs are similar to Firestore from Google but completely decentralized.
- Data is stored on the Arweave permanent storage where anyone can access without permission.
- User authentication is done by pure cryptography using crypto wallets such as MetaMask and ArConnect.
- SmartWeave makes it possible to apply complex computation to stored data for web-centric large-scale dapps just like web2 apps.
To learn more about WeaveDB, click here.
What is 4EVERLAND
4EVERLAND is a Web 3.0 cloud computing platform that integrates storage, computing, and network as its core capabilities. It aims to provide a more affordable and efficient way for developers to build and host their dApps, without the need for expensive and complex server infrastructures.
More information about 4EVERLAND can be found here.
What is Arweave
Arweave is a Web3 protocol that offers data storage on a decentralized network of devices. It has a set of nodes that are incentivised to store data permanently and access the content stored on the network via the Arweave gateway.
Get started
4EVERLAND provides a solution for WeaveDB users who have large file storage requirements, including:
- Before relayed queries can be processed, all conditions must be met including the
job ID
,allowed relayers
, andextra data schema
, which can be pre-set on the WeaveDB instance for a relayer job. - In order to send a query and attach metadata, the data owner signs the metadata and sends both the
signed query
and thenote
to the relayer. - The relayer uses 4EVERLAND to upload the
note
to Arweave and obtains atx.id
, which is then attached as extra data and signed with eip712 before being sent as a transaction to the WeaveDB contract on Warp. - The WeaveDB contract validates
job ID
,allowed relayers
, andextra data schema
, as well as eip712 signatures. - Access control rules on the collection allow for modification of the initial query data, which includes adding the data
owner
andtx.id
if thesigner
is indeed theowner
. - To access the
note
, the frontend dapp can retrieve the metadata from WeaveDB and utilize thetx.id
to retrieve thenote
atarweave.net/[tx.id]
.
Assuming that you’re using 4EVERLAND + WeaveDB to create a simple blog for publishing articles, each article should include the following elements:
- Title
- Author (wallet address of the person who posted the article)
- Publication date
- Content
To achieve this, you’ll need to:
- Deploy a WeaveDB contract to store article data.
- Run a Relayer to upload the article content to Arweave via 4EVERLAND and add the article hash value to the article data uploaded to WeaveDB.
- WeaveDB will tag the article owner based on their signer and write the data to Arweave.
Deploy WeaveDB Contract
Clone the Repo
git clone https://github.com/weavedb/weavedb.git
cd weavedb
yarn
Deploy WeaveDB Contracts
node scripts/generate-wallet.js mainnet
yarn deploy
Or you could follow this tutorial and use the Web Console.
Now you should receive contractTxId
for the deployed contract.
If you have never used WeaveDB before, we recommend going through this quick start tutorial.
Configure DB Instance
Set up Data Schema
We are going to set up only 1 collection.
notes
: a collection of notes
const schema = {
type: "object",
required: ["date", "id", "author", "title"],
properties: {
title: {
type: "string",
},
date: {
type: "number",
},
id: {
type: "string",
},
author: {
type: "string",
},
},
}
await sdk.setSchema(schema, "notes", { ar: wallet })
date
: published dateid
: Arweave Transaction IDauthor
: author of the notetitle
: title of the note
Set up Relayer Job
Set a simple relayer job.
relayerAddress
: an EVM address of the relayer to sign and relay WeaveDB queries.schema
: JSON schema for the additional data to be attached by the relayer. The relayer will attach 3 pieces of extra data,date
,id
, andauthor
.jobID
: our arbitrary jobID will be4ever
.
const job = {
relayers: [relayerAddress],
schema: {
type: "object",
required: ["date", "id", "author"],
properties: {
date: {
type: "number",
},
id: {
type: "string",
},
author: {
type: "string",
},
},
},
}
await sdk.addRelayerJob("4ever", job, { ar: wallet })
With these simple settings, we expect the relayer to receive the author
, title
, and body content before adding the date
and uploading it to 4EVERLAND.
Finally, it relays the signed WeaveDB query with extra data of (author
, date
, id
) to the signed query.
Set up Access Control Rules
const rules = {
"let create,update": {
"resource.newData.author": { var: "request.auth.extra.author" },
"resource.newData.date": { var: "request.auth.extra.date" },
"resource.newData.id": { var: "request.auth.extra.id" },
},
"allow create": {
"==": [{ var: "request.auth.signer" }, { var: "resource.newData.author" }],
},
"allow update,delete": {
"==": [{ var: "request.auth.signer" }, { var: "resource.data.author" }],
},
}
await sdk.setRules(rules, "notes", { ar: wallet })
Set up Local gRPC Nod
For a better performance for the relayer, you would want to set up a local grpc node.
Follow this tutorial to do so.
NextJS Frontend APP
Set up Environment Variables
Create .env.local
file and set the following variables.
WEAVEDB_RPC_NODE="localhost:8080"
RELAYER_PRIVATEKEY="Relayer_EOA_Privatekey"
NEXT_PUBLIC_WEAVEDB_CONTRACT_TX_ID="Your_Contract_Tx_Id"
NEXT_PUBLIC_WEAVEDB_RPC_WEB="http://localhost:8080"
Set up Relayer
Relayers are the infrastructure used for sending assets or information across chains, and in WeaveDB’s case, the Relayer they developed can be used to bridge any data from other chains to WeaveDB, storing it directly on Arweave. It can execute queries on behalf of you using your eip-712 signatures. This comes in handy, when making cross-chain state verifications with off-chain oracles.
In this case, we will set up the relayer as the NextJS serverless api.
The relayer
- constructs a
note
object - uploads it to 4EVER
- adds extra data of
{ id, author, date }
to the user signed params - and relays the query to WeaveDB.
import { S3 } from "@aws-sdk/client-s3";
const contractTxId = process.env.NEXT_PUBLIC_WEAVEDB_CONTRACT_TX_ID
const { isNil } = require("ramda")
const SDK = require("weavedb-node-client")// Create S3 instance
// View API key in 4EVERLAND Dashboard
const accessKey = "YOUR_ACCESS_KEY";
const secretKey = "YOUR_SECRET_KEY";const s3Instance = new S3({
endpoint: "https://endpoint.4everland.co",
credentials: {
accessKeyId: accessKey,
secretAccessKey: secretKey,
},
region: "eu-west-2",
})
export default async (req, res) => {
const { body, params } = JSON.parse(req.body)
const note = {
title: params.query[0].title,
body,
author: params.caller,
date: Date.now(),
}
const data = JSON.stringify(note); let error = null
let success = false
let relay_tx = null
try {
//Upload note to 4EVER arbucket and get tx.id
await s3Instance.putObject({
Bucket: 'YOUR_BUCKET_NAME',
Key: 'note.json',
Body: data,
ContentType: 'application/json',
});
const result = await s3Instance.headObject({
Bucket: 'YOUR_BUCKET_NAME',
Key: 'note.json',
}); const 4ever_txid = result.Metadata["arweave-hash"]
//Signing and uploading to WeaveDB
if (!isNil(4ever_txid)) {
const sdk = new SDK({
contractTxId,
rpc: process.env.WEAVEDB_RPC_NODE,
})
relay_tx = await sdk.relay(
params.jobID,
params,
{ id: 4ever_txid, author: note.author, date: note.date },
{
jobID: params.jobID,
privateKey: process.env.RELAYER_PRIVATEKEY,
}
)
if (relay_tx.success) {
success = true
} else {
error = relay_tx.error
}
} else {
error = relay_tx
}
} catch (e) {
console.log(e)
error = e
}
res.status(200).json({ success, error, tx: relay_tx })
}
UI Implementation
You can then get creative and implement a front-end decentralised application based on the above framework. A reference interface is shown below.
To conclude
That’s it! You have just used a few lines of code to combine 4EVERLAND + WeaveDB to complete a fully decentralized blog!