POLTech: Proof of Liquidity goes Social

Beary Cucumber
Beary Cucumber
5 min read
POLTech: Proof of Liquidity goes Social

friend.tech, despite how you might feel about it, has redefined how we engage with content and the monetary value we ascribe to influencers' attention and insights.

We introduce POLTech, which integrates friend.tech's social trading elements into Berachain's Proof of Liquidity (PoL) paradigm. POLTech showcases the adaptability of PoL beyond the typical "stake your ERC20 receipt token to earn BGT". Interested? Let's dive in.

Overview

In this article, we walk through the following:

  1. How the POLTech app works and its features
  2. A deep dive into the smart contracts intrinsically linking POLTech's share trading with PoL staking

POLTech App Walkthrough

Check out the app here: https://poltech.vercel.app/

Share Trading

Buy and sell shares associated with specific addresses using BERA tokens.

Automatic Staking for BGT

Every share purchase is automatically staked in a Rewards Vault, granting eligibility to BGT rewards.

Claim the BGT which has accrued to your account from your share purchases on the Leaderboard page.

Chat

Owning an address' shares entitles users to enter an exclusive chat with them and everyone else who owns shares.

Pretty cool right? Give this a spin at https://poltech.vercel.app/

POLTech Smart Contracts

The astute reader might be asking - "wait the demo user didn't stake in a Reward Vault, how are they earning BGT??"

Very reasonable question, because in many cases, users expect to have to stake their assets on the Vaults dashboard. But less is known about the feature where smart contracts can stake on behalf of users.

Contracts can use the delegateStake function on Reward Vaults to manage their users' PoL staking positions

The following diagram illustrates how the different elements interact:

Users can see the live POLTech contract here: https://bartio.beratrail.io/address/0x2B1B76961C0e38DD79FEf76611f71465d4b0B817/contract/80084/code

Automatic Staking with delegateStake

The POLTech constructor creates a new dummy staking token, which the POLTech contract has control over, and whose sole purpose is to represent its users' BGT staking positions.

Below, we see how the POLTech contract creates a new PoLStakingToken, and subsequently a Reward Vault from the Factory:

  constructor() {
      address vaultFactory = 0x2B6e40f65D82A0cB98795bC7587a71bfa49fBB2B;

      stakingToken = new PoLStakingToken(address(this));

      polVault = IBerachainRewardsVault(
          IBerachainRewardsVaultFactory(vaultFactory).createRewardsVault(
              address(stakingToken)
          )
      );
  }

When a user buys shares, the contract automatically stakes the equivalent value in the Vault (in the buyShares function):

  // Mint and stake according to the value of shares bought
  stakingToken.mint(address(this), buyPrice);
  stakingToken.approve(address(polVault), buyPrice);
  polVault.delegateStake(msg.sender, buyPrice);

The PoL entitlement buyers get (through delegateStake) is dictated by the buyPrice (how much BERA they paid for the shares).

Let's quickly explore how share prices work on POLTech...

Dynamic Pricing Mechanism

The POLTech contract implements a bonding curve for share pricing, so that price scales with demand:

function getBuyPrice(
    address subject,
    uint256 amount
) public view returns (uint256 totalCost, uint256 endPrice) {
    uint256 price = getSharePrice(subject);

    totalCost = 0;
    for (uint256 i = 0; i < amount; i++) {
        totalCost += price;
        price = price + PRICE_CHANGE_FACTOR; // Each purchase pumps price
    }

    endPrice = price;
    return (totalCost, endPrice);
}

Just Reverse this Logic When Selling Shares Right?

To quickly recap:

  • The price of an address' shares go up the more people that buy it
  • The more you spend buying shares, the more you get staked on your behalf in the Vault

The opposite is true where selling makes share prices go down. So prices are expected to go up and down, creating a dynamic market for attention.

So what happens to your Vault/PoL stake if you sell your shares when the price is higher or lower than when you purchased?

Well that's a problem, because we can't just simply unstake the amount someone gets back from a sale. They might have paid a less for those shares after all!

FIFO Lot Tracking

To solve the problem of tracking PoL stakes as share prices fluctuate, POLTech uses a FIFO lot tracking system to maintain records of what each user initially paid for their shares:

struct Lot {
    uint256 shares;
    uint256 stakedAmount;
}

mapping(address => mapping(address => Lot[])) public shareLots;

// When buying shares (buyShares):
shareLots[msg.sender][subject].push(
    Lot({shares: amount, stakedAmount: buyPrice})
);

// When selling shares (sellShares):
while (sharesToSell > 0) {
    Lot storage lot = shareLots[msg.sender][subject][0];
    // Process the oldest lot first
    if (lot.shares <= sharesToSell) {
        // Sell entire lot
        totalStakeToWithdraw += lot.stakedAmount;
        sharesToSell -= lot.shares;
        removeLot(msg.sender, subject);
    } else {
        // Sell part of the lot
        uint256 partialStake = (lot.stakedAmount * sharesToSell) / lot.shares;
        totalStakeToWithdraw += partialStake;
        lot.shares -= sharesToSell;
        lot.stakedAmount -= partialStake;
        sharesToSell = 0;
    }
}

// After calculating totalStakeToWithdraw (still in sellShares)
polVault.delegateWithdraw(msg.sender, totalStakeToWithdraw);
stakingToken.burn(address(this), totalStakeToWithdraw);

The FIFO lots work as follows:

  1. When shares are bought, a new Lot is created and added to the user's array of lots for that subject/address. Each Lot stores:
    1. the number of shares bought; and
    2. the amount staked (corresponding to the purchase price)
  2. When selling shares, the contract processes the oldest lots first (FIFO)
  3. If a Lot is fully consumed, it's removed. If only part of a lot is sold, the remaining shares and staked amount of that Lot are updated
  4. This process continues until all shares to be sold are accounted for, finally calling delegateWithdraw with the totalStakeToWithdraw

Your accounting professor would be pleased.

Conclusion

If you've made it this far, you've gained valuable insights into how POLTech cleverly integrates Proof of Liquidity within a SocialFi context. Let's recap the key points we've covered:

  • Automatic Staking: delegateStake is powerful for automatically managing users' PoL stakes. Conversely use delegateWithdraw when users leave/sell
  • FIFO Lot Tracking: POLTech accurately tracks users' stakes even as share prices fluctuate

More examples on how PoL can be adapted to other interesting applications coming soon!


đŸ» Full Code Repository

If you want to see the final code and see other guides, check out POLTech Code.

GitHub - larrythecucumber321/poltech
Contribute to larrythecucumber321/poltech development by creating an account on GitHub.

đŸ› ïž Want To Build More?

If you’re looking to dive deeper into the details, take a look at our Berachain Docs.

Berachain Docs
Berachain protocol core docs for learning how the blockchain works, developer guides, and understanding how to manage


Looking For Dev Support?

Make sure to join our Berachain Discord server and check out our developer channels to ask questions.

❀ Don’t forget to show some love for this article đŸ‘đŸŒ