Open Action | Rewards Swap

Introduction

Rewards Swap lets you create incentivized swaps for your token. To start you need a UniswapV3 poool for the token that you want to shill. Then you can create a Shill2Earn pool by transferring in tokens to be disbursed as rewards and setting some parameters

Now a user can initialize the action on their post by passing in the index of the rewards pool to point to. They can also opt to share some of the rewards with the swapper. When the action is processed the user will pass in the Uniswap path to swap in through and the swap is called on the Uniswap Router. Rewards are doled out to the publication owner, the acter (if the owner has opted to share rewards), and the first profile in the mirror referrals array, if there is one.

You can also select a token when initializing your post that doest not have a pool. In this case the post creator will get .25% of incoming swaps as a reward.

Users can still act on the publication even after the rewards pool is used up and they will be able to swap but no rewards will be given out.

The post creator can opt to share some of their reward with the client also. This is done by setting the sharedRewardPercent in the initializePublicationAction call. When acting on the publication the client can inject their recipient address into the action module data and the specified percent of the reward will be sent to them.

Handler

The @madfi/lens-oa-client npm package exports a class called RewardsSwapAction that can be used to simplify interactions with the Rewards Swap Lens Open Action.

Instantiate class

The class needs to be instantiated with a environment config (testnet or production), the profile id and publication id of the publication to act on and then the authenticated profile id of the actor.

import { production } from "@lens-protocol/client";
import { RewardsSwapAction } from "@madfi/lens-oa-client";

const handler = new RewardsSwapAction(production, "0x0e76", "0x023a", "0x0e76", {
    137: process.env.NEXT_PUBLIC_POLYGON_RPC!,
});

Helper Methods

There are several helper methods that can be used to access relevant data.

  • Get active reward pools: returns rewards pools that a new publication can be pointed to to begin earning swap rewards

const activePools = await handler.getActiveRewardsPools();
  • Get user balances: returns the balances of some common quote tokens on Polygon (WMATIC, USDC, USDT, DAI, WETH) for a given wallet address

const balances = await handler.getUserTokenBalances("0xDC4471ee9DFcA619Ac5465FdE7CF2634253a9dc6");
  • Get the params for the post being acted on: returns the params for the specified post given profile and pub ids

const postData = await handler.getPostData("0x0e76", "0x023a");
  • Get splits from the token out: returns the token amounts that go to each party based on the amountOut value of a swap. The args can be determined from the params of the post and the params of the rewards pool (if there is one, otherwise from the direct promotion params specified in the post). Reward pool params can be passed in as zero for direct promotions and they will be ignored.

  /**
   * Returns the token amounts that go to each party
   * @param amountOut The amount received from the swap
   * @param isDirectPromotion True if no rewards pool is used
   * @param percentReward The percent of amountOut being distributed as reward from reward pool
   * @param cap The max amount of rewards to distribute per tx from reward pool
   * @param remainingRewards The remaining rewards in the reward pool
   * @param sharedRewardPercent The percent of their reward the poster shares with swapper
   * @param isReferral Whether this post was a mirror referral or not
   * @param hasClient Whether this post has a client address defined or not
   *
   * @return Amounts going to poster, swapper, referrer and client
   */
  async getSplitsTokenOut(
    amountOut: bigint,
    isDirectPromotion: boolean,
    percentReward: bigint,
    cap: bigint,
    remainingRewards: bigint,
    sharedRewardPercent: bigint,
    isReferral: boolean,
    hasClient: boolean
  ): Promise<any>
  • Get splits from the token in: there is only one split that happens on the token in, and that is the protocol fee. Call this function to get the exact value

const amountIn = await handler.getSplitsTokenIn(BigInt("1000000000000000000"));
  • Create a new rewards pool. Requires token allowance to be approved on the Rewards Swap contract first in order to be transferred into the pool

/**
   * Creates a rewards pool
   * @param walletClient Viem wallet client to sign tx
   * @param token The token to swap for and distribute rewards for.
   * @param rewardsAmount The total amount of rewards to bew distributed.
   * @param percentReward The percent of each swap to distribute as rewards bps i.e. 1000 = 10%.
   * @param percentCap The max amount of rewards to distribute per tx, as a percent of the total rewards amount.
   * Direct amount is calculated as percent of rewardsAmount and stored as a uint256.
   * @param profileId The profile id of a MadFi club to receive points on (optional)
   */
  async createRewardsPool(
    walletClient: WalletClient,
    token: string,
    rewardsAmount: bigint,
    percentReward: bigint,
    percentCap: bigint,
    profileId: bigint = BigInt(0)
  )

Encode Module Data

  • Encode module init data:

const encodedInitData = handler.encodeModuleInitData({
  isDirectPromotion: false, // not a direct promotion
  sharedRewardPercent: 50_00, // share 50% of reward with swapper
  recipient: "0xDC4471ee9DFcA619Ac5465FdE7CF2634253a9dc6", // rewards to this address
  rewardsPoolId: 1, // rewards pool id number 1
  token: zeroAddress, // only needed for direct promotions
});
const encodedActData = handler.encodeModuleActData({
  path: [WMATIC_ADDRESS, 10000, BONSAI_ADDRESS], // swap WMATIC to BONSAI through the 1% fee pool
  deadline: Math.floor(Date.now() / 1000) + 20 * 60, // 20 minutes from now
  amountIn: "1000000000000000000",
  amountOutMinimum: 0, // set this to an expected min value to avoid MEV attacks
  clientAddress: CLIENT_TREASURY_ADDRESS, // client can inject their own address to earn from hosting the action
});

If you want to encode the init or act data directly here is what you'll need:

// init data types and descriptions
(
    bool isDirectPromotion, // promoting a token directly, no rewards pool
    uint16 sharedRewardPercent, // percent of reward to share with publication actor (0 - 10000)
    address recipient, // address to receive rewards, most likely profile owner 
    uint256 rewardsPoolId, // id of rewards pool to point to, 0 if direct promotion
    address token // token address to promote if direct promotion, ignored otherwise and can be zero address
)
// act data types and descriptions
(
    bytes memory path, // swap path i.e. bytes memory path = abi.encodePacked(address(inputToken), uint24(500), token);
    uint256 deadline, // deadline to complete swap. 20 minutes is typically good
    uint256 amountIn, // amount of input token to swap in base decimal unit
    uint256 amountOutMinimum, // minimum amount out to receive. This can be estimated from the Uniswap router, it can also be 0 (at risk of getting MEV'd)
    address clientAddress // [OPTIONAL] address to receive client reward
)

Deployments

Testnet

https://mumbai.polygonscan.com/address/0x8a3fFD86C4409Eb3c3b94DCC5219024CCf6C6179

Mainnet

https://polygonscan.com/address/0x3394E78a3389b1f0216F30fA0613f4975D0573C3

Last updated