Understanding LensAPI-Oracle By Phala Network

Keshav SharmaKeshav Sharma
8 min read

Oracles play a crucial role in obtaining off-chain data and delivering it to contracts, offering them true "Smart" Contract capabilities. An Oracle service tries to act as a bridge between smart contracts and off-chain data providers. Phala Network attempts to revolutionize how an oracle works. Phala Network is more of a decentralized infrastructure computation network that provides off-chain computation to smart contracts. The results can be verified on-chain, hence making them trustless and verifiable. This article attempts to emphasize various ways you can interact with the contract in Phala Network and also gives a demonstration regarding one of the pathways to interact with an example of LensAPI-Oracle, a way to bring information about Lens through their APIs to the smart contracts.

Phat Contracts

The advantage bestowed upon developers through the utilization of "Phat Contract" sets this network apart. This assertion finds its basis in the array of features it strives to offer to developers:

  • Extensive blockchain compatibility

  • The ability for Phat contracts to seamlessly integrate data from any web API onto the blockchain, regardless of whether it's on-chain or cross-chain

  • Enabling the execution of complex off-chain logic, effectively functioning as an infrastructure service

  • Ultimately, ensuring every computation is both trustless and verifiable, thereby reinforcing the concept of a decentralized infrastructure network.

The purpose of the Phat Contract is to do off-chain computation as well as interact with various blockchains. The pathways to achieve this is to use:

  • Phat Bricks: A no-code solution to use ready-made, audited phat contracts, i.e. Blueprints, to perform a bundle of ordinary functions through an easy-to-use UI.

  • Phat Contracts: An off-chain computation unit that gives more power to Smart Contracts making them wiser, i.e. "Smarter" Contracts πŸ˜‰. Having a decent understanding of either rust, typescript, javascript, or ink! can help you create these smarter contracts.

In short, Phat contracts are not a competition to smart contracts but give them more power to create use cases that were not possible for factors such as the inability to do computation, fetch off-chain data, or trust off-chain data providers.

LensAPI-Oracle: A Phat-Bricks Blueprint

Now, as we have a general idea regarding the purpose of a Phat Contract, comprehending how it functions will make the picture crystal clear. Although the scope of this article is only to give you a teaser about what is a phat contract and how it works. So, we will create a solidity smart contract that gets total followers, total posts, total comments, total mirrors, and total collects of a Lens Profile and then we give an NFT to the Lens Profile Handle owner if it completes certain checkpoints in terms of these five parameters.

Let's see how to use Phat-Bricks to create a blueprint in a few clicks.

PhatBricks Testnet - bricks-poc5.phala.network

*Requirements: a polkadot{.js} wallet extension, test-net PHA and clone this repo - lensapi-oracle-consumer-contract.

Phat-Bricks user guide

Once you set up your wallet and have enough test-net PHA,

Step 1: Click + Start fast with a blueprint

Step 2: Click LensAPI Oracle

Step 3: Scroll through the whole Introduction about LensAPI Oracle and then click on the Next button at the end.

Step 4: Provide a relevant name associated with your project so that it's easier for you to recall.

Step 5: You can customize your transform function or select one of the features in JSON Path.

Step 6: This is the review page so that you can review your field or transform function.

Step 7: Stake your test PHA token for computation resource. It is recommended to stake at least 20 tokens.

Step 8: After clicking Deploy, wait and sign until all the instantiation, and configuration process is complete.

And, you have just deployed a Phat-Bricks Blueprint. Cheers!πŸ₯‚

But what to do with this?

What to do with this Oracle?

Honestly, you can do a lot with this oracle as it provides 7 different feature stats about a given Lens Profile. Not to you! You can easily get it by going to Lenster but your contract can use this information to perform a certain task. Now the task depends on the smart contract but this information is what makes this whole process so unique.

There are endless possibilities to build something useful out of this information. A couple of examples you can build with this oracle are:

  • Provide an NFT to the Lens Profile that reaches a certain milestone, for example, 100,000 followers, 1000 total likes, etc.

  • Create groups with a certain level of qualification in terms of followers, posts, collects, etc. which can also be used as a gate.

To prove our point, let's try to build the first idea and bring it to fruition.

PokeLens

The idea is to create a smart contract that rewards you with an evolved NFT if you accomplish the checkpoints. There are three checkpoints - Level 0, Level 1 & Level 2.

  • Level 0, i.e. Pichu is the bare minimum that can be claimed by an account if the Lens Profile has less than 10 total followers, 50 total posts, 50 total comments, 10 total mirrors, and 10 total collects.

  • Level 1, i.e. Pikachu can evolve from the prior if the profile is in the range of 10-50 total followers, 50-100 total posts and comments, and 10-20 total mirrors and collects.

  • Level 2, i.e. Raichu can be acquired if the profile is above 50 total followers, 100 total posts and comments, and 20 total mirrors and collects.

Pokemon Evolution

The Transform Function: The JS Way

Generally, the easiest way to get all the features from the API Oracle is to create instances for each and every feature and then create a request function for each and every API Oracle in the contract. But the new transformative way that we can try for this project is:

function transform(arg) {
  let input = JSON.parse(arg);
  const totalFollowers = input.data.profile.stats.totalFollowers;
  const totalPosts = input.data.profile.stats.totalPosts;
  const totalComments = input.data.profile.stats.totalComments;
  const totalMirrors = input.data.profile.stats.totalMirrors;
  const totalCollects = input.data.profile.stats.totalCollects;

  let finalNumber = "";
  const features = [totalFollowers, totalPosts, totalComments, totalMirrors, totalCollects];

  for (const feature of features) {
    const featString = String(feature).length;
    finalNumber += featString;
    finalNumber += String(feature);
  }

  return Number(finalNumber);
}

Now, the new thing, about this particular implementation is that it stores the number of digits of every feature and concatenates the feature value next to it.

Example: Total Followers: 10, Total Posts: 15, Total Comments: 40, Total Mirrors: 5, Total Collects: 1

Result: 2-10 2-15 2-40 1-5 1-1 => 2102152401511

This can really come in handy to fetch all the features at once. But the only catch is that this is javascript and we all know that conversion of big-number string to number leads to inaccuracy as the limit to number is 253-1 i.e. +/-9,007,199,254,740,991. As shown in the above example, such a small number of followers led to such a huge number because we concatenated to get to the result and not storing it as a string because the Blueprint does not allow us to do so.

The Decryption Function: The Solidity Way

The below given solidity function might not be optimized but works just fine. It can show inaccurate results due to the value of the data it receives from the oracle as the transform function in the prior segment. But it can really help you get all the data under a single roof. If one-day strings are also allowed by the Blueprint, this can make getting the features a cakewalk.

struct Metrics {
    uint256 totalFollowers;
    uint256 totalPosts;
    uint256 totalComments;
    uint256 totalMirrors;
    uint256 totalCollects;
}

// convert number to an array of digits
function digitArray(int256 _number) public pure returns (int256[] memory) {
    int256 num = _number;
    int256 no_digits = 0;

    while (num > 0) {
        num /= 10;
        no_digits++;
    }

    num = _number;
    int256[] memory numberArray = new int256[](uint256(no_digits));
    for (int256 i = no_digits - 1; i >= 0; i--) {
        int256 digit = num % 10;
        numberArray[uint256(i)] = digit;
        num /= 10;
    }

    return numberArray;
}

// decode encrypted value back to struct metrics
function decode(uint256 _number) public pure returns (Metrics memory user) {
    int256 num = int256(_number);
    int256[] memory numberArray = digitArray(num);
    uint256[5] memory decoded;
    int256 offset = 0;

    for (int256 i = 0; i < 5; i++) {
        int256 numLength = numberArray[uint256(offset)];
        offset += 1;
        int256 extractedValue = 0;

        for (int256 j = offset; j < offset + numLength; j++) {
            // get the least significant digit and add to the number
            extractedValue = extractedValue * 10 + numberArray[uint256(j)];
        }

        decoded[uint256(i)] = uint256(extractedValue);
        offset += numLength;
    }

    user = Metrics(decoded[0], decoded[1], decoded[2], decoded[3], decoded[4]);
}

The Consumer Contract Segment

Now, we are ready with the encryption and the decryption functions. Now, comes the contract part. To understand consumer contracts, let's understand how the data relays from our consumer contract to Phala Network as a request and then come back to us as a MetaTx or a response to keep it simple.

If you have cloned the repo given in the requirements, there are three contracts apart from TestLensApiConsumerContract.sol,

  • MetaTransaction.sol

  • PhatRollupAnchor.sol

  • TestPriceReceiver.sol

These three contracts act as an off-chain Phat Rollup to generate requests, queue them, and return them when received.

in TestLensApiConsumerContract.sol , Here's where you write all your solidity,

function _onMessageReceived(bytes calldata action) internal override {
    require(action.length == 32 * 3, "cannot parse action");
    (uint respType, uint id, uint256 data) = abi.decode(
        action,
        (uint, uint, uint256)
    );
    if (respType == TYPE_RESPONSE) {
        emit ResponseReceived(id, requests[id], data);
        // Run your NFT Mint function here along 
        // with the decrypt function & logic!
        delete requests[id];
    } else if (respType == TYPE_ERROR) {
        emit ErrorReceived(id, requests[id], data);
        delete requests[id];
    }
}

Here's the gist of everything there is to Phat-Bricks and LensAPI Oracle Blueprint. There is a lot more to Phat-Contracts though which we will try to cover some other day.

Conclusion

The main reason behind this article was to give a quick introduction to how Phat Contracts can really help you fetch data in seconds. Still, if you want to have a look at the contract for the implementation of PokeLens: you can click here. It's still not ready, but it can give you the inspiration to build your own.
If you like this article, you give the article a like and share it with anyone who wants to learn about Phala Network.

Feel free to reach out to me if you have any inquiries or if there are any corrections needed that I might have misunderstood.

Here's my twitter link: skeshav25

CaioπŸ‘‹πŸ»

1
Subscribe to my newsletter

Read articles from Keshav Sharma directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Keshav Sharma
Keshav Sharma