Build a DApp on top of a decentralized social graph
When building a DApp that aims to return users control and ownership over their social data, interactions and relationships, a decentralized social graph could be useful. The first two social graphs that I was thinking of using are Lens Protocol and Farcaster.
However, there might be some difficulties to onboard users with these two protocols. Lens requires users to claim Lens handle through whitelisting while Farcaster asks users to pay an upfront annual storage fee.
Our team was frustrated as the onboarding friction might scare potential users away. I was thinking that an alternative solution would be to store the data on smart contract and decentralized data storage (like IPFS / Arweave). To reduce the cost, we can make use of some blockchains that have significantly lower gas fee than Ethereum. The drawbacks of this proposed solution are 1) no matter how low the gas is, it’s not completely free 2) users are required to sign transaction for their actions, this may affect the user experience 3) how to make the social data usable in the other DApps. It’s also unlikely for our team to build another social graph from scratch.
After days of research, I found Orbis. Orbis provides social infrastructures that is built on the Ceramic Network so it is gasless. Another beneficial feature of Orbis is that they only require single signature when onboarding.
To make the DApp more widely adopted among both Web3 and Web2 users, I have integrated Thirdweb Connect with the options of Web3 wallets and social login. In this design, users don’t have to setup and memorize another password. Making use of Orbis also makes it possible for our users to reuse the decentralized identity within the ecosystem.
Integrate Orbis with Web3 social onboarding
Orbis provides a demo coupled with open-source code snippets to showcase seamless Web3 social onboarding with Privy, Magic and Web3Auth. Read more about it on Mirror.
Please note that only an EOA wallet is supported so far, smart contract signatures aren’t supported by Ceramic (the data network Orbis is built on). This means that we cannot use Thirdweb Smart Wallet with Orbis yet.
As I’m personally a big fan of Thirdweb devtools, I would like to integrate Orbis and Thirdweb Connect to my DApp. The integration requires a little modification from the code example provided by Orbis team. I also appreciated the Orbis developer, Donat’s help in solving the challenges I encountered during the integration.
In provider-utility.js
, we need to wrap the provider with customized enable
and request
functions.
export const processToOrbisProvider = (signer) => {
const provider = signer?.provider;
provider.enable = async () => [await signer.getAddress()];
provider.request = async (payload) => {
if (payload.method === "personal_sign") {
const message = payload.params[0];
if (/^(0x)?[0-9A-Fa-f]+$/i.test(message)) {
const bufferMessage = Buffer.from(
message.startsWith("0x") ? message.slice(2) : message,
"hex"
);
return signer.signMessage(bufferMessage);
}
return signer.signMessage(message);
}
return provider.send(payload.method, payload.params || []);
};
return provider;
};
In orbis-context.tsx
, we connect to Orbis if the user is logged in and disconnect when the user is not.
"use client"
import { FunctionComponent, ReactNode, createContext, useContext, useEffect } from "react";
import { Orbis } from "@orbisclub/orbis-sdk";
import { useSigner, useUser } from '@thirdweb-dev/react';
import { processToOrbisProvider } from "@/lib/provider-utility";
// you may add extra fields in this context
export const OrbisContext = createContext({});
export function useOrbis() {
return useContext(OrbisContext);
}
interface IOrbisState {
children?: ReactNode;
}
interface IOrbisProps {
children?: ReactNode;
}
const orbis = new Orbis({});
export const OrbisProvider: FunctionComponent<IOrbisState> = ({ children }: IOrbisProps) => {
const signer = useSigner()
const { isLoggedIn } = useUser();
useEffect(() => {
async function checkOrbisSession() {
const orbisConnected = await orbis.isConnected();
if (orbisConnected && !isLoggedIn) {
orbis.logout()
}
if (!orbisConnected && isLoggedIn) {
const provider = signer?.provider;
if (provider) {
const orbisProvider = processToOrbisProvider(signer)
const orbisConnection = await orbis.connect_v2({
chain: "ethereum",
provider: orbisProvider,
});
}
}
}
checkOrbisSession()
}, [signer, isLoggedIn])
return <OrbisContext.Provider value={{}}> {children} </OrbisContext.Provider>;
}
Make sure you put ThirdwebProvider
upper level of OrbisProvider
as we need to use Thirdweb function in OrbisProvider
.
app/layout.tsx
import { ThirdwebProvider } from '@/components/thirdweb/thirdweb-provider' // setup your ThirdwebProvider based on Thirdweb guide, nothing special here
import { OrbisProvider } from '@/components/orbis/orbis-context'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>
<ThirdwebProvider>
<OrbisProvider>
{children}
</OrbisProvider>
</ThirdwebProvider>
</body>
</html>
)
}
The differences from code example orbis-web2-auth-examples:
We don’t convert the provider to BrowserProvider
(Ethers v6) or Web3Provider
(Ethers v5). This is because we already have Web3Provider
when connecting with a Web3 wallet, which is not the case if you use social login.
There are two ways to get signer:
- from
useSigner
import { useSigner} from '@thirdweb-dev/react';
const signer = useSigner();
/// ...
2. from the provider.getSigner()
import { useSigner} from '@thirdweb-dev/react';
/// ...
const signer = useSigner();
const provider = signer?.provider;
const orbisProvider = walletToBrowserProvider(provider);
/// ...
export const walletToBrowserProvider = (provider) => {
provider.enable = async () => [await provider.getSigner().getAddress()];
provider.request = async (payload) => {
if (payload.method === "personal_sign") {
const signer = await provider.getSigner();
const message = payload.params[0];
if (/^(0x)?[0-9A-Fa-f]+$/i.test(message)) {
const bufferMessage = Buffer.from(
message.startsWith("0x") ? message.slice(2) : message,
"hex"
);
return signer.signMessage(bufferMessage);
}
return signer.signMessage(message);
}
return provider.send(payload.method, payload.params || []);
};
return provider;
};
If it’s a Web3 wallet login, the signers we get with both methods are the same. For social login (Thirdweb embedded wallet), provider.getSigner()
returns an instance of JsonRpcSigner
, while signer returns an instance of EthersSigner
. As social login doesn't come with a wallet by default, we need the functions (eg:getAddress
) from EthersSigner
so it can be processed via EmbeddedWalletIframeCommunicator
.
That should be all we need to integrate Orbis with Thirdweb Connect. Have fun coding!