# OpenRank

OpenRank is a decentralized ranking and reputation protocol. It enables a verifiable reputation compute layer for the open web that unlocks a broad range of useful applications, including those that resist cryptographic or game-theoretic mechanisms of trust. Using graph compute algorithms like EigenTrust, it offers resilience from sybil contexts, provides scalable and context-specific compute, and enables permissionless access to compute and reputation data for any developer.

Using OpenRank, developers can create ranking, recommendation and sybil-resistance systems for their applications and protocols. Developers can also power search and discovery for marketplaces and consumer applications.


# Ranking and Reputation

The rapid growth of onchain users and transactions has come with an increasing amount of fraud, rug pulls and spam. To continue attracting and retaining onchain users and high quality developers, there's an urgent need for trust and reputation solutions. It's a pain for users to discover, use, fund, read or buy something on-chain without worrying about getting spammed or scammed. In web2, this ranking and reputation service layer was managed by centralized companies such as App Store, Airbnb, eBay and Twitter. But these companies ended up capturing all the value, while gatekeeping developers and users, and stifling innovation. We need an open and decentralized reputation protocol to solve this.

## **Verifiable Algorithms on Reputation Graphs**

OpenRank leverages reputation graphs for trust and coordination in the decentralized context. These can be constructed using on-chain or any peer-to-peer social graph data. OpenRank enables graph algorithms like EigenTrust, Hubs and Authorities, Collaborative Filtering to compute reputation and ranking using these reputation graphs.

Using OpenRank, consumer applications and marketplaces can integrate context-specific, native rankings and recommendations seamlessly.

Any user or developer using OpenRank is assured of verifiability of the rankings and reputation computation. This enables a critical trust layer for users who are interacting with various applications to search and discover things onchain, or even for protocols to reward contributors based on verifiable ranking and reputation data.

## **Open and Composable Reputation**

OpenRank lets developers and protocols compose reputation from one use case context to another, which is nearly impossible in most web2 applications. This helps solve the inherent cold start or bootstrapping problem for new applications, networks and communities.

With web3's open and composable data layer, developers can leverage any data sets that suit their application context without having to worry about the cost or verifiability of computing on the data. Rankings and reputation computed using OpenRank are available for any smart contract, protocol or developer without having to run, manage or redo the compute.

In the longer run, anyone could publish their algorithms to OpenRank and get rewarded if their algorithms are utilized by application developers. A shared reputation infrastructure for accessing compute and existing rankings creates a flywheel for developer network effects.

<figure><img src="/files/ewy8WFwKZlZNazMwzzea" alt=""><figcaption></figcaption></figure>


# Use Cases

## Social Networks

Open social graphs need a ranking and algorithm layer to power search, discovery and recommendations without relying on centralized moderation and censorship, and enable safe and personalized user experiences. Web3 social graph protocols natively create peer-to-peer data based on engagement actions (likes, follows, comments, recasts, mints, tips, etc.). This open data can be used to create different reputation graphs which can be computed using graph algorithms like EigenTrust. The compute results in a ranking or score for all users in the network. A developer can decide the heuristics for the reputation graph, and the algorithm parameters and weights, making it easily verifiable.

An Open Ranking layer for social networks:

* Provide ranking and recommendation systems, enabling personalized and curated community experiences instead of a single global feed or ranking.
* Enables clients and developers to choose their algorithms and give users freedom to explore content and feeds of their choice.
* Helps detect and reduce spam and sybil clusters faster and cheaper through community curation of rankings.

*Read More about our implementation with Lens and Farcaster in the integrations section*

***

## Marketplaces

Permissionless and open marketplaces require a verifiable reputation layer to aid users in making informed decisions before interacting with goods, software, applications, or sellers. Marketplaces experience huge pain points around fraud, scams and rug pulls. There is no easy and resilient way to attest or aggregate the reputation of a creator, NFT collection or in general a smart contract. For users, these marketplaces become the de-facto venues for search and discovery. The time and cost of finding useful and safe things on-chain is high and borne by the users.

A verifiable reputation system for marketplaces can help capture contextual peer-to-peer attestations or transactions to create a community sentiment or wisdom that powers reputation of creators/sellers/developers and make it easy and safe for users to transact on the marketplace.

An Open Ranking layer for marketplaces :

* Enables transparency in how ratings, rankings, and recommendations are done on a marketplace and bring personalized search and discovery for users.
* Helps bring down the cost and efficiency of trust and safety through community wisdom versus centralized teams that end up costing significantly higher.
* Opens up the innovation surface for ranking systems for trust and fraud heuristics to the community instead of relying on a centralized authority's opinion.

Consumer and Developer marketplaces such as NFT platforms, App Ranking portals, Gitcoin, Uniswap Hooks, Metamask Snaps, Farcaster Frames, Lens Open Actions can utilize rankings and ratings for their specific contexts.

*Read More about our implementation with MetaMask Snaps Permissionless Distribution Experiment in the integrations section*

***

## **Consumer Apps and Wallets**

The rapid growth of on-chain users, transactions, bots and agents has highlighted the need for ranking and reputation systems for consumer apps. Wallets, block explorers, onchain communities can power better user experiences for search and discovery.

An Open Ranking layer for apps:

* Enables personalized and contextually relevant feeds or recommendations of most popular apps/tokens/NFTs in a users own onchain graph.
* Drives composability of reputation graphs across different applications.
* Brings social discovery as the key construct for users instead of a general or popular thing to do onchain.

*Read More about our implementation of onchain feed prototype in the integration section*

***

## Governance and Public Goods Funding&#x20;

Reputation aims to solve several key questions in governance: who to trust with what decisions, who gets how many votes on what decisions, and who gets how much resource allocation based on contribution.

An Open Ranking layer for communities:

* Enables collectives to leverage reputation to better drive governance and funding decisions, capturing peer-to-peer trust signals
* Makes possible the discovery of project impact based on onchain engagement data that is anchored by trusted members of the community
* Facilitates building various reputation graphs that signal domain expertise of community members in various contexts, resulting in reputation-based vote weighting


# The Reputation Stack

<figure><img src="/files/lFdKJX7OhuE5r5uvWODG" alt=""><figcaption></figcaption></figure>

## The Ranking and Reputation stack

* **Data Layer:** This enables any developer or application to bring their own data sets for compute. This involves two main operations - *Data sourcing and Pre-processing.* Any data source such as onchain attestations, blockchain transactions, open data repositories can participate for sourcing the data.  In addition, data analysis and data science communities can bring their own heuristics to form input data sets or reputation graphs as an input for the ranking and reputation compute. The output of the data layer is an `[i,j,v]` matrix, which is a context-specific reputation graph that will be computed by OpenRank protocol. Read an extensive overview of this in the next page.&#x20;
* **OpenRank Protocol** - A decentralized compute network to ensure verifiable, scalable and permissionless ranking and reputation compute. This layer computes the desirable algorithm (EigenTrust/Hubs and Authorities/Graph neural networks) on the reputation graphs committed by the data layer or clients and provides the converged ranking results (output). The compute nodes provide guarantees of computation and leverage a data availability layer for storing the input and output for any compute operation.
* **Apps and Clients using Rankings** - Developers, Protocols and Data scientists can utilize the verifiable rankings data to build desirable reputation, filtration, airdrop, search and discovery, feeds or gating strategies and applications. Rankings of one context can serve as input data for compute for another use case.


# Data

Anyone can permissionlessly leverage OpenRank Protocol to compute on various app-specific reputation graphs. There are two main operations involved:

**Data Sourcing:** Any data indexers or open data sets can bring their data sets for pre-prcoessing stage.

**Data Pre-Processing:** Using the source data, developers and data scientists can perform desired transformations to create app-specific transactions, credentials, attestations schemas or social graph data to generate reputation scores and rankings. The transformed data is a reputation graph, an input to OpenRank.&#x20;

## **Data Sets for OpenRank**

OpenRank computes on reputation graph data which can be constructed from peer-to-peer trust signals. A context-specific p2p reputation graph expresses this statement - *"**who** trusts **whom** by **how much**, for **what"*****.**

* **Who**: the one that extends the trust; the **truster**
* **Whom**: the one that receives the trust; the **trustee**
* **How much**: the **level of trust** from the truster to the trustee
* **What**: the **type of trust** that the truster extends; the role or context

## **Implicit and Explicit Reputation graphs**

Peer-to-peer trust signals can be captured in several ways.

**Implicit** - we can take existing data and derive reputation graphs from it. This is very useful in bootstrapping ranking and reputation using existing large scale data sets.

A good candidate for implicit signals is data from social graphs like Farcaster or Lens. This data can be used to compute rankings and recommendations for social networks and apps. An example reputation graph can be a linear combination of engagement actions such as comments, tips, recasts, follows between X and Y.

Another example of implicit data is onchain transaction data: If X sends tokens to Y, X may be assumed to trust Y, and the value of the asset determines the level of trust.

Implicit trust can be derived from any relevant verifiable data set. However, this requires implied assumptions around peer-to-peer trust heuristics. This may not work for certain use cases where the threshold for trust signals is high or must meet objective and explicit expression.

**Explicit** - we can use attestations and credentials as explicit p2p signals. The schemas or scope of these assertions help form a reputation graph. For example, the MetaMask Snaps Permissionless Distribution (SPD) platform gives users an option to express their trust opinions about other users for software security or software developer skills.

A web2 analogy for this is a 5-star rating system on marketplaces where users leave reviews for a counterparty. These reviews enable a ranking and reputation compute, which can aid in trust and safety, search and discovery.\
\
Once a developer decides where to source the data for ranking and reputation compute, we need to pre-process the data for computation. This usually requires applying transformation and linear combination operations on the source data. The output of these operations gives us the desired reputation graph for the desired use case.

For instance, lets consider an onchain graph constructed by using peer-to-peer token transfers between any two EOAs. To create a quality input data set for OpenRank computation, we must create a graph of only EOAs as nodes (vertices). The directed edges should capture value of token transfers between any two peers. Pre-processing operations involve removing contract events, filtering out non-desirable EOAs (labelled as spam/scam/CEX) from the graph.&#x20;

Another example of pre-processing data is using onchain attestations of a particular schema. The first step is to chose the specific schema which captures the peer-to-peer trust heuristic. For example X attesting that Y is a good software developer. Once we have all the attestations related to this schema, we can combine any other useful heuristics or schemas and do a linear combination of these to formulate an \[i,j,v] matrix.

## **Data Verifiability and Provenance**

### **Sourcing Data**

OpenRank computes on openly available data sets such as onchain transactions, attestations or open social graphs. There are at least two ways for developers to use open or public data sets - *trustless and trusted*.

In the *trustless* approach, the correctness and veracity of data can be proved using the system that hosts the data. For example, onchain transactions are verified by means of inclusion in a block considered part of the canonical chain.

In the *trusted* approach, the correctness and veracity of data is not directly proven; instead, the dataset is published by a trusted party, who signs the dataset. The consumer of the dataset establishes trust relationship with the publisher of the data.

The right approach often depends on the use case. For example, high-stakes use cases (such as p2p lending) require trust-minimized setup, where trustless source data is essential. Low-risk use cases such as social graph data for feeds and rankings can be sourced via reliable 3rd party data providers that developers and apps already leverage, as long as the data set is open and verifiable.

## **Pre-processing Data**

After identifying the data source, there may be several pre-processing operations before getting to the desired reputation graph for OpenRank compute. For instance, let's consider a profile ranking system on Farcaster powered by OpenRank. The first step is to get the entire social graph data from a node (Farcaster hubble). Next, we transform the data into different peer-to-peer actions, such as X likes Y's casts, X recasts Y, or X mentions Y. Next, we do a linear combination of these user actions to form a reputation graph - which is an \[i,j] matrix capturing peer-to-peer engagement on Farcaster.

Each of these pre-processing steps operate on one set of data (originating in source Farcaster graph), and produces another set of data. The output from one step is used as the input in a subsequent step, with the final data set becoming the input for OpenRank compute. A simple way to verify that the reputation graph data has not been tampered with during the pre-processing phase is for anyone to run the same transformation and linear combination operations on the public dataset and comparing the outputs. The provenance record of the input data for compute is signed by a data provider when they request for compute from OpenRank.

In the future, a trustless setup for data pre-processing will be made possible by submitting a proof of transformation along with the output dataset as a part of the provenance record.

With a data verifiability system, the end-user who consumes OpenRank compute can now check the data provenance of the input data itself. It first backtracks through the pre-processing graph towards all terminal input datasets used by the graph, then for each interim dataset it encounters, it checks the signature or proof submitted by the data provider.

To summarize, while submitting the reputation graph to the compute node, a data provider is responsible for:

* Verification of all input data it has used. This may be signature verification or correctness proof verification.
* Correct execution of the core logic inside the pre-processing stage.

## **Incremental Processing**

In a steady state, most datasets will continue to get incremental updates. A data node may maintain a pipeline to incorporate incremental updates.

Using such update or maintenance actions, a data provider will produce a new version of output dataset (reputation graph) for the next epoch of compute. This fresh input data set for the compute will be marginally different from the previous version. The data provider may opt to publish the output in the form of *deltas (patches)*, which anyone who has the previous version of the data can apply to mutate the previous version into the subsequent version, instead of publishing a full snapshot of the new version of data.

Even in this case, the data provider should still periodically emit *full snapshots*. Using a base snapshot and all the deltas published after the snapshot, anyone can reconstruct the up-to-date version of the data. Multiple checkpointing strategies exist for these full/diff data publication methods. OpenRank does not mandate any given strategy. Instead, data providers may clarify, given the desired point in time, how to recreate the dataset as of that point in time by combining a base snapshot with a series of deltas, as well as how to obtain such snapshots and deltas.

## **Current Integrations and Roadmap**

For the current integrations (see details in this section), Karma3 Labs is sourcing and pre-processing data with the help of 3rd party data providers.

OpenRank will offer an opportunity for data infrastructure providers to participate in the protocol and start offering indexed onchain or open public datasets for ranking and reputation use cases. Developers who require ranking and reputation compute will simply rely on data and compute infrastructure powered OpenRank protocol.

In the future, a custom VM wil be enabled to handle data verification, provenance and cheap transformation operations for pre-processing large data sets ahead of the core compute.


# OpenRank Protocol

The key objective of the OpenRank protocol is to accept a reputation graph dataset that contains a list of peers and their pairwise trust, and produce a list of scores for all the peers in the dataset. The properties ensured by OpenRank are:

* Anyone can post transactions for computing ranking, reputation or recommendation
* The user transactions containing the reputation graph are available on a DA network
* The computed scores and rankings are posted to a DA network for permissionless usage
* The computed scores and ranking are verifiable

A detailed explanation of the protocol is covered in the litepaper.

## Ranking and Reputation Compute

The compute nodes receive application or context-specific reputation graphs from the data providers and run a graph algorithm computation until we get a converged eigenvector. This eigenvector, along with input data and its blueprint is committed to the data availability layer. Any developer, smart contract or protocol can trustlessly access the resulting scores or ranking and use it in their own use case.

Developers can choose the type of algorithm they want to use. Additionally, they can run any number of different ranking compute jobs by changing various model parameters in the computation - set of seed peers, confidence level of seed peers, weights and  biases, global vs. personalized ranking compute. For a detailed understanding of all the parameters in EigenTrust and other algos that can be expressed as GNNs, refer to the litepaper.

After the Compute Node has finished running a specific job, they are responsible for making a commitment of resulting values, since these values will need to be verified by other network participants.&#x20;

## **Live Use Cases powered by OpenRank Compute**

The live use cases powered by OpenRank compute include:

1. EigenTrust on Farcaster and Lens graph data to generate global and personalized rankings and recommendations for users, frames, channels and feeds.
2. Hubs and Authorities on NFT transaction data on Ethereum to generate a NFT ranking system that is based on transitive purchase transactions, which helps in weeding out reputable NFTs from spam/scam.
3. EigenTrust on EVM transaction data such as p2p token transfers to power a personalized onchain ranking and recommendation for any EOA.
4. EigenTrust on peer-to-peer token tip transactions to power rankings for Airdrops.
5. Matrix Factorization on NFT ownership and EOA-to-contract interaction transactions to power a personalized recommendation of reputable onchain artifacts and power a trending/valuable users and contracts lists for any chain.
6. EigenTrust on Metamask Snaps Directory experimental attestation data to generate a community sentiment for finding safe and trustworthy snaps based on peer-to-peer attestations from reputable software security experts and auditors.


# Apps and Clients

Once the OpenRank compute nodes have committed the compute results, they can be combined with other scores or any other data to solve a particular use case or application's need.

For example, for a permissionless marketplace like Metamask Snaps, once EigenTrust compute produces a ranking for security experts and developers, their scores can be combined with what they say about a particular snap. This post-processing step can be defined and modified by the community to create a community sentiment score for a Snap. In addition, the community can also use other OpenRank developer or security expert scores from another use case in calculating a cumulative Snap score. You can read more here.

This shared and composable reputation layer enables scope for permissionless innovation in rankings, algorithms for any type of app or marketplace. OpenRank protocol remains un-opinionated about how the scores should be used, or who's score should be used. This is left for the community to decide. OpenRank enables this infrastructure for anyone to publish rankings and reputation scores for a use case, enabling wider choice for apps and users instead of trusting a single party.


# Integrations

This section gives an overview of live integrations powered by OpenRank.&#x20;

For each of the applications and use cases below, we have generated context-specific reputation graphs and computed various reputation and ranking algorithms like EigenTrust, Collaborative Filtering, Hubs and Authorities.&#x20;

1. [Farcaster](/integrations/farcaster) - Ranking APIs to build better profile, frames, cast rankings and recommendations based on Farcaster social graph.
2. [Lens](/integrations/lens-protocol) - Ranking APIs  to build better profile rankings and content feeds based on Lens social graph.
3. [Metamask Snaps Permissionless Distribution](/integrations/metamask-spd) - Prototype of a reputation computer based on positive/negative attestations to detect a community sentiment around the safety of a Snap. This can enable permissionless distibuion of Snaps within Metamask. &#x20;
4. [Onchain Graphs and Feeds](/integrations/onchain-graphs-and-feeds) - Personalized Ranking APIs to find your onchain graph - your extended onchain network of friends/users based on p2p token transfers, NFT ownership and contract interaction.

You can read [Upcoming integrations](/integrations/upcoming-integrations) to learn more about how OpenRank is currently in development across other use cases.


# Farcaster

Using Farcaster social graph data propagated by [Farcaster's Hubble](https://hub.docker.com/r/farcasterxyz/hubble), and replicated locally by [Farcaster's Replicator](https://hub.docker.com/r/farcasterxyz/replicator), we have launched a set of **OpenRank APIs** that can help developers building applications, clients, frames or any consumer experience to **filter out spam** and leverage **personalized ranking and recommendations.**&#x20;

**Using our APIs**, developers can leverage and customize the algorithms to power multitude of use cases, including but not limited to **identifying high quality users** vs. sybil users, **create ranking and recommendation** engine for profiles, frames and casts, generate **better feeds**, **airdrops to valuable users** and other social and on-chain experiences like [onchain feed](https://onchain.k3l.io).

Our APIs are implemented using OpenRank, an open-source and verifiable reputation computation protocol.

{% hint style="info" %}
Global ranking is updated every 2 hours \
Personalized Graph is served on-demand
{% endhint %}

{% hint style="info" %}
Try out the APIs here! — <https://graph.cast.k3l.io/docs#/>
{% endhint %}

## Examples of using our APIs:

1. To reward high quality users in the form of airdrop or power badges, you can use our Global Profile Ranking APIs along or Channel Profile Ranking APIs to make sure the reward is given to relevant and high quality users.
2. If you are building a client, you can use our Feed APIs to power a trending for you feed and use the Channel Feed APIs to develop a channel specific trending feed.
3. To build a frames specific client you can use our Top Frames API to have the users of you app only displayed relevant and high quality frames.
4. If you want to help with discovery in your client, you can use our Personalized Network APIs, (Extended network) to display relevant users as recommended profiles to your user.
5. You can filter spam in your application by using or Global Ranking APIs.


# Openrank Scores Onchain

Farcaster users OpenRank scores are now available on **base**. You can read more about how these scores are computed [here](https://docs.openrank.com/integrations/farcaster/ranking-strategies-on-farcaster). For now, the scores are updated weekly.&#x20;

## Contracts

#### Proxy

| Chain | Address                                                                                                                                            | Deploy Transaction                                                                                                                                                          |
| ----- | -------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Base  | 0xaC1EBa9e86740e38F0aCbE016d32b3B015206cd8 ([BaseScan](https://basescan.org/address/0xaC1EBa9e86740e38F0aCbE016d32b3B015206cd8#readProxyContract)) | 0xcde84be21e1b644885c184bd87ae7c4ec978ed497571e64b31d3b26715ff9244 ([BaseScan](https://basescan.org/tx/0xcde84be21e1b644885c184bd87ae7c4ec978ed497571e64b31d3b26715ff9244)) |

> We recommend using this upgradeable proxy contract to read the scores.

#### Implementation

| Chain | Address                                                                                                                               | Deploy Transaction                                                                                                                                                          |
| ----- | ------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Base  | 0x6f53EEC4C84935FFAE187019C0f55143AaD34e34 ([BaseScan](https://basescan.org/address/0x6f53eec4c84935ffae187019c0f55143aad34e34#code)) | 0x8e6acebb69feeaac28b4354afd0e64be3f721ca8969cc076e5760f961c00eb1f ([BaseScan](https://basescan.org/tx/0x8e6acebb69feeaac28b4354afd0e64be3f721ca8969cc076e5760f961c00eb1f)) |

The implementation contract cannot be queried directly, because all the scores are in the proxy contract under the Transparent Proxy pattern.

### Interface

```jsx
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.10;

interface IFarcasterOpenRank {
    /// @notice One leaderboard entry.
    struct User {
        /// @notice Farcaster ID of the user
        uint256 fid; // Farcaster ID of the user
        /// @notice OpenRank score of the user.
        /// The value is scaled so that score range [0.0, 1.0) maps to
        /// [0, 2**256), e.g. 0x8000...0000 == 0.5, 0x4000...0000 == 0.25, etc.
        /// 1.0 is an exception: it maps not to 2**256 but to 2**256-1
        /// because 2**256 is out of uint256 range.
        /// Instead, type(uint256).max (2**256-1) unambiguously identifies 1.0.
        /// 1-2**-256, which is the score value that would otherwise map to
        /// the same type(uint256).max, cannot be represented,
        /// which is okay because it cannot be represented in IEEE 754 either.
        uint256 score; // OpenRank score of the user
    }

    // Events

    /// @notice Emitted when a leaderboard entry has been set (added).
    /// @param fid Farcaster ID.
    /// @param rank Rank (position) in the leaderboard.
    /// @param score The score value.
    event ScoreSet(uint256 indexed fid, uint256 rank, uint256 score);

    /// @notice Emitted when a leaderboard entry has been deleted.
    /// @param fid Farcaster ID.
    /// @param rank Rank (position) in the leaderboard.
    /// @param score The score value.
    event ScoreDeleted(uint256 indexed fid, uint256 rank, uint256 score);

    // Functions
    /// @notice Gets the given FID's rank.
    /// @param fid Farcaster ID.
    /// @return rank Rank (position) in the leaderboard.
    function fidRank(uint256 fid) external view returns (uint256 rank);

    /// @notice Returns number of entries in the leaderboard.
    function leaderboardLength() external view returns (uint256);

    /// @notice Returns user (FID and score) at the given rank.
    /// @param rank The rank.  One-based, i.e. 1 is the top user.
    function getUserAtRank(uint256 rank) external view returns (User memory user);

    /// @notice Returns users (FIDs and scores) at the given ranks.
    /// @param ranks The ranks.  One-based, i.e. 1 is the top user.
    /// Nonexistent ranks result in empty user (fid = 0, score = 0).
    function getUsersAtRanks(uint256[] calldata ranks) external view returns (User[] memory users);

    /// @notice Returns users (FIDs and scores) in the given rank range.
    /// @param start The first rank to return.  One-based, i.e. 1 is the top user.
    /// @param count The number of users to return.
    /// If start/count is too large, only those that are in the leaderboard are returned,
    /// e.g. on a 100-user leaderboard (ranks 1-100), start=91, count=20 (ranks 91-110) returns only 10 users
    /// (ranks 91-100), and start=101, count=10 (ranks 101-110) returns no users.
    function getUsersInRankRange(uint256 start, uint256 count) external view returns (User[] memory users);

    /// @notice Returns the rank and score of the given FID.
    /// @param fid Farcaster ID.
    /// @return rank (One-based) rank; 0 means unranked.
    /// @return score Score value; 0 if unranked.
    function getRankAndScoreForFID(uint256 fid) external view returns (uint256 rank, uint256 score);

    /// @notice Returns the ranks and scores of the given FIDs.
    /// @param fids Farcaster IDs.
    /// @return ranks (One-based) ranks, i.e. 1 is the top user.  0 means unranked.
    /// @return scores Scores; 0 if unranked.
    function getRanksAndScoresForFIDs(uint256[] calldata fids)
        external
        view
        returns (uint256[] memory ranks, uint256[] memory scores);

    /// @notice Returns the FID, rank, and score for the given verifier address.
    /// @param verifier Verifier address.
    /// @return fid Farcaster ID; 0 if no FID is associated with the given verifier address.
    /// @return rank (One-based) rank; 0 if unranked or no FID is associated with the given verifier address.
    /// @return score Score value; 0 if unranked or no FID is associated with the given verifier address.
    function getFIDRankAndScoreForVerifier(address verifier)
        external
        view
        returns (uint256 fid, uint256 rank, uint256 score);

    /// @notice Returns the FIDs, ranks, and scores for the given verifier addresses.
    /// @param verifiers Verifier addresses.
    /// @return fids Farcaster IDs; 0 if no FID is associated with the given verifier address.
    /// @return ranks (One-based) ranks; 0 if unranked or no FID is associated with the given verifier address.
    /// @return scores Score values; 0 if unranked or no FID is associated with the given verifier address.
    function getFIDsRanksAndScoresForVerifiers(address[] calldata verifiers)
        external
        view
        returns (uint256[] memory fids, uint256[] memory ranks, uint256[] memory scores);
}

```

### Example Implementation

In the following snippet, `isTop100` returns whether the given address is one of the top 100.

```solidity
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;

import {IFarcasterOpenRank} from "src/IFarcasterOpenRank.sol";

contract FarcasterOpenRankExample {
    IFarcasterOpenRank private farcasterOpenRank;

    constructor(address _farcasterOpenRank) {
        farcasterOpenRank = IFarcasterOpenRank(_farcasterOpenRank);
    }

    function isTop100(address verifier) external view returns (bool) {
        (uint256 fid, uint256 rank, uint256 score) = farcasterOpenRank.getFIDRankAndScoreForVerifier(verifier);
        return rank >= 1 && rank <= 100;
    }
}
```


# Ranking Strategies on Farcaster

Profile reputation scoring to address trustworthiness in social networks

## **Defining Ranking Strategies**

We have implemented a set of strategies that can reveal high quality (highly ranked) profiles from the entire Farcaster network. These ranking strategies are based on [**following**](#strategy-following) and [**engagement**](#strategy-engagement) reputation graphs.

## **How are the Rankings performed?**

We use Farcaster social graph data and use a linear combination of peer-to-peer actions such as Follows, Recasts, Mentions, Comments to calculate a personalized reputation graph for each user. This helps in figuring your own network or friends and friends of friends. We then apply EigenTrust on these graphs to generate a ranking of users.&#x20;

Developers can change the algorithm weights and the rankings change real-time based on the updated parameters.

{% hint style="info" %}
To see how this is done in our codebase, checkout the repo on our `farcaster-graph` [GitHub repo](https://github.com/Karma3Labs/farcaster-graph), specifically on [these lines of code](https://github.com/Karma3Labs/farcaster-graph/blob/main/pipeline/globaltrust/compute.py#L108-L118)
{% endhint %}

### Seeding the Rankings

For Ranking the entire set of Farcaster profiles (Global Ranking), we use a seed peer set of profiles. For Personalized Ranking, the seed peer is the profile(s) itself.

The Global Profile Ranking compute is seeded with a few profiles chosen as a starting point to begin the computation of transitive trust among profiles. This seed peer selection is upto the developers. But for simplicity, we have currently chosen a curated list of profiles using the Dune [Farcaster Explorer dashboard](https://dune.com/ilemi/farcaster-explorer).  The seed peers are the influencers and VIPs retrieved via this query on [Dune](https://dune.com/queries):

```sql
SELECT 
    fid, fname, fid_active_tier, 
    CASE 
        WHEN fid_active_tier = 3 THEN 'influencer' ELSE 'vip'
    END AS fid_active_tier_name
FROM
    query_3418402 
WHERE
    fid_active_tier in (3,4) 
LIMIT 100
```

as a result, these are the sample of the seed users loaded into a `pretrust` table (as seen in the [db\_schema.sql](https://github.com/Karma3Labs/farcaster-graph/blob/main/db_schema.sql#L340-L347))

<figure><img src="/files/SU6W3YadFc6HX3emOI88" alt=""><figcaption><p>Sample Influencers and VIPs picked as seed trusted users for Profile Ranking using OpenRank</p></figcaption></figure>

### Strategy: **following**

This strategy emphasizes only on `following` as peer-to-peer trust heuristics, disregarding all other actions such as `likes`, `replies`, `recasts` and `mentions`. &#x20;

{% hint style="info" %}
***Weight Assignments:** Follows = 1*
{% endhint %}

You can see the weight assignments [here in the code](https://github.com/Karma3Labs/farcaster-graph/blob/main/pipeline/globaltrust/compute.py#L77-L83).

### Strategy: engagement

This strategy emphasizes on engagement actions as peer-to-peer trust heuristics, by combining `likes`, `replies`, `recasts`, `mentions` and `follows`.  The more engagement a profile receives on their casts, the more they inherit trustworthiness from the source profile, but weighted by the trustworthiness or reputation of the source as well.&#x20;

Therefore, if a set of sybil or spam clusters boost a particular profile (say Alice) and their casts, Alice's ranking will likely not increase because of the low rank of the profiles which are engaging with Alice.&#x20;

{% hint style="info" %}
***Weight Assignments:** Likes = 1, Replies = 6, Recasts = 3, Mentions = 12, Follows = 1*
{% endhint %}

You can see the weight assignments [here in the code](https://github.com/Karma3Labs/farcaster-graph/blob/main/pipeline/globaltrust/compute.py#L88-L93). Developers can also change the weights in the algorithms.&#x20;

Ranking Scope includes [Global](/integrations/farcaster/global-profile-ranking) and [Personalized](/integrations/farcaster/personalized-network), you can know more about them in their respective sections.

{% hint style="info" %}
Tryout the APIs here! — <https://graph.cast.k3l.io/docs#/>
{% endhint %}


# Global Profile Ranking

**Global Profile Rankings** of Farcaster users are basically categorized into the two strategies: [**following**](#strategy-following) and [**engagement**](#strategy-engagement).

<figure><img src="/files/tI38IHXqldzXxI4nXLdE" alt=""><figcaption></figcaption></figure>


# Top Profiles (based on Following)

Given a list of input FIDs, return a list of FIDs that are ranked based on the follows relationships in the Fracaster network and scored by Eigentrust algorithm.

`/scores/global/following/rankings`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/scores/global/following/rankings" method="get" expanded="false" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}


# Top Profiles (based on Engagement)

Get Top Engagement Profiles. This strategy emphasizes peer-to-peer likes, recasts, replies and mentions in increasing order of importance.

`/scores/global/engagement/rankings`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/scores/global/engagement/rankings" method="get" expanded="false" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}


# Profile Rank (based on Following)

Given a list of input handles, return a list of FIDs that are ranked based on the follows relationships in the Fracaster network and scored by Eigentrust algorithm.

`/scores/global/following/fids`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/scores/global/following/fids" method="post" expanded="false" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}

{% hint style="info" %}
Query via Handles:\
\&#xNAN;**/scores/global/following/handles** [**\[Try It!\]**](https://graph.cast.k3l.io/docs#/Global%20OpenRank%20Scores/get_following_rank_for_handles_scores_global_following_handles_post)
{% endhint %}


# Profile Rank (based on Engagement)

Given a list of input FIDs, return a list of FIDs that are ranked based on the engagement relationships in the Farcaster network and scored by Eigentrust algorithm.

`/scores/global/engagement/fids`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/scores/global/engagement/fids" method="post" expanded="false" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}

{% hint style="info" %}
Query via Handles:\
\&#xNAN;**/scores/global/engagement/handles** [**\[Try It!\]**](https://graph.cast.k3l.io/docs#/Global%20OpenRank%20Scores/get_engagement_rank_for_handles_scores_global_engagement_handles_post)
{% endhint %}


# Channel User Rankings

{% hint style="warning" %}
Channel rankings are live for top 500 popular channels based on this [dune query,](https://dune.com/queries/3422001/5745837) Here's a [sheet](https://docs.google.com/spreadsheets/d/1Hgv4hV0O0OgQ99Xx7NlHHF1DeRUgqTHPEtwEdm6CELs/edit#gid=0) with all the channels supported. We will be adding more support for all other channels soon.\
If you are a developer or a channel moderator looking to consume these rankings, [reach out](mailto:dharmi@karma3labs.com).
{% endhint %}

If you have gone through the [Ranking strategies for farcaster](/integrations/farcaster/ranking-strategies-on-farcaster), Channel user rankings are calculated in the same way as Global Ranking, with only two differences:

1. The Moderators are used as seed peers,
2. We only consider interactions between profiles who are active in that channel particularly and do not give importance to a user's global rank.

TLDR: We calculate a user ranking based on the seed peers interaction with other profiles. We use profile/cast engagement to generate the score - this includes users that have interacted with other users casts by way of either liking, replying, recasting or mentioning. Learn more, [here](/integrations/farcaster/ranking-strategies-on-farcaster).


# Top Profiles in Channel

Get a list of FIDs based on the engagement relationships in the given channel and ranked by Eigentrust algorithm.

{% openapi src="/files/bgVpG7BZSCa4pJmLJGDc" path="/channels/rankings/{channel}" method="get" %}
[channel-rankings-casts-openapi.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FgOCDthghwDUOJI1A23Cq%2Fchannel-rankings-casts-openapi.json?alt=media\&token=9b6bf39a-52c5-4237-98fd-1adad250a1d4)
{% endopenapi %}


# Profile Rank in Channel

Given a list of input FIDs, return a list of FIDs that are ranked based on the engagement relationships in the channel and ranked by Eigentrust algorithm.

{% openapi src="/files/bgVpG7BZSCa4pJmLJGDc" path="/channels/rankings/{channel}/fids" method="post" %}
[channel-rankings-casts-openapi.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FgOCDthghwDUOJI1A23Cq%2Fchannel-rankings-casts-openapi.json?alt=media\&token=9b6bf39a-52c5-4237-98fd-1adad250a1d4)
{% endopenapi %}

{% hint style="info" %}
Query via **handles**:\
\&#xNAN;**/channels/rankings/{channel}/handles** [**\[Try It!\]**](https://graph.cast.k3l.io/docs#/Channel%20OpenRank%20Scores/get_channel_rank_for_handles_channels_rankings__channel__handles_post)
{% endhint %}


# Personalized Network

**Personalized User Network + Rankings** for a profile or a curated set of `n` profiles are of two types: **Direct Network and Extended Network.**  The Direct Network is the first degree of separation from the profile that is being computed for.  The Extended Network showcases popular profiles based on your extended network (which may be your friends of friends or 2nd/3rd degree connections).

<figure><img src="/files/ci3oErBmPLL8Ye8bB3qI" alt=""><figcaption></figcaption></figure>


# Direct Network

1st degree follows or engagement, Users 1 hop away. Helps in surfacing content from your personal social graph

<figure><img src="/files/nmMyG4v3SvuyCtEPIHYb" alt=""><figcaption></figcaption></figure>


# Get Direct Following

Given a list of input FIDs, return a list of FIDs that only the input FIDs are directly following.

`/links/following/fids`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/links/following/fids" method="post" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}

{% hint style="info" %}
Query via **handles**:

**/links/following/handles** [**\[Try It!\]**](https://graph.cast.k3l.io/docs#/Direct%20Links/get_direct_following_for_handles_links_following_handles_post)
{% endhint %}


# Get Direct Engagement

Given a list of FIDs, return a list of FIDs that only the input FIDs have directly engaged with.

`/links/engagement/fids`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/links/engagement/fids" method="post" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}

{% hint style="info" %}
Query via **handles:**

**/links/engagement/handles** [**\[Try It!\]**](https://graph.cast.k3l.io/docs#/Direct%20Links/get_direct_engagement_for_handles_links_engagement_handles_post)
{% endhint %}


# Extended Network

Users in your extended network. Users ‘k’ hops away. Helps in Discover of popular content and users in your network

<figure><img src="/files/cXHtW0TYYUZTyGHHF3hx" alt=""><figcaption></figcaption></figure>


# Personalized Following

Given a list of FIDs, return a list of addresses trusted by the extended network.

`/scores/personalized/following/fids`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/scores/personalized/following/fids" method="post" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}

{% hint style="info" %}
Query via **handles**: \
\&#xNAN;**/scores/personalized/following/handles** [**\[Try It!\]**](https://graph.cast.k3l.io/docs#/Personalized%20OpenRank%20Scores/get_personalized_following_for_handles_scores_personalized_following_handles_post)

Query via **addresses**:\
\&#xNAN;**/scores/personalized/following/addresses** [**\[Try It!\]**](https://graph.cast.k3l.io/docs#/Personalized%20OpenRank%20Scores/get_personalized_following_for_addresses_scores_personalized_following_addresses_post)
{% endhint %}


# Personalized Engagement

Given a list of FIDs, return a list of addresses trusted by the extended network.

`/scores/personalized/engagement/fids`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/scores/personalized/engagement/fids" method="post" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}

{% hint style="info" %}
Query via **handles**: \
\&#xNAN;**/scores/personalized/engagement/handles** [**\[Try It!\]**](https://graph.cast.k3l.io/docs#/Personalized%20OpenRank%20Scores/get_personalized_engagement_for_handles_scores_personalized_engagement_handles_post)

Query via **addresses**:\
\&#xNAN;**/scores/personalized/engagement/addresses** [**\[Try It!\]**](https://graph.cast.k3l.io/docs#/Personalized%20OpenRank%20Scores/get_personalized_engagement_for_addresses_scores_personalized_engagement_addresses_post)
{% endhint %}


# Frames

OpenRank APIs for ranking Frames

Discover high quality Frames (casts with frames) based on the ranking of profiles that have casted or engaged with the Frame (cast). The ranking of a Frame depends on a weighted linear combination of the scores of all the profiles that have interacted with the frame (cast) and the parameters of this API are used to control the weights and combination.


# Top Frames

Get a list of frame urls that are used by highly ranked profiles.

Discover high quality Frames (casts with frames) based on the ranking of profiles that have casted or engaged with the Frame (cast). The ranking of a Frame depends on a weighted linear combination of the scores of all the profiles that have interacted with the frame (cast) and the parameters of this API are used to control the weights and combination.

Developers can choose their own preferred strategies to rank frames. For simplicity, we have provided a reasonable default parameter setting.

This is a GET request to <https://graph.cast.k3l.io/frames/global/rankings> with a few optional parameters which are described further below.

`/frames/global/rankings`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/frames/global/rankings" method="get" expanded="false" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}


# Personalized Recommended Frames

Given a list of FIDs, return a list of frame urls used by the extended trust network

Discover high quality Frames (casts with frames) based on your personalized social graph. This ranking follows an approach very similar to the Global Frame Rankings endpoint described in the previous section, but the difference is that we replace the global graph with a personalized graph specific to the given profile. In other words, Frames are assigned scores that are weighted linear combination of the interactions and trust scores of profiles within a given profile’s personalized (Extended network) trust graph.

`/frames/personalized/rankings/fids`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/frames/personalized/rankings/fids" method="post" expanded="false" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}

{% hint style="info" %}
Query via **handles**:\
\&#xNAN;**/frames/personalized/rankings/handles** [**\[Try It!\]**](https://graph.cast.k3l.io/docs#/Frames/get_personalized_frames_for_handles_frames_personalized_rankings_handles_post)
{% endhint %}


# Feeds

A set of APIs to power Personalized For You feeds or Channel Feeds.

{% hint style="info" %}
The **For You** feeds have a latency of around **1.3 seconds** and **channel feed** have a latency of **sub second.**
{% endhint %}

## Creating Feeds

Here is how our Feed endpoints generates relevant casts to populate the trending feed (for you/channel) for your application.

Here along with explaining some assumption that we have used to generate a very well curated feed we have also explained our all the ways the API will have configurations to customize the experience for the feed based on your (developers') own judgement.

### Step 1: Getting all the casts

As of the first step in creating a feed, we take in all casts ever created on the Farcaster network for the last 5 days.

To understand the full extent of the what kind of data we deal with - its around 600,000+ new casts as of April 2024. When fetching these casts we also take into consideration all the actions on each of those casts like - Likes, Recasts, Replies.

At the end of this step we get a list of all casts with all engagement data in the past 5 days.

### Step 2: Generating relevant Graphs to power feeds&#x20;

In **For You feeds**, we create a personalized graph and rank a users neighbors. For the current For You API endpoints we default it to k=1 i.e. only map the graph for first degree neighbors. But there is an option to configure it to upto 5.

{% hint style="info" %}
The wider the graph (higher k value) the higher is the latency.
{% endhint %}

In **Channel feeds**, we create a graph by ranking all the users in that channel based on their interactions. These graphs are generated based on engagement, learn more about it [here](/integrations/farcaster/personalized-network/extended-network/personalized-engagement). At the end of this step we have a ranked list of users with a score based on their incoming engagement in a channel.

### Step 3: Filtering all casts

We now have two lists, first includes all the casts and the second includes the ranked network graph. Next, we iterate through each of the casts from the first list and apply a score to it. This score is a multiplier assigned to each cast based on the networks interaction with the cast.

Example on how this step works for For You Feed:

Lets say Michelle who is a second degree neighbor to You, and has a personalized ranking score of 0.5. And there is a cast that Michelle interacted with (liked + recasted) then we give the cast a score of (0.5\*1)+(0.5\*5) giving the cast a score of 3 and we also square this so it becomes 9.&#x20;

Now if Adam who is also in your graph has also interacted with the same cast then it gets added to the score 9 on that cast that was generated by Michelle interaction. Hence giving the cast a better score and making it rank higher on the feed.

{% hint style="info" %}
We us *sumofsquares* i.e. square the scores and add it to amplify good casts, but this too can be controlled by changing it to *rootmeansquare* or *sum* based on the developers choice of amplification.
{% endhint %}

There are two main things to note here on this step

1. We remove casts that don't have any score applied to it, removing all casts the relevant network hasn't interacted with.
2. All casts that aren't parent casts are also removed. (i.e. we remove all replies from this) Making the feed only be populated by Parent casts.

> This was done based on the feedback we got from the community and can/should be configurable  by developers.

What we get at the end of this step is a score assigned to each cast based on the relevant networks interest in that cast.

### Step 4: Sorting and Time based filtering

Now that we have all the casts and corresponding scores attached to them we bucket the casts based on 1 hour time slots and in that bucket we sort them based on the score we generated in the previous step.

What this leaves you with is a list of casts that is first sorted based on recency and then sorted based on relevancy to provide your users with the best feed possible.

And this is how the feeds are generated.


# For You Feed

The For You Feed uses the graph that was generated based on the users' personal network specific to each user, Learn more [here](/integrations/farcaster/feeds).

{% hint style="info" %}
The For You feeds have **\~1.3 seconds latency**.&#x20;
{% endhint %}

{% hint style="info" %}
If you are a developer building on Farcaster, [reach out to us here](mailto:gloria@karam3labs.com)!
{% endhint %}


# For You

Get a list of casts that have been interacted with the most in a user's extended network.

`https://graph.cast.k3l.io/casts/personalized/popular/{fid}`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/casts/personalized/popular/{fid}" method="get" expanded="false" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}


# For You (by Authorship)

Get a list of casts that have been casted by the popular profiles in a user's extended network.

`https://graph.cast.k3l.io/casts/personalized/recent/{fid}`

This one as opposed to the previous one is used to find what your network is saying. The [previous end point](/integrations/farcaster/feeds/for-you-feed/for-you) is more for not just showing you casts for what your network is saying but also to show casts of what your network is interested/engaging in.

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/casts/personalized/recent/{fid}" method="get" expanded="false" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}


# Channel Feed

{% hint style="warning" %}
Channel Feeds are live for top 500 popular channels based on this [dune query,](https://dune.com/queries/3422001/5745837) Here's a [sheet](https://docs.google.com/spreadsheets/d/1Hgv4hV0O0OgQ99Xx7NlHHF1DeRUgqTHPEtwEdm6CELs/edit#gid=0) with all the channels supported.

We will soon be launching a more general purpose API to generate feed for any channel.
{% endhint %}

The Channel Feed uses the graph that was generated using the channel specific network, Learn more [here](/integrations/farcaster/feeds).

{% hint style="info" %}
The Channel feeds have a **sub second latency**.
{% endhint %}

{% hint style="info" %}
If you are a developer building on Farcaster, [reach out to us here](mailto:gloria@karam3labs.com)!
{% endhint %}


# Channel Trending Casts

Get a list of recent casts that are the most popular based on Eigentrust scores of fids in the channel.

{% openapi src="/files/bgVpG7BZSCa4pJmLJGDc" path="/channels/casts/popular/{channel}" method="get" %}
[channel-rankings-casts-openapi.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FgOCDthghwDUOJI1A23Cq%2Fchannel-rankings-casts-openapi.json?alt=media\&token=9b6bf39a-52c5-4237-98fd-1adad250a1d4)
{% endopenapi %}


# Metadata

Utility APIs to fetch Farcaster user FIDs, Handles and Addresses

Most compute APIs offer implementations to utilize any of the three (FIDs, Handles, and Addresses) for obtaining the desired responses. However, if you strongly prefer using only one method for fetching data, you can utilize these APIs to convert between FIDs, Handles and Addresses.


# Get FIDs for Addresses

Given a list of addresses, this API returns a list of FIDs.

`/metadata/fids`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/metadata/fids" method="post" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}


# Get Handles For Addresses

Given a list of addresses, this API returns a list of handles.

`/metadata/handles`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/metadata/handles" method="post" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}


# Get Addresses for FIDs

Given a list of FIDs, this API returns a list of addresses linked to each Address.

`/metadata/addresses/fids`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/metadata/addresses/fids" method="post" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}


# Get Addresses for Handles

Given a list of Farcaster handles, this API returns a list of addresses.

`/metadata/addresses/handles`

{% openapi src="/files/B0ETZjcXuU1nJxZFc8sH" path="/metadata/addresses/handles" method="post" %}
[openapi\_20240501.json](https://1110142702-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FEtcdATXDvC0WSePF1xED%2Fuploads%2FzfvZMaEw2L48Ze7p6tDv%2Fopenapi_20240501.json?alt=media\&token=eb5b7b89-d6c2-48b3-a250-58c67529f226)
{% endopenapi %}


# Ideas to Build using OpenRank APIs

Using OpenRank APIs for Profiles and Frames, developers can hack and build cool stuff leveraging Farcaster social graph.  A non-exhaustive wishlist below:

## Clients and Applications

1. Build relevant and high quality a) global channel feeds, b) personalized channel feeds using Profile Ranking APIs
2. Build 'For You' feeds on Farcaster based on your personalized social graph
3. Builld an onchain feed that shows what your network or a curated set of quality Farcaster profiles are doing onchain. (<https://onchain.k3l.io> as a reference)
4. Build a Trending List of Apps/NFTs/Tokens popular among a) top ranked Farcaster profiles, b) your personalized social graph on Farcaster
5. Build a spam feed using Profile Ranking APIs
6. Build a client for Frames showing a) Popular Frames among entire network, and b) 'For You' Frames based on personalized social graph
7. Airdrop Generator based on Farcaster Profile Rankings
8. Spam spotter in a channel feed
9. Build an app that shows two feeds: a) Recent Casts, b) Casts based on Profile Ranking APIs
10. Farcaster Discovery App that Recommends Profiles, Channels, Frames based on your or anyone's Farcaster social graph.&#x20;

## &#x20;**Frames**&#x20;

1. Frame to check Ranking/Reputation of your followers based on Global Profile Ranking - basically check profile ranking of my followers, to understand how many bots/sybils are following me
   * How many followers in 'Top 100 Rank', 'Bottom '10%', etc
   * Percentage of followers who are likely spam/bots
2. Frame to see Global Rank of a profile (Text based Frame which takes profile name as input)
3. Frame to show last on-chain action of a Top 100 Profile
4. Frame to see personalized graph of any user (text based frame)
5. Frame to show Top NFTs held by your Farcaster Network
   * Top Tokens held by your network
   * Most popular contracts by your network
6. Frame to see onchain network/neighbors related to your Farcaster linked EOA
   * See Top Tokens held by your onchain network
   * See Top NFTs held by your onchain network
   * See Top contracts by your onchain network

## **Ranking/Recommendation for Frames**

1. Filter our most spammy frames using Frames Ranking API. Alternatively, you can also sort frames that have been ‘used’, ‘recasted’, ‘liked’, ‘commented’ on by Highly ranked people
2. Personalized Recommendations of Frames based on interaction from your own social graph on Farcaster.&#x20;


# Neynar x OpenRank Guides (WIP)


# Build "For You" Feeds for your Client, using Neynar and OpenRank

Create your own For You Feed for your client.

Developers can utilize Personalized Graphs and For You Feed Endpoints to seamlessly integrate with Neynar and set up a feed promptly. The current For You feed utilizes personalized network APIs to identify relevant casts based on your network's interactions with them. Here's a simple guide demonstrating how you can utilize the For You Feed API alongside Neynar's API to create a Personalized feed for your clients.

{% hint style="info" %}
This guide expects you to have already have signed up for a Neynar account and have the basics set up ready to be able to consume Neynars' APIs. \
If you haven't, you can go through the [getting started](https://docs.neynar.com/docs/getting-started-with-neynar) guide here.
{% endhint %}

### Step 1:  Getting the FID

When generating a personalized For You feed, we require the FID (Feed ID) for the user. In this example, we're using a static number. However, this step entirely depends on the client developer's preference, whether they opt for authentication or any other method to obtain the FID.

```javascript
const USER_FID = 2025; // This could be static or also based on auth.
```

### Step 2: Retrieving Recommended Cast Hashes for the Feed&#x20;

In this step, we simply take the base URL and append various parameters to create the fetching URL for the feed. By the end of this step, we will have an array containing all the casts relevant to your user, based on their personalized graph. We will use that array to fetch data for casts from Neynar.

```javascript
const openRankBaseURL = 'https://graph.cast.k3l.io/casts/personalized/popular'
const recommendedCastHashesParams = 'agg=sumsquare&weights=L1C10R5Y1&k=1&offset=0&limit=25&graph_limit=100&lite=true'
const recommendedCastHashesUrl = `${openRankBaseURL}/${USER_FID}?${recommendedCastHashesParams}`;
const recommendedCastHashesResponses = await fetch(recommendedCastHashesUrl, {
  headers: {
    'Content-Type': 'application/json',
  },
});
const recommendedCastHashesArray = await recommendedCastHashesResponses.json().then(response => response.result);
console.log(recommendedCastHashesArray); // logs information about the cast hashes in ranked in order of recommendation
```

### Step 3: Using Neynar to retrieve casts:

In the previous step, we successfully retrieved all the cast hashes in the order of their recommendation. Now, in this step, we transform the response from the previous step and connect to the Neynar API to obtain the cast information for each of the hashes. Neynar offers a convenient API endpoint where you can pass in an array of cast hashes and receive details for each cast in the response.

```javascript
// Step 3: Integrating with Neynar to fetch the contents for these casts Hashes
// https://docs.neynar.com/reference/casts
const transformedCastHashesArray = recommendedCastHashesArray.map(element => element.cast_hash).join(',');
const neynarBaseURL = 'https://api.neynar.com/v2/farcaster/casts'
const neynarCastsURL = `${neynarBaseURL}?casts=${transformedCastHashesArray}`
const feedResponse = await fetch(neynarCastsURL, {
    headers: {
      'Content-Type': 'application/json',
      api_key: process.env.NEYNAR_API_KEY
    },
});
const feed = await feedResponse.json().then(response => response.result.casts);
console.log(feed) // logs the feed which is an array in order of all casts
```

### Enabling Pagination

Building a client means you need to enable infinite scrolling and to enable that we need pagination.

The URL already has two parameters - offset: no of casts to skip from the entire list & limit: no of casts in each fetch - which can be used to generate the paginated feed.

For most part the code remains the same, we have just enclosed it within a `paginatedFeed` aync function which has two parameters, first the `page` and second the `userFID` which we can pass when calling the function. The logic on how to update the page parameter sits outside the async function.

Other that that we have only added two more variables in Step 2 - `castsPerPage` which essentially is the limit parameter and the `offset` parameter which we generate based on the page number. We pass these two variable to the `recommendedCastHashParams` string to which controls the parameters of the url.

Finally you have a feed that is paginated!

```javascript
let page = 0;
const userFID = 2025;
const castsInPage = await paginatedFeed(page, userFID);
```

```javascript
async function paginatedFeed(page, userFID) {

    // Step 1: Getting The Users FID
    const USER_FID = userFID;


    // Step 2: Getting the ranked casts hashes for creating the Personalized For You Feed
    const castsPerPage = 25; // Making casts per page as a configurable option
    const offset = page * castsPerPage; // Adding offset based on page.

    const openRankBaseURL = 'https://graph.cast.k3l.io/casts/personalized/popular'
    const recommendedCastHashesParams = `agg=sumsquare&weights=L1C10R5Y1&k=1&offset=${offset}&limit=${castsPerPage}&graph_limit=100&lite=true`
    const recommendedCastHashesUrl = `${openRankBaseURL}/${USER_FID}?${recommendedCastHashesParams}`;
    const recommendedCastHashesResponse = await fetch(recommendedCastHashesUrl, {
    headers: {
        'Content-Type': 'application/json',
    },
    });
    const recommendedCastHashesArray = await recommendedCastHashesResponse.json().then(response => response.result);


    // Step 3: Integrating with Neynar to fetch the contents for these casts Hashes
    // https://docs.neynar.com/reference/casts
    const transformedCastHashesArray = recommendedCastHashesArray.map(element => element.cast_hash).join(',');
    const neynarBaseURL = 'https://api.neynar.com/v2/farcaster/casts'
    const neynarCastsURL = `${neynarBaseURL}?casts=${transformedCastHashesArray}`
    const feedResponse = await fetch(neynarCastsURL, {
        headers: {
        'Content-Type': 'application/json',
        api_key: process.env.NEYNAR_API_KEY
        },
    });
    return await feedResponse.json().then(response => response.result.casts);
}
```

### Configuring the feed

In the Step 2 above, If you notice in `recommendedCastHashesParams`, there are a few parameters we pass as defaults, however each of them can be configured to generate a different feed. Lets look at each of these parameters in detail.

```javascript
const recommendedCastHashesParams = `agg=sumsquare&weights=L1C10R5Y1&k=1&offset=${offset}&limit=${castsPerPage}&graph_limit=100&lite=true`
```

<table><thead><tr><th>Parameter</th><th width="246">Description</th><th width="140">Options</th><th>Default</th></tr></thead><tbody><tr><td><strong>agg</strong></td><td>Deciding which aggregation function to use to. Essentially decides how are the weights amplified.</td><td><strong>sumsquare</strong><br><strong>rms</strong><br><strong>sum</strong></td><td>sumsquare</td></tr><tr><td><strong>weights</strong></td><td>Linear combination of how each of the 4 actions (Like, Casts, Recast and Replies)</td><td>L{weight}C{weight}R{weight}Y{weight}</td><td>L1C10R5Y1<br>(i.e. Like has weight 1, Cast has weight 10, Recast has weight 10 and replies have weight 1)</td></tr><tr><td><strong>k</strong></td><td>How wide the your personalized network to spread when trying to use it to assign weights for the casts.</td><td>1 to 5</td><td>1</td></tr></tbody></table>

To understand this, we should first know why we need these weights. When generating feeds, it is actually a list of casts sorted or ranked based on the score associated with each cast. This score is calculated based on how the input FID (the user's personalized network) interacts with these casts.

A **simple example** of this works:

Let's there is a **cast C1** and **C2**. \
The **input FID is F1** and F1 has **neighbors F11, F12, F13 and F14** with personalized scores **S11, S12, S13 and S14** respectively. \
Some random profile **Fx** is the **author** of the cast **C1** and **neighbor F14** is the author of the **cast C2**.&#x20;

F11 has liked C1, replied to C1 and recasted C1. \
F12 has replied to C1 and recasted C1. \
F13 has also replied to C1 and liked C2. \
F14 has liked C2.

If the API is called with `agg=sumsquare, weights='L1C10R5Y1'`&#x20;

The score of **Cast C1** for **FID F1** will be:

```latex
[(S11 * L1 * TD(ts)) + (S11 * Y1 * TD(ts)) + (S11 * R5 * TD(ts))]^2 +
[(S12 * Y1 * TD(ts)) + (S12 * R5 * TD(ts))]^2 +
[(S13 * Y1 * TD(ts)) + (S13 * L1 * TD(ts))]^2 
```

The score of **Cast C2** for **FID F1** will be:

```
[(S14 * C10 * TD(ts)) + (S14 * L1 * TD(ts))]^2 +
[(S13 * L1 * TD(ts))]^2
```

Here \
L1 = weight of Like is **1**, \
Y1 = weight of reply **1**, \
R5 = weight of recast is **5**, \
C10 = weight of cast is **10**

Whats TD? \
We apply an hourly time decay value of \
`TD(action) = (1-(1/(365*24)) ^ ActionTimestamp`

Similarly, for an input FID, the score is calculated for each cast in the network. This score is then used to rank or sort the casts, which is how the feed is generated. \
By changing the aggregation parameter `agg`, we can control how the final score is calculated and adjusting the `weights` allows us to determine the importance given to each of the various actions.


# Build "User Search" using Neynar and OpenRanks' Global Ranking API

Surface better search results for your client.

In your client, whether it's a global user search or a search based on @mentions in a cast, both scenarios necessitate returning results that prioritize non-spam accounts at the top.

{% hint style="info" %}
Using the combination of these APIs will lead to a latency time of around 500ms \
OpenRank API - 300ms and Neynar APIs - 200ms
{% endhint %}

Search functionality will be one of the pivotal features of your client. In this guide, we'll utilize Neynar's API to initially retrieve all user results based on a search query and subsequently sort these results according to the global rank of each user.

{% hint style="info" %}
This guide expects you to have already have signed up for a Neynar account and have the basics set up ready to be able to consume Neynars' APIs. \
If you haven't, you can go through the [getting started](https://docs.neynar.com/docs/getting-started-with-neynar) guide here.
{% endhint %}

### Step 1: Getting the search query

This aspect is particularly tailored to your client's specifications, particularly in how it manages text inputs. For the purpose of this guide, we will utilize a static string as the search query and we also need the users FID, so we can pass it as the `viewer_fid` to Neynars API as a parameter in the next step, for relevant results.

```javascript
// Step 1: Getting the search Query
const searchQuery = "d"
const USER_FID = 2025
```

### Step 2: Using Neynar API to get a list of responses for the search query

In this step, we utilize Neynar's user search API endpoint to retrieve results for all users based on the username search query. The search query is set with a hardcoded `limit` of 10 results, which can be adjusted according to your specific requirements. Additionally, we enhance the quality of responses by passing the `viewer_fid` parameter as the User FID while using the Neynar API.

By the end of this step, you'll have an array of user objects that match the relevant search query.

```javascript
// Step 2: Using the Neynar API to get a list of responses for the search query
const neynarBaseURL = "https://api.neynar.com/v2/farcaster/user/search"
const urlSearchQueryParams = `q=${searchQuery}&viewer_fid=${USER_FID}&limit=10`
const neynarUsernameSearchURL = `${neynarBaseURL}?${urlSearchQueryParams}`
console.log(neynarUsernameSearchURL)
const neynarUsernameResponse = await fetch(neynarUsernameSearchURL, {
    headers: {
        'Content-Type': 'application/json',
        api_key: process.env.NEYNAR_API_KEY
      },
})
const usernameResponsesArray = await neynarUsernameResponse.json().then(res => res.result.users);
// console.log(usernameResponsesArray)
```

### Step 3: Using OpenRanks' Global Ranking APIs to sort the result based on global rank

From the previous step, we obtained an array of various users based on the search query. Now, we'll filter out FIDs for all the returned users and pass them as the body to OpenRanks Global Ranking API to obtain the global ranking for each of those users.

```javascript
// Step 3: Using OpenRanks GlobalRanking APIs to find out the returned users Global Rank
// Here we use the previous Array and filter out the FIDs from it and in this step we try finding the Global Profile Rank based on engagement for each of these FIDs.
const usersGlobalRankBaseURL = 'https://graph.cast.k3l.io/scores/global/engagement/fids'
const usersGlobalRankResponse = await fetch(usersGlobalRankBaseURL, {
    method: 'POST',
    headers: {
        "Content-Type": "application/json"
    },
    body: JSON.stringify(usernameResponsesArray.map(element => element.fid))
});
const usersGlobalRankResponseArray = await usersGlobalRankResponse.json().then(element => element.result)
// console.log(usersGlobalRankResponseArray);
```

### Step 4: Creating a new array merging the userwith rank and score key values

At the end of the previous step, we now have two arrays: one from Neynar, `usernameResponsesArray`, containing user objects based on the search query, and another from OpenRank, `usersGlobalRankResponseArray`, containing ranks of all those users' FIDs. Now, we create a new array, `userResponseArrayWithRank`, which merges the two based on FIDs and helps create a new array with all the user information we have from Neynar, along with each user's rank and score.

The code below accomplishes the following: it compares user FIDs from both arrays, and if the FID matches, it adds the rank and score key-value pairs from the `usersGlobalRankResponse` to the `usernameResponsesArray`.

Now, we have an array of objects for the users along with their global rank and score included.

```javascript
// Step 4: Adding the rank and score key values to respective users in the userResponseArray that we got in Step 2 from Neynar
// Here we have the Global Profile Rank for each of these FIDs and then
const userResponseArrayWithRank = usernameResponsesArray.map(user1 => {
    const correspondingUser2 = usersGlobalRankResponseArray.find(user2 => user2.fid === user1.fid);
    if (correspondingUser2) {
      user1.rank = correspondingUser2.rank;
      user1.score = correspondingUser2.score;
    }
    return user1;
});
// console.log(userResponseArrayWithRank);
```

### Step 5: Sorting the array based on global rank

In this step, we pass the previous array through the function `sortByRankAscending` to sort the array based on the ascending list of rankings. This ensures that highly globally ranked users appear first. This helps significantly in sorting out spam accounts, pushing them down the list rather than displaying them at the top.

```javascript
function sortByRankAscending(usersArray) {
    return usersArray.sort((a, b) => a.rank - b.rank);
}
const sortedRankingArray = sortByRankAscending(userResponseArrayWithRank)
console.log(sortedRankingArray)
```

#### Future Improvements:

In the future, once personalized rankings APIs are made available for each user, which can be queried based on an input list of FIDs, providing rankings based on the viewer's FID, we can utilize this instead of the global rankings API to generate or even sort the list.


# Build "Suggested follow list" based on OpenRank and Neynar

Surface suggested follow list from your network.

In all these cases, this guide can be used as a reference, whether it is to display a suggested follow list on the user's homepage or to provide a suggested follow list when a user follows another user (similar to Twitter/X).

{% hint style="info" %}
This guide expects you to have already have signed up for a Neynar account and have the basics set up ready to be able to consume Neynars' APIs. \
If you haven't, you can go through the [getting started](https://docs.neynar.com/docs/getting-started-with-neynar) guide here.
{% endhint %}

### Step 1: Fetching the user FID

This is specifically tailored to your client's specifications, particularly in how it manages user data storage. However, the USER\_FID will be utilized to retrieve the personalized network and to identify and filter out already followed FIDs from that list of individuals in the personalized network. The subsequent steps will provide further detail on how these components interconnect.

```javascript
// Getting the USER FID
const USER_FID = 2025;
```

### Step 2: Fetching FIDs in the users personalized network

In this step, we create a function called `fetchPersonalizedRankings`, which takes an FID as a required parameter and returns a list of user arrays sorted based on their ranking, essentially representing the FID's network.

At the conclusion of this step, we now have a variable called `personalizedNetworkRankedArray`, which is an array containing all the people in my personalized network based on engagement. To learn more about how these rankings work, refer to this [resource](https://docs.openrank.com/integrations/farcaster/ranking-strategies-on-farcaster).

A brief overview of the other parameters:

* `k`: Determines the width of the network mapping.
* `limit` (maximum 5000): Used to restrict the returned response limit.
* `lite`: Defaults to true and is utilized to reduce the response payload to only include an FID and its corresponding score.

```javascript
// Step 2: Using Personalized Rankings API to find people in your extended network
async function fetchPersonalizedRankings (fid, k = 3, limit = 1000, lite = true) {
    const personalizedRankingsBaseURL = 'https://graph.cast.k3l.io/scores/personalized/engagement/fids'
    const personalizedRankingsParameters = `k=${k}&limit=${limit}&lite=${lite}`
    const personalizedRankingsURL = `${personalizedRankingsBaseURL}?${personalizedRankingsParameters}`
    const personalizedRankingsResponse = await fetch(personalizedRankingsURL, {
        method: 'POST',
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify([fid])
    });
    const personalizedNetworkRankedArray  = await personalizedRankingsResponse.json().then(element => element.result).then(element => element.slice(1))
    const filteredPersonalizedNetworkRankedArray = personalizedNetworkRankedArray.map(element => element.fid)
    return filteredPersonalizedNetworkRankedArray
}
const personalizedNetworkRankedArray = await fetchPersonalizedRankings(USER_FID)
console.log('Step 2 Personalized Network Ranked Array: ', personalizedNetworkRankedArray);
```

### Step 3: Find all of the following for an FID using Neynars API

From the previous step, we have an array of FIDs in our extended network. In this step, we are constructing another array containing all the users whom the user's FID is following. To achieve this, we've developed a small function called `fetchAllFollowing`, which accepts an FID as the query parameter and returns an array of users whom the input FID is following.

We're storing information about this in a variable called `usersFollowing`, and then creating another variable called `filteredUsersFollowing`, which exclusively retains the followed FIDs in the array, eliminating all unnecessary information. Therefore, we now have another array named `filteredUsersFollowing`, which contains all the FIDs that the user is following

`usersFollowingArray` is the final array in which we are storing the result of this function, which is an array of FIDs that are being followed by the user

```javascript
// Step 3: Using Neynars API to find all following of the User FID
async function fetchAllFollowings (fid) {
    const neynarBaseURL = "https://api.neynar.com/v2/farcaster/following"
    let cursor = "";
    let users = [];
    do {
        const urlFollowingQueryParams = `fid=${fid}&limit=100&cursor=${cursor}`
        const neynarUsersFollowingURL = `${neynarBaseURL}?${urlFollowingQueryParams}`
        const result = await fetch(neynarUsersFollowingURL, {
            headers: {
                'Content-Type': 'application/json',
                api_key: process.env.NEYNAR_API_KEY
            }
        })
        const resultResponse = await result.json()
        users = users.concat(resultResponse.users);
        cursor = resultResponse.next.cursor
        // console.log(cursor);
    } while (cursor !== "" && cursor !== null);

    const filteredUsersFollowing = users.map(element => element.user.fid)
    return filteredUsersFollowing
};

const usersFollowingArray = await fetchAllFollowings(USER_FID);
console.log('Step 3 Users Following: ', usersFollowingArray); // Array of FIDs that the user if following
```

{% hint style="warning" %}
Here sometimes if a user has a lot of followings, the response time can increase, We are actively working on a better and faster work around to this, but until then.
{% endhint %}

### Step 4: Finding Users in your Extended Network who you aren't already following

From the previous two steps, we now possess two arrays of FIDs: one comprising our extended network and the other our following. In this step, we create an array called `suggestedFollowFIDsArray`. Essentially, it iterates through the first array, `personalizedNetworkRankedArray`, and checks if each FID is included in the second array, `usersFollowingArray`. If it isn't, then that FID is added to a new array, `suggestedFollowFIDsArray`.

```javascript
// Step 4: Finding Users in your Extended Network who you aren't already following
function findSuggestedFollowFIDs (personalizedNetworkRankedArray, usersFollowingArray) {
    const suggestedFollows = [];
    for (let i = 0; i < personalizedNetworkRankedArray.length; i++) {
        if (!usersFollowingArray.includes(personalizedNetworkRankedArray[i])) {
            suggestedFollows.push(personalizedNetworkRankedArray[i])
        }
    }
    return suggestedFollows
}
const suggestedFollowFIDsArray = findSuggestedFollowFIDs(personalizedNetworkRankedArray, usersFollowingArray);
console.log(suggestedFollowFIDsArray)
```

### Step 5: Finding User Information of the suggested follows

From the previous step, we now possess an array called `suggestedFollowFIDsArray`. To enable your client to display all their information, fetching all the necessary data is imperative. Neynars provides an endpoint, accessible here: [Neynars User Bulk](https://docs.neynar.com/reference/user-bulk), which allows fetching bulk user information, catering to at most 100 users at a time.

We've developed a function called `fetchUserInfoBulk`, which accepts an array of FIDs, splits them into chunks of 100 to comply with Neynars' criteria, concatenates those chunks into a string, and forwards them to the Neynars API. The resulting responses are stored in the `resultResponse` array. This process is repeated for all the split chunks.

Executing this function yields a response stored in the variable named `suggestedFollowUserInfo`, which essentially represents an array containing information about all the suggested users.

```javascript
async function fetchUserInfoBulk(fidsArray) {
    const chunkSize = 99;
    const neynarBaseURL = "https://api.neynar.com/v2/farcaster/user/bulk";

    let resultResponses = [];

    // Split the array into chunks of size 100
    for (let i = 0; i < fidsArray.length; i += chunkSize) {
        const chunk = fidsArray.slice(i, i + chunkSize);
        const suggestedFollowFIDsString = chunk.map(element => element).join(",");
        console.log(suggestedFollowFIDsString);
        const neynarUserInfoParams = `fids=${suggestedFollowFIDsString}`;
        const neynarUserInfoURL = `${neynarBaseURL}?${neynarUserInfoParams}`;

        const result = await fetch(neynarUserInfoURL, {
            headers: {
                'Content-Type': 'application/json',
                api_key: process.env.NEYNAR_API_KEY
            }
        });

        const resultResponse = await result.json();
        const resultResponseUsers = resultResponse.users;
        resultResponses.push(resultResponseUsers);
    }

    return resultResponses.flat(1);
}

const suggestedFollowUserInfo = await fetchUserInfoBulk(suggestedFollowFIDsArray);
console.log('Suggested Follow: ', suggestedFollowUserInfo);
```

### Extending suggestions to also show on other user profile who you just followed.

Twitter and other popular platforms offer a feature wherein, upon following a user, they promptly display another suggested list of users to follow based on the followed user's network.

The aforementioned functions can be repurposed to create this functionality. The only adjustment required is to replace the network array (from Step 2) with the recently followed FID. Following this, all other steps can remain unchanged to generate this feature.

{% hint style="info" %}
The current implementation of this is still slow, we are currently working with partners to optmize this and make it faster. The docs will be updated as and when it happens.
{% endhint %}


# Build Channel Trending Feeds for your Client using Neynar and OpenRank APIs

Create your own Channel Trending Feed for your client.

{% hint style="warning" %}
Channel rankings are live for top 100 popular channels based on this [dune query,](https://dune.com/queries/3422001/5745837) by 14th June, we'll support 500 channels. Here's a [sheet](https://docs.google.com/spreadsheets/d/1Hgv4hV0O0OgQ99Xx7NlHHF1DeRUgqTHPEtwEdm6CELs/edit#gid=0) with all the channels supported.
{% endhint %}

Developers can utilize the channel trending endpoints to seamlessly integrate with Neynar and promptly set up a channel feed. The current channel feed uses **channel rankings with moderators as seed peers** to identify relevant casts based on the interactions of the channel's top-ranked profiles.

Here's a simple guide demonstrating how you can use the Channel Trending Feed API alongside Neynar's API to create a channel trending feed for your clients. If you have previously gone through the "For You" guide, this process will be quite similar.

{% hint style="info" %}
This guide expects you to have already have signed up for a Neynar account and have the basics set up ready to be able to consume Neynars' APIs. \
If you haven't, you can go through the [getting started](https://docs.neynar.com/docs/getting-started-with-neynar) guide here.
{% endhint %}

### Step 1: Getting the channel

The primary required parameter for the OpenRanks API is the channel name. In this example, we are using a static string, but this can be customized according to the client developer's preference.

```javascript
const channelName = 'degen';
```

### Step 2: Retrieving Trending Cast Hashes for the Channel

In this step, we will create a function called `getChannelTrendingCastHashes`, which takes the following parameters: `channelName`, `agg`, `weights`, `offset`, `limit`, and `lite`. This function returns an array of cast hashes for the specified channel, sorted based on their trendiness.

Let's look at each of these parameters:

* **channelName**: The name of the channel.
* **agg**: The aggregation function used to determine how the weights should be aggregated. In simpler terms, it defines how the weights are amplified.
* **weights**: Specifies the importance given to each action on the cast (like, cast, recast, reply).
* **offset**: Used for pagination, indicating the number of casts to skip.
* **limit**: Used for pagination, specifying the number of casts to fetch.
* **lite**: A boolean parameter that, when set to true, reduces the payload to only return hashes instead of hashes and additional information.

To understand how the weights and agg parameters affect the feed, read [here](/integrations/farcaster/neynar-x-openrank-guides-wip/build-for-you-feeds-for-your-client-using-neynar-and-openrank#configuring-the-feed).

```javascript
async function getChannelTrendingCastHashes(channelName = 'degen', agg = 'sumsquare', weights = 'L1C10R5Y1', offset = 0, limit = 25, lite = true) {
  const openRankBaseURL = `https://graph.cast.k3l.io/channels/casts/popular/${channelName}`
  const channelTrendingCastHashesParams = `agg=${agg}&weights=${weights}&offset=${offset}&limit=${limit}&lite=${lite}`
  const channelTrendingCastHashesUrl = `${openRankBaseURL}?${channelTrendingCastHashesParams}`;
  const channelTrendingCastHashesResponse = await fetch(channelTrendingCastHashesUrl, {
    headers: {
      'Content-Type': 'application/json',
    },
  });
  return channelTrendingCastHashesResponse.json().then(response => response.result);
}
const channelTrendingCastHashesArray = await getChannelTrendingCastHashes();
console.log(channelTrendingCastHashesArray); // logs information about the cast hashes in ranked in order of recommendation
```

### Step 3: Using Neynar to retrieve cast data:

In the previous step, we successfully retrieved all the cast hashes in the order of their recommendation. Now, in this step, we will transform the response from the previous step and connect to the Neynar API to obtain the cast information for each of the hashes. Neynar offers a convenient API endpoint where you can pass an array of cast hashes and receive detailed information for each cast in the response.

We will create another function that takes an array of cast hashes as a parameter and returns an array of casts with all the necessary information to generate the feed. The `transformedCastHashesArray` variable will simply extract the hash values from the `channelTrendingCastHashesArray`.

```javascript
// Step 3: Integrating with Neynar to fetch the contents for these casts Hashes
const transformedCastHashesArray = channelTrendingCastHashesArray.map(element => element.cast_hash).join(',');
async function getChannelTrendingFeed(castHashesArray = []) {
  // https://docs.neynar.com/reference/casts
  const neynarBaseURL = 'https://api.neynar.com/v2/farcaster/casts'
  const neynarCastsURL = `${neynarBaseURL}?casts=${castHashesArray}`;
  const feedResponse = await fetch(neynarCastsURL, {
      headers: {
        'Content-Type': 'application/json',
        api_key: process.env.NEYNAR_API_KEY
      },
  });
  const feed = await feedResponse.json().then(response => response.result.casts);
  console.log(feed) // logs the feed which is an array in order of all casts

  return feed;
}

const channelTrendingFeed = await getChannelTrendingFeed(transformedCastHashesArray);
console.log(channelTrendingFeed); // logs the feed which is an array in order of all casts
```

### Enabling Paginating:

To build a client with infinite scrolling, you need to implement pagination. The `channelTrendingCastHashesUrl` already supports two parameters: `offset` (the number of casts to skip) and `limit` (the number of casts to fetch each time). These parameters are essential for generating a paginated feed.

Here’s how to enable pagination:

1. **Add Variables**: Declare a `page` variable outside the `paginatedFeed` function. This variable will represent the page number. Also, declare a `castsPerPage` variable to define the number of casts per page and an `offset` variable to track the number of casts to skip.
2. **Function Modification**: Enclose the function calls to `getChannelTrendingCastHashes()` and `getChannelTrendingFeed()` inside the `paginatedFeed()` function. This function should take `page` and `channelName` as parameters and return the paginated feed.

Here's a revised version of the code:

```javascript
let page = 1;
const channel = 'degen';
const castsInPage = await paginatedFeed(page, channel)
```

```javascript
async function paginatedFeed(page, channel) {

    const castsPerPage = 25;
    const offset = page * castsPerPage;

    // Step 1: Getting The Users FID
    const channelName = channel;

    // Step 2: Getting the ranked casts hashes for creating the Personalized For You Feed
    const channelTrendingCastHashesArray = await getChannelTrendingCastHashes(channel, 'sumsquare', 'L1C10R5Y1', offset, castsPerPage, true);
    console.log(channelTrendingCastHashesArray); // logs information about the cast hashes in ranked in order of recommendation
    const transformedCastHashesArray = channelTrendingCastHashesArray.map(element => element.cast_hash).join(',');

    // Step 3: Integrating with Neynar to fetch the contents for these casts Hashes
    const channelTrendingFeed = await getChannelTrendingFeed(transformedCastHashesArray);
    return channelTrendingFeed;
}
```

With this setup, the feed is paginated based on the page number and the number of casts per page. Calling `paginatedFeed(page, channelName)` with the appropriate parameters will return the desired paginated feed.

### Configuring the feed:

You can currently use the aggregation (`agg`) parameter and the `weights` parameter to change how the feed is generated. A detailed explanation of this process can be found [here](/integrations/farcaster/neynar-x-openrank-guides-wip/build-for-you-feeds-for-your-client-using-neynar-and-openrank#configuring-the-feed).


# Build "Discover New Users Feed" using Neynar and OpenRanks Global Ranking API

(Coming Soon)


# Build Power Badges for your Client using Global & Personalized Ranking APIs by OpenRank

Create your own custom PowerBadge for your client

### Custom Power Badge Strategies:

1. **Based on Top X Globally Ranked Users**
2. **Based on a Cut Off Percentile**
3. **Based on Top X Profiles from a Customized Graph Generated for a Given List of Input FIDs**

Before delving into the code for each strategy, we need to introduce some utility functions at the outset. These functions will be utilized later to transform data, ensuring compatibility with the input parameter schema.

```javascript
// Function to call global rankings
async function fetchGlobalRankings (offset, limit) {
    const globalRankingsBaseURL = 'https://graph.cast.k3l.io/scores/global/engagement/rankings'
    const globalRankingsParameters = `offset=${offset}&limit=${limit}`
    const globalRankingsURL = `${globalRankingsBaseURL}?${globalRankingsParameters}`
    const globalRankingsResponse = await fetch(globalRankingsURL, {
        method: 'GET',
        headers: {
            "Content-Type": "application/json"
        },
    });
    const globalRankedArrayResponse  = await globalRankingsResponse.json()
    const globalRankedArray = globalRankedArrayResponse.result;
    return globalRankedArray
}

// Function to call Personalized Rankings End Point
async function fetchPersonalizedRankings (fidsArray, limit = 100, lite = true) {
    const personalizedRankingsBaseURL = 'https://graph.cast.k3l.io/scores/personalized/engagement/fids'
    const personalizedRankingsParameters = `&limit=${limit}&lite=${lite}`
    const personalizedRankingsURL = `${personalizedRankingsBaseURL}?${personalizedRankingsParameters}`
    const personalizedRankingsResponse = await fetch(personalizedRankingsURL, {
        method: 'POST',
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify(fidsArray)
    });
    const personalizedRankedArrayResponse  = await personalizedRankingsResponse.json()
    const personalizedRankedArray = personalizedRankedArrayResponse.result;
    return personalizedRankedArray
}
```

And finally some utility functions:

```javascript
// Utilities
// Function to merge objects in array of objects by FID
function mergeObjectsByFID(array) {
    return array.reduce((acc, obj) => {
        const found = acc.find(item => item.fid === obj.fid);
        if (found) {
            // If an object with the same fid is found, merge the objects
            Object.assign(found, obj);
        } else {
            // If not found, add the object to the accumulator
            acc.push({ ...obj });
        }
        return acc;
    }, []);
}
```

### Strategy 1: Top X Globally Ranked users

The `fetchTopXUsers` async function retrieves the top users from a global ranking system. It iterates through the rankings in batches of 1000, avoiding duplicates by tracking seen user identifiers. The loop breaks when the desired number of top users is reached or when there are no more records to fetch. The function returns an array containing the specified number of unique top users.

The `fetchTopXUsers` async function takes one parameter, `numberOfTopUsers`, specifying the desired number of top users to fetch. It returns an array containing the unique top users retrieved from the global ranking system, limited to the specified number.

With this strategy, you now have the capability to distribute customized power badges to the top 10,000, 5,000, 1,000, etc globally ranked users.

{% hint style="info" %}
More information on how these global ranks are generated can be found [here](/integrations/farcaster/ranking-strategies-on-farcaster)
{% endhint %}

```javascript
async function fetchTopXUsers(numberOfTopUsers) {
    const limit = 1000;
    let offset = 0;
    let resultResponses = [];
    const seenFIDs = new Set();

    while (resultResponses.length < numberOfTopUsers) {
        const globalRankingsResponse = await fetchGlobalRankings(offset, limit);
        if (globalRankingsResponse.length === 0) {
            break; // No more data to fetch
        }
        for (const obj of globalRankingsResponse) {
            if (!seenFIDs.has(obj.fid)) {
                resultResponses.push(obj);
                seenFIDs.add(obj.fid);
            }
            if (resultResponses.length === numberOfTopUsers) {
                break;
            }
        }

        offset += limit; // Increment the offset for the next API call
    }
    return resultResponses.slice(0, numberOfTopUsers); // Ensure exactly `count` objects are returned
}
const topXGlobalRankedUsers = await fetchTopXUsers(1000);
// console.log('Top X Global Ranked Users: ', topXGlobalRankedUsers);
```

### Strategy 2: **Till a certain Cut Off Percentile**

This function `fetchFIDsUntilCutoffPercentile` retrieves user data from the global rankings API until reaching a specified percentile cutoff. It accepts a `cutoffPercentile` parameter indicating the desired percentile threshold. The function aggregates fetched data until an object's percentile value falls below the cutoff, then filters the aggregated results to include only objects meeting or exceeding the cutoff percentile. Finally, it returns the filtered user data. Example usage demonstrates fetching users up to the 98th percentile and logging the result.

```javascript
async function fetchFIDsUntilCutoffPercentile(cutoffPercentile) {
    const limit = 1000;
    let offset = 0;
    let resultResponses = [];
    let keepFetching = true;

    while (keepFetching) {
        // Fetch data from the API with offset and limit
        const response = await fetchGlobalRankings(offset, limit);

        // Aggregate the results
        resultResponses = resultResponses.concat(response);

        // Check if any object in the fetched data reaches or exceeds the cutoff percentile
        if (response.some(obj => obj.percentile < cutoffPercentile)) {
            keepFetching = false;
        }

        // Increment the offset for the next fetch call
        offset += limit;

        // If no more data is returned, stop fetching
        if (response.length < limit) {
            break;
        }
    }
    // Filter the aggregated results to include only those objects up to the cutoff percentile
    const filteredObjects = resultResponses.filter(obj => obj.percentile >= cutoffPercentile);
    const mergedFilteredObjects = mergeObjectsByFID(filteredObjects);
    return mergedFilteredObjects;
}

const topXPercentileUsers = await fetchObjectsUntilCutoffPercentile(98);
console.log('Top X Percentile Users: ', topXPercentileUsers);
```

### Strategy 3: Top X Profiles from Custom FIDs graph

This function `topXProfilesFromCustomFIDsGraph` retrieves top user profiles based on a custom array FIDs using personalized rankings. \
It accepts two parameters: `fidsArray`, an array of user identifiers, and `count`, the number of top profiles to retrieve. \
The function limits the input FIDs array to 99 elements due to API restrictions, fetches personalized rankings based on the modified input array, and returns an array containing the top user profiles.&#x20;

Example usage demonstrates fetching the top profiles based on FIDs \[1, 2, 3] and logging the result.

{% hint style="info" %}
Thee is a temporary restriction of this endpoint:

1. You can input a maximum of 100 FIDs.
2. Currently, the output is capped at 5000.

Both of these things should be fixed and opened up in the coming months
{% endhint %}

```javascript
async function topXProfilesFromCustomFIDsGraph(fidsArray, count) {
    const inputFIDsArray = fidsArray.slice(0, 99);
    const response = await fetchPersonalizedRankings(inputFIDsArray, count);
    return response;
}
const topXProfilesFromFIDsUsers = await topXProfilesFromFIDs([1,2,3], 5000);
console.log('Top X Profiles From FIDs: ', topXProfilesFromFIDsUsers);
```

### Future:

The current global rankings are determined by seed peers and weights selected by us, along with certain Dune queries.&#x20;

However, in the upcoming months, we aim to extend this capability to developers, empowering them to customize both seed peers and weights. This enhancement will enable developers to create their own power badge ranking system, allowing for the customization of various parameters such as seed peers and weights. By tailoring these parameters to their preferences, developers can refine the ranking criteria to better suit their specific needs and objectives.


# Build "Sort Replies" on a cast using Neynar and OpenRanks' Global Ranking API

Sort Direct Replies in a cast for your client

{% hint style="info" %}
This guide expects you to have already have signed up for a Neynar account and have the basics set up ready to be able to consume Neynars' APIs. If you haven't, you can go through the [getting started](https://docs.neynar.com/docs/getting-started-with-neynar) guide here.
{% endhint %}

### Step 1: Getting Details of the Cast

To gather cast information, utilize Neynar by providing a URL identifier. While we're using constants here, you can also obtain these details dynamically from feeds or other sources.

This could also be done using castHashes just change the Identifier to the `cast_hash` and cast type to `hash`

```javascript
// Step 1: Getting Details of the Cast 
const castIdentifier = "https://warpcast.com/dwr.eth/0xb1e61e72"
const castType = "url"
const USER_FID = 2025
```

### Step 2: Accessing Responses with the Neynar API

In this step, we'll establish an asynchronous function named `getCastWithResponses`. This function takes three parameters: `castIdentifier`, `castType`, and `viewerFID`, and returns the cast along with its responses in an array.

{% hint style="info" %}
Additionally, Neynar provides a few more parameters such as reply depth and include\_chronological\_parent\_casts. For the purposes of this guide, we'll keep these set to their default values: 1 for reply depth and false for include\_chronological\_parent\_casts.
{% endhint %}

```javascript
// Step 2: Accessing Responses with the Neynar API
async function getCastWithResponses(castIdentifier, castType, viewerFID) {
    const neynarCastResponsesBaseURL = `https://api.neynar.com/v2/farcaster/cast/conversation`
    const urlCastResponsesParams = `identifier=${castIdentifier}&type=${castType}&reply_depth=1&include_chronological_parent_casts=false&viewer_fid=${viewerFID}`
    const neynarCastResponsesURL = `${neynarCastResponsesBaseURL}?${urlCastResponsesParams}`
    const neynarCastResponsesResponse = await fetch(neynarCastResponsesURL, {
        headers: {
            'Content-Type': 'application/json',
            api_key: process.env.NEYNAR_API_KEY
          },
    })
    const castWithResponses = await neynarCastResponsesResponse.json().then(res => res.conversation);
    // console.log(castWithResponses)
    return castWithResponses
}
```

We invoke this function and store the results in a variable named `castWithResponses`. Then, we define another variable called `castDirectReplies`, which is an array of objects containing all the direct replies. Later, we'll utilize this `castDirectReplies` array to generate and store global ranks for each reply.

```javascript
const castWithResponses = await getCastWithResponses(castIdentifier, castType, USER_FID)
// console.log(castWithResponses)
const castDirectReplies = castWithResponses.cast.direct_replies;
// console.log(castDirectReplies)
```

### Step 3: Using OpenRanks APIs to find out Global Rank of the users who replied

In this step, we use OpenRanks APIs to determine the global rank of users who replied directly. We define two functions:

1. `fetchGlobalRanks`: This function takes an array of FIDs and returns their corresponding global ranks. As the API can handle only up to 100 FIDs at a time, we need to batch process the FIDs.
2. `getAllGlobalRanks`: This function takes an array of FIDs similar to `fetchGlobalRanks`, but it divides the input array into chunks of 100 elements each to comply with the API requirement. It then retrieves global ranks for each chunk and aggregates them into a single array, which is returned. Thus, `getAllGlobalRanks` can process an array of any number of FIDs and return the global ranks for all of them.

```javascript
// Step 4: Using OpenRanks APIs to find out Global Rank of the users who replied
async function fetchGlobalRanks(fidsArray) {
    const usersGlobalRankBaseURL = 'https://graph.cast.k3l.io/scores/global/engagement/fids'
    const usersGlobalRankResponse = await fetch(usersGlobalRankBaseURL, {
        method: 'POST',
        headers: {
            "Content-Type": "application/json"
        },
        body: JSON.stringify(fidsArray)
    });
    const usersGlobalRankResponseArray = await usersGlobalRankResponse.json().then(element => element.result)
    // console.log(usersGlobalRankResponseArray);
    return usersGlobalRankResponseArray
}
async function getAllGlobalRanks(fidsArray) {
    const chunkSize = 99
    let globalRanksArray = []
    for (let i = 0; i < fidsArray.length; i += chunkSize) {
        const chunk = fidsArray.slice(i, i + chunkSize)
        const chunkedGlobalRanksArray = await fetchGlobalRanks(chunk)
        globalRanksArray = globalRanksArray.concat(chunkedGlobalRanksArray)
    }
    return globalRanksArray
}
```

Now that we have the two functions defined, let's create an array of FIDs based on the authors of the replies. We'll achieve this using a simple map function and store the FIDs of all repliers in a variable called `fidsOfRepliers`. Then, we'll pass this array as an argument to the `getAllGlobalRanks` function and store the returned array of global ranks in a variable called `usersGlobalRankResponseArray`.

```javascript
const fidsOfRepliers = castDirectReplies.map(reply => reply.author.fid)
// console.log(fidsOfRepliers)
const usersGlobalRankResponseArray = await getAllGlobalRanks(fidsOfRepliers)
// console.log(usersGlobalRankResponseArray);
```

### Step 4: Adding Global Ranks to Direct Replies

In this step, we'll create a new array called `castDirectRepliesWithGlobalRank`. As the name suggests, it's essentially the same array as `castDirectReplies` from Step 2, but with the addition of global ranks. We'll achieve this by declaring a utility function called `addGlobalRank`, which takes two arrays as parameters: the first array is `castWithDirectReplies`, and the second one is `usersGlobalRankResponses`. It matches the FID and adds the global rank to each object.

```javascript
// Step 4: Adding Global Ranks to Direct Replies
function addGlobalRank(array1, array2) {
    array1.forEach(item1 => {
        array2.forEach(item2 => {
            if (item1.author && item1.author.fid === item2.fid) {
                item1.author.globalRank = item2.rank;
            }
        });
    });
    return array1;
}
```

```javascript
const castDirectRepliesWithGlobalRank = addGlobalRank(castDirectReplies, usersGlobalRankResponseArray)
// console.log(castDirectRepliesWithGlobalRank)
```

At the conclusion of this step, we now have a variable named `castDirectRepliesWithGlobalRank`. This variable contains an array comprising all the direct replies to the focused cast. Additionally, each reply within this array includes the global rank of its respective author.

### Step 5: Sorting Replies by Authors' Global Rank

To complete the process, we need to sort the `castDirectRepliesWithGlobalRank` array in ascending order based on the authors' global ranks. To achieve this, we'll define a generalized function named `sortByRankAscending`. This function takes an array as input and returns a sorted array in ascending order based on the authors' global rank.

```javascript
// Step 5: Sorting Replies by Authors' Global Rank
function sortByRankAscending(usersArray) {
    return usersArray.sort((a, b) => a.author.globalRank - b.author.globalRank);
}
const sortedRepliesBasedOnGlobalRankingsArray = sortByRankAscending(castDirectRepliesWithGlobalRank)
// console.log(sortedRepliesBasedOnGlobalRankingsArray)
```

Now, you have an array sorted based on the replies' authors' global rank. This can be utilized as a method to surface relevant replies first.


# Clanker OpenRank Scores

**Clanker OpenRank Scores** are calculated for tokens deployed using Clanker. These scores are calculated based on the reputation of traders/holders of clanker tokens. These scores can be used by social apps, bots and clients to power search and discovery of clankers through feeds such as recent, trending, top or popular among my friends/network.

{% embed url="<https://www.loom.com/share/c4b31127ab0e404598a2eb2c0c53d111?sid=932103ae-b7cb-4594-86a7-f7be5e324f7f>" %}

## Use Cases

Some of the common use cases for Clanker Scores include:

* Sorting token feeds by **recent**, **trending**, or **new** activity.
* Creating indexes of **popular tokens** or **funds**.
* Identifying tokens associated with **high-reputation users**.

Further examples and use cases can be found below.

## Available Scores

A variety of Clanker scores can be viewed on our dune [dashboard](https://dune.com/openrank/clanker-scores-dashboard).

{% hint style="info" %}
Currently these scores are updated every 15 mins and available as an API on Dune. we may make these available via dedicated APIs.
{% endhint %}

1. ### Scores based on buyers reputation <a href="#scores-based-on-buyers-reputation" id="scores-based-on-buyers-reputation"></a>

   These scores are based on the OpenRank scores of buyers, which reflect the reputation of individual users. We map the buyer’s reputation to their buying activity, generating the Clanker Score based on **intentional** buying actions. This score highlights the trustworthiness of buyers within the Farcaster network.\
   **Query ID**: `4355256`
2. ### Scores based on wallet holders reputation

   These scores are derived from the reputation of wallets that hold Clanker tokens. This includes tokens that may have been airdropped to highly reputable users. However, please note that these scores can be more susceptible to Sybil attacks.\
   **Query ID**: `4354937`
3. ### Buyer Breakdown

   For a given token who are the top reputable users buying the tokens and for a given farcaster user which are some of their top token purchases\
   **Query ID:** `4631126`
4. ### Scores for recent trending clankers

   This score will reflect the recency of activity related to a new token, helping to identify newer or more actively traded tokens.\
   **Query ID:** `4398242`

## Getting Started

Here is a quick [Google Collab](https://colab.research.google.com/drive/1cjUNrEK0w0YXPpv3F-EzqEHQ2sa3Ydk3?usp=sharing), that shows how can you use the dune SDK to quickly fetch the scores

You can integrate Clanker Scores into your application in two ways:

1. **Fetch All Scores at Once**: You can retrieve all Clanker Scores in bulk and store them in your database for periodic use.
2. **Fetch Scores in Real-Time**: You can query Clanker Scores from Dune in real-time for a specific token address.

### Step-by-Step Guide:

1. **Obtain a Dune API Key**\
   To get started, [sign up for a Dune account](https://dune.com/) and get an API key.
2. **Decide Which Scores You Need**\
   Choose the Clanker Score type(s) you require. Refer to the relevant **Query ID** for each score type:
   * Buyer Reputation Score: `4355256`
   * Wallet Holder Reputation Score: `4354937`
3. **Fetch Scores from Dune**\
   You can fetch scores either in bulk or for a specific token address. Below is a quick Google Colab example that demonstrates how to use the Dune SDK to fetch Clanker Scores.


# Lens Protocol

The OpenRank APIs for Profile scoring aim to surface individual profile scores and highlight socially acceptable attestations within the Lens ecosystem, while the content recommendation system focuses on surfacing new and engaging posts through ranking and classification. Additionally, our platform provides personalization algorithms that suggest profiles and posts relevant to each individual's social network and interests, enhancing the overall user experience within the Lens ecosystem.

Our APIs for profile scores and personalized recommendations help builders in the Lens ecosystem deliver highly relevant and engaging social experiences by giving them access to a readily-available recommendation service and allowing them to focus on building amazing user experiences.

Developers:

* Get started with [**demos and tutorials**](https://docs.karma3labs.com/lens-protocol/getting-started-demos)
* We published the [**documentation**](https://docs.karma3labs.com/lens-protocol/ranking-strategies-on-lens) describing pre-configured reputation scoring strategies
* Developers can tryout the API via [**OpenAPI**](https://openapi.lens.k3l.io/) using the 4 initial [**scoring strategies**](https://docs.karma3labs.com/lens-protocol/ranking-strategies-on-lens)


# Ranking Strategies on Lens

Profile reputation scoring to address trustworthiness in social networks

We implemented a set of strategies to help the Lens Protocol community with a heuristics that can help reveal engaging profiles and recommend interesting content, calling them ranking strategies.

Ranking strategies are parameters placed in front of an algorithmic computation, which is highly intensive, with involvement of linear algebra and matrix convergence to generate EigenValue scores from any graph-like dataset, such as Web3 social graphs from the [Lens Protocol](/integrations/lens-protocol) ecosystem.

## Ranking Strategies

### Seeding the Strategies

The following strategies below are used for Lens Protocol's API offered by Karma3 Labs (K3L).  All of the strategies will be seeded by 10 profiles chosen as a starting point of hand-picked profiles to begin the computation of trustworthiness.  The profiles are:

```javascript
	const ogs = ["yoginth.lens", "christina.lens", "mariariivari.lens",
	"bradorbradley.lens", "wagmi.lens", "levychain.lens", "nicolo.lens",
	"sasicodes.lens", "stani.lens", "davidev.lens" ]
```

### Strategy: followship

This strategy emphasizes only on the relevant and meaningful `follows` as peer-to-peer attestations, disregarding `mirrors` and `comments`.  If the profile quietly collects NFTs by influencers and creators, these are a signal of non-Sybil activities.

{% hint style="info" %}
***Weight Assignments:** Follows = 1*
{% endhint %}

### Strategy: engagement

This strategy emphasizes on social engagements as attestations, combining `follows`, `mirrors` and `comments`.  The more engagements a profile receives for their posts and profiles, this will result in higher profile scores.

{% hint style="info" %}
***Weight Assignments:** Follows = 6, Comments = 3, Mirrors = 8*
{% endhint %}

### Strategy: influencer

Similar to the **`engagement`** strategy, combining `follows`, `mirrors` and `comments` interactions (or attestations) between profiles, but adds another datapoint where posts can be turned into [NFT collections](https://docs.lens.xyz/docs/collect) by influencers.  When these NFTs are collected by others, these are strong signals of a reputable profile.

{% hint style="info" %}
***Weight Assignments:** Follows = 6, Comments = 3, Mirrors = 8, NFT Collects = 12*
{% endhint %}

### Strategy: creator

Similar to the **`influencer`** strategy, we add another datapoint where NFT collections that carry a price tag.  These become another strong indicator where an influencer has gained a strong following that NFT mints of posts reflect popular amongst a fan base in a creator economy.

{% hint style="info" %}
***Weight Assignments:** Follows = 6, Comments = 3, Mirrors = 8, NFT Collect Prices = 12*
{% endhint %}


# Lens Profile APIs

APIs for Profile Scores and Ranking

All profiles in the Lens ecosystem are scored and ranked every hour. The scores and rankings are then made available through 5 different APIs that clients can call depending on their use case.&#x20;

{% hint style="info" %}
For details on each strategy ID, see [Profile Scoring Strategies](/integrations/lens-protocol/ranking-strategies-on-lens#web3-social-lens-protocol-strategies)
{% endhint %}

## Profile Score

**Purpose:** Retrieve a single profile score on the Lens ecosystem

The `/profile/score` endpoint retrieves a single profile score.  Each profile `score` ranges between `0` and `1`, with `0` being the lowest score and `1` being the highest score.

This endpoint requires a `strategy` parameter as described in the [Scoring Strategies](/integrations/lens-protocol/ranking-strategies-on-lens) section.  This strategy simplifies the developer experience abstracting away the EigenTrust Local-Trust and Pre-Trust strategies.  Each strategy is pre-computed on a daily basis and applied to each Lens profile.

By using the `date` parameter, you can choose a particular day when the strategies were generated.  This is helpful to get a snapshot in time of where each profile scores and ranks are, to help develop time series metrics dashboards for each profile to see how their scores trend over time.

You can try out this API at this **OpenAPI interface** — [https://openapi.lens.k3l.io](https://openapi.lens.k3l.io/#/default/getScore)

## Profile Scores

**Purpose:** Retrieve a list of global profile scores on the Lens ecosystem

The `/profile/scores` endpoint retrieves a list of numeric profile scores and ranks in relations with all other profiles, ordered by the highest score.  Each profile `score` ranges between `0` and `1`, with `0` being the lowest score and `1` being the highest score. &#x20;

This endpoint also returns a `rank` position, starting with `1` as the top-ranked profile calculated for the day, and `n` being the lowest ranked, with `n` being the number of profiles included in the converged scoring calculations for the day.

This endpoint requires a `strategy` parameter as described in the [Scoring Strategies](/integrations/lens-protocol/ranking-strategies-on-lens) section.  This strategy simplifies the developer experience abstracting away the EigenTrust Local-Trust and Pre-Trust strategies.  Each strategy is pre-computed on a daily basis and applied to each Lens profile.

The results returned are paginated to 50 profiles per response by default.  This can be managed with a `limit` parameter with pagination alongside an `offset` parameter (first record at `offset` position `0`).  To help with pagination, you can use the [Profile Count endpoint](broken://pages/WTEvut9w17wylIlq7aYL) to retrieve the total number of profiles available.

By using the `date` parameter, you can choose a particular day when the strategies were generated.  This is helpful to get a snapshot in time of where each profile scores and ranks are, to help develop time series metrics dashboards for each profile to see how their scores trend over time.

## Profile Scores by Users

**Purpose:** Retrieve a list of global profile scores of a subset of users in the Lens ecosystem.

The `/profile/scores_by_users` endpoint retrieves a list of profile scores of a subset of users requested, and it will return with profile scores ranked in relations with all other profiles, ordered by the highest score.  Each profile `score` ranges between `0` and `1`, with `0` being the lowest score and `1` being the highest score. &#x20;

This endpoint also returns a `rank` position, starting with `1` as the top-ranked profile calculated for the day, and `n` being the lowest ranked, with `n` being the number of profiles included in the converged scoring calculations for the day.

This endpoint requires a `strategy` parameter as described in the [Scoring Strategies](/integrations/lens-protocol/ranking-strategies-on-lens) section.  This strategy simplifies the developer experience abstracting away the EigenTrust Local-Trust and Pre-Trust strategies.  Each strategy is pre-computed on a daily basis and applied to each Lens profile.

The results returned are paginated to 50 profiles per response by default.  This can be managed with a `limit` parameter with pagination alongside an `offset` parameter (first record at `offset` position `0`).  To help with pagination, you can use the [Profile Count endpoint](broken://pages/WTEvut9w17wylIlq7aYL) to retrieve the total number of profiles available.

By using the `date` parameter, you can choose a particular day when the strategies were generated.  This is helpful to get a snapshot in time of where each profile scores and ranks are, to help develop time series metrics dashboards for each profile to see how their scores trend over time.

## Profile Count

**Purpose:** Retrieve the total number of profiles found on the Lens ecosystem

This `/profile/count` endpoint retrieves the total number of profiles scored for a particular strategy. The returned value is an unsigned integer.

This endpoint requires a `strategy` parameter as described in the [Scoring Strategies](/integrations/lens-protocol/ranking-strategies-on-lens) section.  This strategy simplifies the developer experience abstracting away the EigenTrust Local-Trust and Pre-Trust strategies.  Each strategy is pre-computed on a daily basis and applied to each Lens profile.

There's a option to choose a particular day of how many profiles are available when the strategies were generated using the `date` parameter.&#x20;

## Profile Rank

**Purpose:** Retrieve a profile's score position, in relations to all other profiles, on the Lens ecosystem

This `/profile/rank` endpoint retrieves a particular profile's score position (ranked) for a particular strategy, in relations to all the other profiles in the Lens ecosystem. The returned value is an unsigned integer starting with `1`.

This endpoint requires a `strategy` parameter as described in the [Scoring Strategies](/integrations/lens-protocol/ranking-strategies-on-lens) section.  This strategy simplifies the developer experience abstracting away the EigenTrust Local-Trust and Pre-Trust strategies.  Each strategy is pre-computed on a daily basis and applied to each Lens profile.

There's a option to choose a particular day of how many profiles are available when the strategies were generated using the `date` parameter.&#x20;

## API Documentation

{% hint style="info" %}
Tryout the APIs here! — [https://openapi.lens.k3l.io](https://openapi.lens.k3l.io/#/default/getScores)
{% endhint %}

{% openapi src="<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>" path="/profile/scores" method="get" expanded="false" %}
<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>
{% endopenapi %}

{% openapi src="<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>" path="/profile/score" method="get" expanded="false" %}
<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>
{% endopenapi %}

{% openapi src="<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>" path="/profile/scores\_by\_users" method="get" expanded="false" %}
<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>
{% endopenapi %}

{% openapi src="<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>" path="/profile/count" method="get" expanded="false" %}
<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>
{% endopenapi %}

{% openapi src="<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>" path="/profile/rank" method="get" expanded="false" %}
<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>
{% endopenapi %}


# Lens Content APIs

APIs for Content Recommendations

Posts by profiles are scored (value between 0 and 1), classified (recommendable, maybe recommendable, not recommendable) and ranked every hour. The posts can then be retrieved by clients through any of the following 4 APIs.&#x20;

As an example, the `/feed` endpoint returns a JSON array of posts using the Popular algorithm as a default algorithm. To choose a different algorithm, specify the algorithm as a path parameter.&#x20;

Let's walk through all the **Global** and **Personalized** content feed recommendation APIs next.

## Global Feed

As for generalized (non-personalized) feed of posts according to each algorithm, we now have the following pre-computed algorithms.  These algorithms are:

* `/feed/recent` to choose the Recent algorithm.&#x20;
* `/feed/popular` to explicitly choose the Popular algorithm.
* `/feed/recommended` to choose the Recommended algorithm.
* `/feed/crowdsourced` to choose the Crowdsourced algorithm.

In addition to the algorithm path parameter, the endpoint also takes an optional `limit` query parameter. All the algorithms return a **max of 100 posts**. To get a smaller set of posts, specify the limit parameter. Example: `/feed/recent?limit=10`

## Personalized Feed

Personalized content feed algorithms are meant to generate lists of posts that are most relevant to each user. These algorithms crawl a user's social graph and activity when recommending posts.  There's a time decay element in this as well. &#x20;

These algorithm deployed is:

* `/feed/personal/{profile}/following` which uses the Following strategy

In addition to the algorithm path parameter, the endpoint also takes an optional `limit` query parameter. All the algorithms return a **max of 100 posts**. To get a smaller set of posts, specify the limit parameter. Example: `/feed/personal/karma3labs.lens/following?limit=10`

## API Documentation

{% hint style="info" %}
Tryout the APIs here! — [https://openapi.lens.k3l.io](https://openapi.lens.k3l.io/#/default/getScores)
{% endhint %}

{% openapi src="<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>" path="/feed/personal/{profile}/{strategy}" method="get" expanded="false" %}
<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>
{% endopenapi %}

{% openapi src="<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>" path="/feed/{strategy}" method="get" expanded="false" %}
<https://raw.githubusercontent.com/Karma3Labs/ts-lens/main/server/openapi/openapi.yaml>
{% endopenapi %}

You can try out this API at this **OpenAPI interface** — [https://openapi.lens.k3l.io](https://openapi.lens.k3l.io/#/default/getPersonalFeed)


# Lens Profile Insights

Diving Deep to discover insights around the relationship of activities and interactions in the Lens social graph

EigenTrust algorithms powered by OpenRank is used to better understand social interactions within the Lens Protocol community.  By using the `engagement` strategies to rank Lens users (we'll refer them as **profiles** from now on), we now have surfaced insights in a visual time series dashboard as well as scatter graphs.

## **Profile Activity (Outbound Actions)**:&#x20;

How does a profile's outwardly actions affect how well the are engaged inwardly?  When Lens profiles initiate actions such as following other profiles, authoring a new posts, commenting on other posts, liking posts/comments and even like (upvoting) other content, we found that it really depends on the reputation on the author that these Lens profiles are acting towards.  This inherently infers the quality of those posts/comments they interacted with to be noteworthy or not in a general community's point of view. By analyzing these actions, we aim to understand a profile's contribution to the entire Lens protocol community.

<figure><img src="/files/Ynbgf1IVEd5DGnkSolYN" alt=""><figcaption><p>Profile Activities charted over time</p></figcaption></figure>

## **Profile Interactions (Inbound Engagement)**

We also looked at how many types of interactions a Lens profile receives, across all types of interactions on their profile and content — whether they are followed by others, or their posts are mirrored, liked or commented by others. The inherent reputation of other profiles interacting with a profile of interest, is what we've surfaced in this most recent iteration in this visualization.

<figure><img src="/files/IlSL4Ri2v42AJsp2J8uN" alt=""><figcaption><p>Profile Interactions time series chart, discovering how active a profile is over time</p></figcaption></figure>

## Relationships between any two Profiles

How about the relationships between one profile with another?  If you'd like to discover how posts and comments are interacted upon by a single profile with another, we have that here as well, plotted over time

<figure><img src="/files/l0wzOxxtq6VzrygGfrfD" alt=""><figcaption><p>Direct Connections over time, discovering when engagement happens over time and at what scale</p></figcaption></figure>

## **Profile Classification**

We first classified actions and interactions for each profile. Then, aggregated each type's follows, post/comment likes, post/comment mirrors and divided them with the population's average totals of the same type, within actions and interactions. &#x20;

<figure><img src="/files/lG6AEpj4HT2Ql9nUPQqv" alt=""><figcaption><p>Classification of High ranked profiles, with most profiles in this cohort reaching higher levels of engagements, as shown above the Y-axis "Spam Threshold" threshold</p></figcaption></figure>

By analyzing these in three cohorts ranked by EigenTrust, we were able to visualize\* which profiles post quality content, like interesting posts, comment on popular threads (and reciprocated by others), and how potentially sybil profiles and spammers try really hard to farm engagement. EigenTrust computed trust rankings are able to gauge the credibility and influence of profiles in Lens Protocol.

{% hint style="info" %}
We visualized each cohort by placing them on a chart with the X-axis showing outward actions with the Y-axis as the amount of inbound interactions, on a non-linear logarithmic scale, we're able to draw the line between at the 0 scale.  If they fall below average, say their post doesn't get liked as much as the average population, then the value computed for this type, when dividing with the average totals, will be less than 1.0.  The log function of a number less than 1.0, will be will be a negative number (e.g. log(0.1) = -1).  If they are above average in a type of interaction, say get liked a lot more for each post, then they'll be in the positive range
{% endhint %}


# Metamask SPD

## **MetaMask Snaps Permissionless Distribution (SPD)**

Metamask is enabling a permissionless way for developers to build apps (Snaps) on Metamask. Third-party developers can leverage Metamask as an Open Platform and create Snaps that add extensible functionality to millions of users.

The discovery and distribution of Snaps is part of this innovation. To power a decentralized Snaps Distribution system, we're prototyping a reputation computer with the community of Snaps users, developers, auditors, and security experts. The initial goal is to help detect and filter safe and secure Snaps, based on collective community wisdom and sentiments, instead of centralized gate-keeping and curation.

Our prototype enables a decentralized reputation rating generated from an open and verifiable trust graph, powered by OpenRank. The reputation of the community members is factored into the reputation of the Snaps being recommended.

## How does the [prototype](https://permissionless.snaps.metamask.io/) use OpenRank

**OpenRank** powers a community-led reputation system for Snaps. It doesn't rely on solely the Metamask team or handpicked auditors and their opinions about Snaps. It opens up the system for external developers and security experts to share their reviews and ratings for Snaps, which leads to a Community sentiment around the safety or popularity of a Snap.

The key components of this system are:

* **Peer-to-Peer Attestations:** Users can issue Trust (and Distrust) assertions to each other, meaning users can endorse or report other users for certain skills (eg: security expert, software developer).
* **Peer-to-Snap Attestations:** Users can issue Trust (and Report) assertions to Snaps, meanings users can express their opinion about the safety or security of a Snap.
* **Trust computer** - A verifiable compute layer powered by **OpenRank,** which runs an open source algorithm to generate rankings for users and snaps based on community sentiment. The rules of the algorithm, the types of rating and reviews can be configured by the community.

<figure><img src="/files/NycBaBenlChcG3icHpma" alt=""><figcaption><p>Reputation of the Solana Wallet Snap &#x26; respective attestations made to it</p></figcaption></figure>

## Useful Links

* Permissionless Snaps Directory Prototype: <https://permissionless.snaps.metamask.io/>
* User Guide: <https://support.metamask.io/hc/en-us/articles/23263846792475-What-is-the-Decentralized-Snaps-Directory>
* Snaps Reputation Graph Explorer: [https://snaps.k3l.io](https://snaps.k3l.io/)
* Dune Dashboard for User and Snaps: <https://dune.com/karma3-labs/metamask-snaps-attestations-and-trust-scores>
* Trust Computer algorithm README: <https://github.com/Karma3Labs/rs-eigentrust>


# Onchain Graphs and Feeds

**OpenRank** computes user, smart contract, nft rankings based on onchain transaction graphs. These rankings can be used for surfacing personalized recommendations or finding valueable apps and users in a particular ecosystem/chain. &#x20;

## **How it Works**

OpenRank computes on social graph data such as Farcaster, Lens and Onchain transactions data (token transfers, contract interaction, nft ownership) using EigenTrust and Matrix Factorization graph compute algorithms to generate a personalized network graph for users.

The compute results can be used to aggregate useful rankings and recommendations such as:

* Popular tokens owned in your network
* Popular NFTs owned in your network
* Popular on-chain contracts in your network

This [prototype](https://onchain.k3l.io/) uses Ethereum and Base Transaction data to populate an onchain feed for a user based on their EOA or a curated set of EOAs. The feed shows a ranked list of users, apps, tokens, NFTs, smart contracts, etc, by their reputation score values.

<figure><img src="/files/y0fRKwtmclScb8is2CGj" alt=""><figcaption></figcaption></figure>

A user can choose from a set of of reputation graphs based on who you follow or engage with on Farcaster or Lens, who you've sent tokens($) to on a chain, what NFTs or mints you own. We run verifiable compute on these graphs to create personalized rankings which can be used in different contexts and feeds.

If you're on Farcaster, select the Farcaster graph and type your or a set of Farcaster handles. Next, select the type of feed you'd like to view:

* *Network* - to see a personalized list of users you and your friends engage with
* *Tokens* - to see popular tokens owned by your network
* *NFTs* - to see popular NFTs owned by your network
* *Contracts* - to see popular contracts used by your network.

\*currently tokens, NFTs and contracts supported for **ETH** and **BASE**. OP support coming soon.

If you are a power user on Ethereum mainnet or Base, and have done token transfers to other EOAs in the past one year, you can generate your Ethereum and Base on-chain graph by entering your address, or even create a graph for a curated set of addresses.

## **API Access and What's next?**

Get in touch with Karma3 Labs team to get access to the onchain feed APIs.

More chains and graphs will be supported, with recent, real-time data of full historical transaction data.


# Upcoming Integrations

## **Reputation-based airdrops**

Using existing onchain and social graph data, communities and protocols utilize OpenRank to surface highly ranked or reputable contributors or participants. These rankings are based on reputation graphs, making it effective and transparent for airdrop allocations

## **Discover valuable users and apps**

Onchain transaction graphs originating from reputable users are used as inputs for OpenRank to compute highly ranked or valuable users and apps on a particular chain.

## **Retroactive Public Goods Funding**

To allocate public goods funding according to the impact generated by applicant projects, Openrank compute can be used to rank most valuable apps (see above use-case). In addition, reputation graphs based on domain-reputation-weighted expert votes can also be computed to generate project rankings (see MetaMask SPD use-case).

## **Spam Filtering and Personalized Recommendations**

Permissionless messaging protocols can use OpenRank to filter out spam and recommend reputable users, based on a variety of reputation graphs.

## **Dapp Stores and Explorers**&#x20;

Personalized recommendation of apps on decentralized apps store and block explorers. Social and onchain transaction-based graphs are used to sort apps based on a user’s network.

## **Personalized and Channel specific Feeds**

Powering community and channel specific rankings and feeds. In addition, personalized 'For You' feeds based on direct engagement reputation graphs.

## **Rating system for crowdfunding protocols**

Crowdfunding protocols can use a community-driven ranking system for filtering, sorting, and rating crowdfunding projects to safeguard users and creators navigating the platform.

## **Contribution ranking in OSS communities**

Open Source Software contribution graphs and dependency graphs benefit from open verifiable reputation ranking to surface contributions for the OSS community. Graph-based algorithms are used to capture peer-to-peer signals in this context.


# GitHub Developers & Repo Ranking

This ranking is seeded by Optimism OP Stack repos.  GitHub developers and repos are scored based on their reputation proximity to the OP Stack seed repos.

BigQuery tables: `openrank-data.devrank_2024{0627,0910}.{user,repo}_scores`

A graph of relevant GitHub event data from [Open Source Observer](https://www.opensource.observer/), covering more than 2,000 organizations and over 30,000 code repositories, was employed in generating these scores. We established two kinds of peers: GitHub users and repositories. We then used a variation of EigenTrust, the Hubs and Authorities algorithm, which computes rankings on an asymmetric bipartite trust graph. Below are the two different trust edges used in the graph:

* **User-to-repository trust** - this signals the user’s interest in the repository through actions such as stars, forks and issue/PR submissions.
* **Repository-to-user trust** - this signals the credit extended by the repository to the user for their contribution. Actions such as PRs merged, direct code commits received, and other maintenance actions.

**Note:**

1. There are two versions of the same dataset:
   * `openrank-data.devrank_20240627` has the scores used for [Optimism Retro 5 guest voter selection](https://gov.optimism.io/t/retro-funding-5-expert-voting-experiment/8613). It uses GitHub activities up until June 27th, 2024 as the input data.
   * `openrank-data.devrank_20240910` has the scores used for [Optimism Retro 5 metrics](https://gov.optimism.io/t/impact-metrics-for-retro-funding-5/8931). It uses GitHub activities up until September 10th, 2024 as the input data.
2. For historic reasons, the two score sets are not on the same scale. In order to compare, multiply the scores in the `openrank-data.devrank_20240627` dataset by 2.25014877694904 (user score) and 1.49998254193340 (repo score).

#### Rationale and Verification

Learn more about the rationale of the scores [here.](https://gov.optimism.io/t/retro-funding-5-guest-voter-selection-algorithm-explanation/8719)

Play with our tool <https://devrankings.openrank.com/> to understand the rationale and create your own rankings to verify it!


# Reputation Algorithms

OpenRank enables verifiable compute for a large class of reputation algorithms, in particular those that (a) operate on a graph, (b) are iterative in nature and (c) have a tendency toward convergence. In the following sections, we give a brief overview of some of these algorithms.

We will also step into the area of even wider family of algorithms that work towards minimising the error through backpropagation, which will give a glimpse of future directions for OpenRank.


# EigenTrust

A peer-to-peer reputation algorithm

[EigenTrust](https://nlp.stanford.edu/pubs/eigentrust.pdf) helps networked peers measure the level of trust placed on one another in a peer-to-peer network. The core idea behind EigenTrust is that a person’s reputation is defined recursively by the people who trust that person, weighted by those people’s reputations.

As a baseline, you can trust your friends. This gives you a good starting point, but because each person only has so many friends, it’s too limited to make a reliable system for millions of users/peers in a network. As a next step, you can expand that by asking your friends who they trust, and weighing their opinions by how much you trust your friends.

The linear algebra behind EigenTrust — you can initialize a trust vector with a set of seed peers that you trust. And then you can keep multiplying that vector by a matrix that represents the pairwise trust judgments of all the peers in the network. This is a power method algorithm, and it converges to the principal eigenvector of the matrix. Eventually you get complete coverage over everyone connected to you, directly or indirectly - in just a single eigenvector calculation.

### Local Trust (Reputation Graphs)

EigenTrust helps map trust from peer A to peer B in a graph. The primary input to EigenTrust is how much each peer directly trusts each other peer. These direct trust levels are called local trust values (LTVs), a function P(A, B) which translates to how much the peer A trusts peer B. Local Trust is not necessarily symmetric. Over the same set of interactions, A may trust B more than B trusts A. Hence, there are two local trust values: how much direct trust A places in B, and how much direct trust B places in A.

**Context of Local Trust is important**

The pairwise trust levels of peers need a related context. For example:

* Linear combination of engagement actions on a social network
* Tipping on an app to reward quality content
* Onchain token transfers
* Graphical NFT activities and ownership
* Peer attestations for being a reputable software developer

These contextual axes bear higher fidelity to specific activities than, say, a simple follower-based pairwise trust, and in general cater much better to users’ interests. We encourage developers to try to bring topical/fine-grained reputation graph data as well as more general/coarse-grained data.

#### **Modeling Distrust**

By default, EigenTrust revolves around degrees of trust above the neutral trust level: A local trust level of 0, for example, indicates a peer places the default amount of trust that they would in any other random peer in the network with which they have had no interaction at all. Trust levels cannot be negative; negative trusts are clipped at zero. This implements the untrusted-by-default model; the original EigenTrust research paper shows that this model is effective, for example, in detecting and thwarting Sybil attacks—a common use case for EigenTrust.

If an application does need a way of expressing/calculating the level of distrust below the neutral trust level, ex: where a somewhat-trusted-by-default model is needed, developers may want to experiment with the positive/negative karma model, where two EigenTrust scores are computed: One that revolves around trust above the neutral trust (zero is neutral trust, above zero is positive trust), and another below the neutral trust (zero is neutral trust, above zero is negative trust). Then the resulting scores may be combined to calculate the final trust score.

### Seed Trust

EigenTrust lets applications or services calculate trust scores tailored for a specific peer. For example, the trust opinions of peer’s immediate network neighbors and vicinity may matter more to the peer. Also, services may place context-specific trust in certain peers, e.g. those who share a common and/or clandestine relationship with the service.

To accommodate these use cases, EigenTrust lets services boost the importance of these pre-trusted peers, so that the opinions of their own matter more (and those of their vicinity too, the closer the stronger boost). For this, in addition to the local trust values for all peers in the network, EigenTrust accepts two more inputs:

* The **seed trust** that a peer and/or service places in certain other pre-trusted peers.
* The **seed confidence**—the strength of the seed trust, expressed as a percentage.

The seed trust by a peer may be distinct from the local pairwise trust that the peer places in other peers. For example, although the local trust by a peer may be derived solely from the interactions that the peer had with other peers, the seed trust may include other peers that the peer had no experience with but nevertheless knew to be trustworthy.

Good selection of seed trust helps achieve better rankings. Without any seed trust, each peer is by default assigned an equal amount of trust level (1/n), which is less Sybil resistant. Seed trust boosts trust levels of peers directly or indirectly trusted by them, and the seed confidence decides the level of this boost. By choosing peers (and their vicinity) generally known to be prudent not to trust Sybils as seed trust, one can effectively limit the impact of the Sybils in the system.

For details about how we can achieve permissionless verifiability of EigenTrust, refer to the original OpenRank paper.


# Hubs and Authorities

An alternative reputation algorithm for bipartite graphs

Hubs and Authorities  is a reputation algorithm that is similar to EigenTrust, but works for bipartite graphs (for example, a graph of people who like books; the edges are between people and the books they like, but not between people or between books). The algorithm defines a good book as a book that is liked by good readers. And it defines a good reader, recursively, as somebody who likes good books. Computing the resulting rankings involves a 2-step iterative process that computes the left and right principal singular vectors. Like EigenTrust, each iteration involves a matrix-vector multiplication, and the computation eventually converges.

It consists of 2 kinds of participants:

* Hubs - nodes that don’t contain any crucial information in the graph, and their job is mostly be a central place that contains information about other nodes, e.g. to point to other nodes in the network (keep a list of nodes that do contain useful information)
* Authorities - nodes that contain useful information

Nodes can take a role of both of these (can be both a Hub and Authority) or it can take a single role. The calculation of scores is done separately for these participants, but they are calculated based on the scores of opposing role:

* Scores for hubs is calculated from outgoing scores to authorities
* Scores of authorities are calculated with incoming scores of hubs

Unlike EigenTrust, the original weights (scores) of nodes (both hubs and authorities) are initialised to 1, meaning that they all have equal importance in the beginning. At later iterations, the importance of authorities is derived from the amount of "votes" received by the hubs. Then, the importance of hubs is derived from how many important authorities they voted for.

The scores of both hubs and authorities are normalised after each "voting process", i.e. after each iteration. One of the reasons is to prevent the scores from exploding into infinity.

The edges (connections) between nodes are described with the Adjacency Matrix $$A$$, and the update functions are defined as:

$$
h = A*a \\
a = A^T*h
$$

The most common normalisation function used is:

$$
h\_i^{(t+1)} = \frac{h\_i^{(t+1)}}{\sqrt{\sum\_i{(h\_i^{(t+1)})^2}}}
$$

The resulting scores, in our example case, should tell us who are good readers, and what are good books, which is mostly derived from the idea that good books are read from many readers, and good readers are the ones who read good books.

However, unlike EigenTrust, H\&A is easier to be taken advantage of by sybil actors, who can easily represent themselves as good readers by simply saying that they like books that are considered good by other peers, which is why further optimisations need to be made in order to use H\&A at scale.

For further detail on how H\&A computation can be verified, refer to the OpenRank paper.


# Latent Semantic Analysis

Collaborative Filtering using backpropagation

Collaborative Filtering is a method of extracting features that explain users personal preferences based on previous actions of that user, as well as every other user in the network. For example, when we want to find movies a person likes, based on the ratings of other people in the network. The most common way of feature extraction is by using Matrix Factorisation.

The goal is to find lower dimensional matrices P and Q such that the PxQ (Matrix Multiplication) results in a higher dimensional R. :

$$
\begin{bmatrix}
p\_{11} & p\_{12}  \\
p\_{21} & p\_{22} \\
p\_{31} & p\_{32}
\end{bmatrix}
\cdot
\begin{bmatrix}
q\_{11} & q\_{12} & q\_{13} \\
q\_{21} & q\_{22} & q\_{23}
\end{bmatrix}
=============

\begin{bmatrix}
r\_{11} & r\_{12} & r\_{13} \\
r\_{21} & r\_{22} & r\_{23} \\
r\_{31} & r\_{32} & r\_{33}
\end{bmatrix}
$$

This means that the matrix R, which represents direct ratings from all peer to all movies is reduced to 2 lower dimensional matrices, such that P represents the peoples preferences of movie genres, and Q represents how much a movie belongs to a particular genre (e.g. A movie can be an action movie that contains some elements of comedy)

In the real world the R matrix is sparse, meaning that people are not rating all the movies in the existence, instead that action happens rarely, and the point is to learn these representation from limited data.

The learning of feature matrices is achieved through backpropagation, by using gradient descent to iteratively update values of matrices P and Q until the desired error is achieved.

Calculating the prediction for one movie goes as following:

$$
r\_{11} = p\_{11}\*q\_{11} + p\_{12}\*q\_{21}
$$

After we do this for all the elements, the error function is calculated as following:

$$
E = \sum\_{i}{(R\_i - r\_i)^2}
$$

Where:

* $$R\_i$$ = True result taken from R matrix
* $$r\_i$$ = Result returned from a model
* $$E$$ = Total error for the whole network

Based on this error, the parameters (edges) are updated by taking the derivative of update function and changing the values of matrix elements such that it goes in the direction of reducing the error.

When the error is low enough, we consider the model to be fit.\
Based on the resulting P and Q matrices, we can infer/predict the missing ratings from the matrix R. That way we can recommend a user the movies that they haven't seen before.

Verifiability of Matrix Factorisation method, including wider class of GNNs is the next stage of evolution of OpenRank.


# Introduction

#### What is the OpenRank SDK?

This SDK allows developers to utilize OpenRank Protocol to create their own context-specific rankings and reputation graphs on any input data set. Developers can bring their onchain or offchain data and define their own parameters for the compute. They can then run a variety of reputation algorithms to compute rankings and reputation scores.&#x20;

#### How does OpenRank SDK work?

The OpenRank SDK provides a set of ranking and reputation algorithms. v1 offers EigenTrust compute, v2 will support Hubs & Authorities, Collaborative Filtering and Matrix Factorization. These algorithms help generate ranking and reputation scores for any intended object such as users (EOAs), smart contracts, DIDs, entities. Refere to Examples for more details.&#x20;

In v1, OpenRank supports [EigenTrust](https://nlp.stanford.edu/pubs/eigentrust.pdf), which helps compute the reputation of participants in a peer-to-peer graph. The nodes in a graph can be users, wallets, or any object that is to be ranked. The edges between nodes represent the trust heuristic from one node to another. For detailed information regarding how EigenTrust works, [read here](/reputation-algorithms/eigentrust).

### Get started:

[Installation](/openrank-sdk/installation)

[Creating your first reputation graph](/openrank-sdk/creating-your-first-reputation-graph)


# Installation

#### Prerequisites

Before you start, ensure you have the following prerequisites:

* git cli (<https://git-scm.com/downloads>)
* git lfs extention (<https://git-lfs.com/>)
* curl (<https://curl.se/download.html>)

To install OpenRank SDK binary, run:

{% hint style="warning" %}
Only MacOS and Linux systems are supported. Windows support is coming soon!
{% endhint %}

```
curl -fsSL https://raw.githubusercontent.com/openrankprotocol/openrank-tee/main/scripts/install.sh | bash
```

This will install `openrank` cli globally on your machine. To verify that installation is done correctly, run:

```
openrank --version
```

Now, we can setup our workspace and start ranking:

[Creating your first reputation graph](/openrank-sdk/creating-your-first-reputation-graph)


# Creating your first reputation graph

This is a getting started guide that lets you create a set of rankings (trust graph) for a sample dataset provided

{% hint style="warning" %}

#### NOTE:  OpenRank protocol is currently in private testnet phase. In order to be allowlisted, please contact Karma3 Labs team for more details at <mark style="color:green;background-color:$warning;"><hello@karma3labs.com></mark>.

{% endhint %}

#### Initialize the workspace

\`openrank\` provides a special command that can help you get started with setting up your workspace:

```
openrank init ./my-workspace
```

This will initialize your first workspace folder that has the following structure:

```
/my-workspace
    /trust
        degen.csv
        jamfrens.csv
        openrank.csv
        small.csv
    /seed
        degen.csv
        jamfrens.csv
        openrank.csv
        small.csv
    .env
```

As you can see, the workspace consists of 2 folders and the .env file.

The file names in the /trust and /seed folder have to be identical in order for \`openrank\` to pair them and request compute for them.

The format of local trust files is in `i,j,v` . This can be inspected by:

```
head -n 5 trust/degen.csv
```

Which should show this output:

```
i,j,v
481656,419388,30
2211,253127,50
469501,355836,10
16565,461286,110
```

These rows represent a local trust values between two peers, specifically FID to FID (Farcaster ID). These scores are derived by Karma3 Labs team specifically for demo purposes.

The format for seed trust files is in `i,v` . This can be inspected by:

```
head -n 5 seed/degen.csv
```

Whic should show this output:

```
i,v
15357,0.7950587070543446
277952,0.199664017398543
309242,0.003994120331468955
248216,0.00027744247206043824
```

These rows represent a seed trust values for a given peer `i` . These scores are derived from well known reputable users in Farcaster ecosystem.

#### Setting up the mnemonic phrase

Before requesting compute jobs, we need to set up our mnemonic phrase, from which the \`openrank\` will create a wallet in order to make TXs. Edit the following variable in .env:

```
MNEMONIC="add your mnemonic here"
```

<mark style="color:$warning;">NOTE: Before requesting compute jobs, make sure you address corresponding with this mnemonic is allowlisted, by contacting Karma3 Labs team (<hello@karma3labs.com>).</mark>

#### Requesting compute jobs

To request a compute job, simply run:

```
openrank compute-request ./trust ./seed
```

And your request will be submitted into OpenRankManager smart contract ready to be processed by the compute nodes. After the request is successfully submitted, you will se a compute id logged into the console.

You can also get the compute metadata using the following command:

```
openrank compute-watch [compute-id]
```

This command will return compute request and compute results TX hash on L1 Sepolia testnet.

For downloading and verifying computed scores:

[Download Rankings with OpenRank SDK](/openrank-sdk/download-rankings-with-openrank-sdk)


# Download Rankings with OpenRank SDK

Downloading the rankings after running the OpenRank compute request

OpenRank allows you to download scores from any historical compute job done by the compute nodes. In order to downloading the scores, use the following command:

```
openrank download-scores [compute-id] --out-dir="./scores"
```

This will download scores computed in job with id: \[compute-id], and save it into `./scores`  folder. The folder structure corresponding to the template datasets:

```
/scores
    degen.csv
    jamfrens.csv
    openrank.csv
    small.csv
```

Each file in /scores folder corresponds to each file in /trust folder for this specific compute job.

#### Verifying the results

`openrank`  gives you an option to verify these scores locally, with following command:

```
openrank verify-local ./trust/degen.csv ./seed/degen.csv ./scores/degen.csv
```

This command will run one iteration of EigenTrust against the given scores and output the verification result in console.

For more information about the CLI tool and protocol in general, see our GitHub readme:\
<https://github.com/openrankprotocol/openrank-tee/blob/main/sdk/README.md>\
<https://github.com/openrankprotocol/openrank-tee/blob/main/README.md>


