Pending approval follows

📘

full code repo https://github.com/lens-protocol/lens-api-examples

🚧

This request is protected by authentication

hint: this means it requires an x-access-token header put in the request with your authentication token.

To get around the issues with people being able to spam other people will a follower NFT we have brought in approval follows. Look at this situation:

  • @josh follows @dodgy.profile
  • @josh gets a follower NFT for @dodgy.profile
  • @josh sends the follower NFT to @coolshoes wallet owner
  • @coolshoes wallet owner now looks like they follow @dodgy.profile

You can see from the above how this could be a huge issue and with spam already a major thing on cheap transaction chains we do see this being a problem from day 1.

To solve this we have a contract call called toggleFollow allowing you to move these from pending approvals to approved follows.

  • @josh follows @dodgy.profile
  • @josh gets a follower NFT for @dodgy.profile
  • @josh sends the follower NFT to @coolshoes
  • The server removes that @josh follows @dodgy.profile from the indexer
  • The server moves @coolshoes follows @dodgy.profile in an approval list
  • This now means @coolshoes needs to approve that they approve to follow this profile

This query returns all the pending approval follow NFT you have been sent and a way to show the user these so they can approve them.

API Design

query Followers {
  pendingApprovalFollows(request: { 
              limit: 10
             }) {
    items {
      id
      name
      bio
      attributes {
        displayType
        traitType
        key
        value
      }
      followNftAddress
      metadata
      isDefault
      handle
      picture {
        ... on NftImage {
          contractAddress
          tokenId
          uri
          verified
        }
        ... on MediaSet {
          original {
            mimeType
            height
            width
            url
          }
          small {
            url
            width
            height
            mimeType
          }
          medium {
            url
            width
            height
            mimeType
          }
        }
      }
      coverPicture {
        ... on NftImage {
          contractAddress
          tokenId
          uri
          verified
        }
        ... on MediaSet {
          original {
            width
            url
            height
            mimeType
          }
          small {
            height
            width
            url
            mimeType
          }
          medium {
            url
            width
            height
            mimeType
          }
        }
      }
      ownedBy
      dispatcher {
        address
        canUseRelay
      }
      stats {
        totalFollowers
        totalFollowing
        totalPosts
        totalComments
        totalMirrors
        totalPublications
        totalCollects
      }
      followModule {
        ... on FeeFollowModuleSettings {
          type
          amount {
            asset {
              name
              symbol
              decimals
              address
            }
            value
          }
          recipient
        }
        ... on ProfileFollowModuleSettings {
         type
        }
        ... on RevertFollowModuleSettings {
         type
        }
      }
    }
    pageInfo {
      prev
      next
      totalCount
    }
  }
}
{
  "data": {
    "pendingApprovalFollows": {
      "items": [
       {
          "id": "0x01",
          "name": "Josh",
          "bio": "Amazing docs",
          "location": "UK",
          "website": "https://mumbai.polygonscan.com/",
          "twitter": "devjoshstevens",
          "attributes": [
            {
              "displayType": null,
              "traitType": null,
              "key": "custom_field",
              "value": "yes this is custom"
            }
          ],
          "followNftAddress": null,
          "metadata": "ipfs://QmSfyMcnh1wnJHrAWCBjZHapTS859oNSsuDFiAPPdAHgHP",
          "isDefault": false,
          "picture": null,
          "handle": "josh.dev",
          "coverPicture": null,
          "ownedBy": "0xD020E01C0c90Ab005A01482d34B808874345FD82",
          "depatcher": {
            "address": "0xEEA0C1f5ab0159dba749Dc0BAee462E5e293daaF"
          },
          "stats": {
            "totalFollowers": 2,
            "totalFollowing": 1,
            "totalPosts": 1,
            "totalComments": 0,
            "totalMirrors": 0,
            "totalPublications": 1,
            "totalCollects": 6
          },
          "followModule": null
        }
      ],
      "pageInfo": {
        "prev": "{\"offset\":0}",
        "next": "{\"offset\":0}",
        "totalCount": 1
      }
    }
  }
}
type Query {
   pendingApprovalFollows(
    request: PendingApprovalFollowsRequest!
  ): PendingApproveFollowsResult!
}

You will see the paging result behavior repeated a lot in the API, this is to allow you to fetch a certain amount and then page it for the most optimal request speed. Every time something is wrapped in a paging result you will always get returned a pageInfo which holds the cursors for the previous and next alongside the total count which exists in the database. These cursors are just pointers for the server to get to the next result and do not need to be understood by your client or server. If you ever want to then page to the next result you can pass these previous and next cursor in the request cursor property.

Full code example

import { apolloClient } from './apollo-client';
// this is showing you how you use it with react for example
// if your using node or something else you can import using
// @apollo/client/core!
import { gql } from '@apollo/client'

const GET_PENDING_APPROVAL_FOLLOWS = `
  query($request: PendingApprovalFollowsRequest!) {
    pendingApprovalFollows(request: $request) { 
                items {
            id
            name
            bio
            attributes {
              displayType
              traitType
              key
              value
            }
            followNftAddress
            metadata
            isDefault
            handle
            picture {
              ... on NftImage {
                contractAddress
                tokenId
                uri
                verified
              }
              ... on MediaSet {
                original {
                  url
                  width
                  height
                  mimeType
                }
                medium {
                  url
                  width
                  height
                  mimeType
                }
                small {
                  url
                  width
                  height
                  mimeType
                }
              }
            }
            coverPicture {
              ... on NftImage {
                contractAddress
                tokenId
                uri
                verified
              }
              ... on MediaSet {
                original {
                  url
                  width
                  height
                  mimeType
                }
                small {
                  width
                  url
                  height
                  mimeType
                }
                medium {
                  url
                  width
                  height
                  mimeType
                }
              }
            }
            ownedBy
            dispatcher {
              address
              canUseRelay
            }
            stats {
              totalFollowers
              totalFollowing
              totalPosts
              totalComments
              totalMirrors
              totalPublications
              totalCollects
            }
            followModule {
              ... on FeeFollowModuleSettings {
                type
                amount {
                  asset {
                    name
                    symbol
                    decimals
                    address
                  }
                  value
                }
                recipient
              }
              ... on ProfileFollowModuleSettings {
               type
              }
              ... on RevertFollowModuleSettings {
               type
              }
            }
          }
       pageInfo {
          prev
          next
          totalCount
       }
        }
  }
`

export const pendingApprovalFollows = () => {
   return apolloClient.query({
    query: gql(GET_PENDING_APPROVAL_FOLLOWS),
    variables: {
      request: {
        limit: 10
      },
    },
  })
}
// this is showing you how you use it with react for example
// if your using node or something else you can import using
// @apollo/client/core!
import { ApolloClient, InMemoryCache, HttpLink, ApolloLink } from '@apollo/client'

const httpLink = new HttpLink({ uri: 'https://api-mumbai.lens.dev/' });

// example how you can pass in the x-access-token into requests using `ApolloLink`
const authLink = new ApolloLink((operation, forward) => {
  // Retrieve the authorization token from local storage.
  // if your using node etc you have to handle your auth different
  const token = localStorage.getItem('auth_token');

  // Use the setContext method to set the HTTP headers.
  operation.setContext({
    headers: {
      'x-access-token': token ? `Bearer ${token}` : ''
    }
  });

  // Call the next link in the middleware chain.
  return forward(operation);
});

export const apolloClient = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
})

Did this page help you?