Skip to main content
Version: V1

Create a Connection (Follow / Unfollow an Address)

Here, we will demonstrate how you can help the users of your DApp follow each other by embedding a simple follow button to Scaffold-ETH.

Write a Get Connections Query

Before allowing users to follow another user, we first check whether such a connection already exists. You can retrieve the connection status between two addresses (whether A follows/is followed by B) using our "Connections" GraphQL query. For more details and all fields retrievable from CyberConnect Connections API, please read Connections.

  • Go to packages\react-app\src\queries\cyberconnect.js and add the following code in cyberconnect.js to implement a Get Follow Status query in cyberconnect.js:

    packages\react-app\src\queries\cyberconnect.js
    import { GraphQLClient, gql } from "graphql-request";

    // CyberConnect Protocol endpoint
    const CYBERCONNECT_ENDPOINT = "https://api.cybertino.io/connect/";

    // Initialize the GraphQL Client
    const client = new GraphQLClient(CYBERCONNECT_ENDPOINT);

    // You can add/remove fields in query
    export const GET_IDENTITY = gql`
    query ($address: String!, $first: Int) {
    identity(address: $address) {
    address
    domain
    avatar
    followerCount
    followingCount
    twitter {
    handle
    }
    followings(first: $first) {
    list {
    address
    domain
    }
    }
    followers(first: $first) {
    list {
    address
    domain
    }
    }
    }
    }
    `;

    export const GET_FOLLOWSTATUS = gql`
    query ($fromAddr: String!, $toAddrList: [String!]!) {
    connections(fromAddr: $fromAddr, toAddrList: $toAddrList) {
    followStatus {
    isFollowing
    isFollowed
    }
    }
    }
    `;
    // Get Address Profile Identity
    export async function getIdentity({ address }) {
    if (!address) return;

    const res = await client.request(GET_IDENTITY, {
    address: address,
    first: 5,
    });

    return res?.identity;
    }

    // Get Address Follow Status
    export async function getFollowStatus({ fromAddr, toAddr }) {
    if (!fromAddr) return;
    if (!toAddr) return;

    const res = await client.request(GET_FOLLOWSTATUS, {
    fromAddr: fromAddr,
    toAddrList: [toAddr],
    });

    return res?.connections[0]?.followStatus?.isFollowing;
    }

Create a Follow Button

To create a follow button component, go to directory packages/react-app/src/components/ and create CyberConnectFollowBtn.jsx.

  • Add the following code to CyberConnectFollowBtn.jsx:

    packages/react-app/src/components/CyberConnectFollowBtn.jsx
    import CyberConnect, { Env, Blockchain } from "@cyberlab/cyberconnect";

    function CyberConnectFollowButton({
    isFollowing,
    targetAddress,
    injectedProvider,
    onSuccess,
    }) {
    const cyberConnect = new CyberConnect({
    namespace: "CyberConnect-Scaffold-Eth",
    env: Env.PRODUCTION,
    chain: Blockchain.ETH,
    provider: injectedProvider,
    });

    const handleOnClick = async () => {
    if (!injectedProvider) {
    alert("Please connect your wallet first!");
    return;
    }

    try {
    if (isFollowing) {
    await cyberConnect.disconnect(targetAddress);
    alert(`Success: you've unfollowed ${targetAddress}!`);
    if (onSuccess) {
    onSuccess();
    }
    } else {
    await cyberConnect.connect(targetAddress);
    alert(`Success: you're following ${targetAddress}!`);
    if (onSuccess) {
    onSuccess();
    }
    }
    } catch (err) {
    console.error(err.message);
    }
    };

    return (
    <button
    onClick={handleOnClick}
    style={{
    background: "black",
    borderRadius: "20px",
    border: "1px solid white",
    color: "white",
    cursor: "pointer",
    fontSize: "14px",
    padding: "6px 20px",
    }}
    >
    {isFollowing ? "Unfollow" : "Follow"}
    </button>
    );
    }

    export default CyberConnectFollowButton;
  • Add the follow button to ExampleUI at packages\react-app\src\views\ExampleUI.jsx.

    packages\react-app\src\views\ExampleUI.jsx
    import { SyncOutlined } from "@ant-design/icons";
    import { utils } from "ethers";
    import { Button, Card, DatePicker, Divider, Input, Progress, Slider, Spin, Switch, Row, Col } from "antd";
    import React, { useEffect, useState } from "react";
    import { Address, Balance, Events } from "../components";
    import CyberConnectFollowButton from "../components/CyberConnectFollowBtn";

    import { getIdentity, getFollowStatus } from "../queries/cyberconnect";

    export default function ExampleUI({
    purpose,
    address,
    mainnetProvider,
    localProvider,
    yourLocalBalance,
    price,
    tx,
    readContracts,
    writeContracts,
    injectedProvider,
    }) {
    const [newPurpose, setNewPurpose] = useState("loading...");
    const demoAddr = "cyberlab.eth";
    const [identityInput, setIdentityInput] = useState(demoAddr);

    const [followInput, setFollowInput] = useState(demoAddr);
    const [followAddr, setFollowAddr] = useState("");

    const [identity, setIdentity] = useState(null);
    const [searchedIdentity, setSearchedIdentity] = useState(null);

    const [isFollowing, setIsFollowing] = useState(null);

    const fetchIdentity = async () => {
    if (!address) return;

    const res = await getIdentity({ address: address });
    if (res) {
    setIdentity(res);
    }
    };

    useEffect(() => {
    fetchIdentity();
    }, [address]);

    const searchIdentityHandler = async () => {
    if (!identityInput) return;
    const res = await getIdentity({ address: identityInput });
    if (res) {
    setSearchedIdentity(res);
    }
    };

    const searchFollowHandler = async () => {
    if (!followInput) return;
    setFollowAddr(followInput);
    const res = await getFollowStatus({ fromAddr: address, toAddr: followInput });
    setIsFollowing(res);
    };

    const formatAddress = address => {
    const len = address.length;
    return address.substr(0, 5) + "..." + address.substring(len - 4, len);
    };

    return (
    <div>
    {/*
    ⚙️ Here is an example UI that displays and sets the purpose in your smart contract:
    */}
    <div style={{ border: "1px solid #cccccc", padding: 16, width: 500, margin: "auto", marginTop: 64 }}>
    {/* CyberConnect Profile Section */}
    <div>
    <h2>CyberConnect Example UI:</h2>
    <Divider />
    <div style={{ textAlign: "left", marginLeft: "10px" }}>
    <h3>Your Profile Identity:</h3>
    {identity && (
    <div>
    <Row>
    <Col span={6}>Twitter handle:</Col>
    <Col span={18}>{identity.twitter?.handle ? identity.twitter.handle : "n/a"}</Col>
    </Row>
    <Row>
    <Col span={6}>Address:</Col>
    <Col span={18}>{identity.address}</Col>
    </Row>
    <Row>
    <Col span={6}>Domain:</Col>
    <Col span={18}>{identity.domain ? identity.domain : "n/a"}</Col>
    </Row>
    <Row>
    <Col span={6}>Followers:</Col>
    <Col span={18}>{identity.followerCount}</Col>
    </Row>
    <Row>
    <Col span={6}>Following:</Col>
    <Col span={18}>{identity.followingCount}</Col>
    </Row>
    </div>
    )}
    </div>
    </div>
    <Divider />
    <div style={{ textAlign: "left", marginLeft: "10px" }}>
    <h3>Search a Profile Identity:</h3>
    <Input
    placeholder="Enter the address/ens you want to search.."
    onChange={e => setIdentityInput(e.target.value)}
    value={identityInput}
    />
    <Button style={{ margin: "8px 0px" }} onClick={searchIdentityHandler}>
    Submit
    </Button>
    {searchedIdentity && (
    <div>
    <Row>
    <Col span={6}>Twitter handle:</Col>
    <Col span={18}>{searchedIdentity.twitter?.handle ? searchedIdentity.twitter.handle : "n/a"}</Col>
    </Row>
    <Row>
    <Col span={6}>Address:</Col>
    <Col span={18}>{searchedIdentity.address}</Col>
    </Row>
    <Row>
    <Col span={6}>Domain:</Col>
    <Col span={18}>{searchedIdentity.domain ? searchedIdentity.domain : "n/a"}</Col>
    </Row>
    <Row>
    <Col span={6}>Followers:</Col>
    <Col span={18}>{searchedIdentity.followerCount}</Col>
    </Row>
    <Row>
    <Col span={6}>Following:</Col>
    <Col span={18}>{searchedIdentity.followingCount}</Col>
    </Row>
    <Row span={18} justify={"center"}>
    <div style={{ padding: "20px" }}>
    <h4>Followers</h4>
    {searchedIdentity &&
    searchedIdentity.followers?.list.map(user => {
    return (
    <div key={user.address}>
    <div>{user.domain || formatAddress(user.address)}</div>
    </div>
    );
    })}
    </div>
    <div style={{ padding: "20px" }}>
    <h4>Followings</h4>
    {searchedIdentity &&
    searchedIdentity.followings?.list.map(user => {
    return (
    <div key={user.address}>
    <div>{user.domain || formatAddress(user.address)}</div>
    </div>
    );
    })}
    </div>
    </Row>
    </div>
    )}
    </div>
    <Divider />
    <div style={{ textAlign: "left", marginLeft: "10px" }}>
    <h3>Follow/Unfollow a Profile:</h3>
    <Input
    placeholder="Enter the address/ens you want to follow/unfollow.."
    onChange={e => setFollowInput(e.target.value)}
    value={followInput}
    />
    <Button style={{ margin: "8px 0px" }} onClick={searchFollowHandler}>
    Submit
    </Button>
    <div>
    {followAddr && (
    <CyberConnectFollowButton
    isFollowing={isFollowing}
    targetAddress={followAddr}
    injectedProvider={injectedProvider}
    onSuccess={() => {
    fetchIdentity();
    searchIdentityHandler();

    searchFollowHandler();
    }}
    />
    )}
    </div>
    </div>
    <Divider />

In order to make sure that your user use their logged-in account to follow another address, add injectedProvider={injectedProvider} to packages\react-app\src\App.jsx:

packages\react-app\src\App.jsx
<Route path="/exampleui">
<ExampleUI
address={address}
userSigner={userSigner}
mainnetProvider={mainnetProvider}
localProvider={localProvider}
yourLocalBalance={yourLocalBalance}
price={price}
tx={tx}
writeContracts={writeContracts}
readContracts={readContracts}
purpose={purpose}
injectedProvider={injectedProvider}
/>
</Route>

If successful, ExampleUI should display the following:

Play around with the connect functionality by inputting different addresses or ENS domains. Feel free to check out several popular users here. When you follow a new user, you’ll find your followings count and list to be updated as well. finished demo

That’s it! You’ve done a simple integration of CyberConnect to Scaffold-ETH. Check out the finished Starter Project here. We look forward to seeing more DApps built on top of the CyberConnect Protocol!

Designed by