Skip to main content

โœจ Your First Yield Strategy

This workshop will guide you through building a simple yield strategy using the APWine SDK. We will go through different interactions with both the Protocol and AMM components, from depositing an interesting bearing token (IBT) to selling future yield (FYT).

The source code of this workshop is available on github.com/ulydev/apwine-workshop, for you to follow along.

๐Ÿž Presentation here

Before we start#

You will need npm and yarn available on your system.

  1. Clone the repository and navigate to the project:
git clone https://github.com/ulydev/apwine-workshopcd apwine-workshop
  1. Copy the example environment file:
cp .env.example .env
  1. Install dependencies, then run local Hardhat node:
yarn installyarn start-node
tip

At any time during the workshop, you can run yarn step-[1-5] to run the current step locally.

Strategy#

The strategy we will use will allow use to get an optimised fixed rate on ETH through the following steps:

  1. Tokenise stETH (Lido Staked ETH) into PT and FYT
  2. Get future yield in advance and reinvest it into a fixed rate (sell FYT for PT)
note

This workshop is based on a fork of Mainnet at block 14200000 so that it can be easily replicated. If you're building a bot for executing the strategy in real time, you will likely want to use the latest block.

Step 1: SDK Creation#

The APWine SDK can directly be imported into your environment of choice, and created as such:

import APWineSDK from "@apwine/sdk"
// create providerconst provider = new ethers.providers.JsonRpcProvider("http://localhost:8545")
// create and initialize SDKconst sdk: APWineSDK = new APWineSDK(    {        provider,        signer: new ethers.Wallet(PRIVATE_KEY, provider),        network: 1, // mainnet    },    { initialize: false })await sdk.initialize()

That's it! You're now ready to use the SDK.

Step 2: Retrieve Futures#

You can get the addresses of all deployed future vaults using sdk.fetchAllFutureVaults(), then retrieve detailed information for each using sdk.fetchFutureAggregateFromAddress():

// Fetch all future vaults addressesconst futureVaults = await sdk.fetchAllFutureVaults()
// Fetch detailed future data (more resource-intensive)const futures = await Promise.all(    futureVaults.map((futureVault) =>        sdk.fetchFutureAggregateFromAddress(futureVault.address)    ))
note

This is equivalent to sdk.fetchAllFutureAggregates()

Step 3: Get basic APR#

The easiest way to get a fixed rate on APWine is to buy PT with the underlying asset. Thus, we will start by getting the spot price of the PT/underlying pair on the AMM:

const stETHVault = futures.find((future) => future.ibtAddress === STETH_ADDRESS)const price = await sdk.fetchSpotPrice(stETHVault, from, to)

This gives us a price PT/ETH < 1, meaning that we can make a profit by buying undervalued PT at a discount and waiting till period expiry to redeem PT for ETH at a 1:1 ratio. If the price is 0.666, we can make a direct 50% profit since we will be able to buy e.g. 3 PT for only 2 ETH, and redeem 3 ETH when the next period begins.

In our case, we get 1.0196 PT per ETH, equivalent to 1.96% over 20 days or 35.72% (!) annualized.

caution

Spot price is not equivalent to the final price. Depending on the available liquidity, there will be more or less substantial slippage to be taken into account. In the next steps, we will see how we can leverage both PT/Underlying and PT/FYT liquidity to get a similar result but with much higher trade volumes - hence much better capital efficiency.

Step 4: Tokenise stETH into PT and FYT#

Now, instead of directly buying PT with our ETH, we are going to:

  • Deposit 10 ETH on Lido Finance to get stETH (Staked ETH)
  • Deposit stETH on APWine to get PT and FYT
  • Trade FYT for PT on the AMM

First, we want to convert our ETH to stETH (an interest-bearing token):

await sdk.signer.sendTransaction({    to: STETH_ADDRESS,    value: parseEther("10"),})

We get the same amount of stETH (1 ETH = 1 stETH).

Once done, we simply call the deposit method on the SDK, and enable automatic approval (this will require an additional transaction).

await sdk.deposit(stETHVault, parseEther("10"), { autoApprove: true })

And there we have it! If everything went well, we now have PT and FYT in our wallet. With just one line, we extracted the future yield of our stETH!

Step 5: Trade FYT for PT on the AMM#

To trade on the APWine AMM, we will use the AMM Router, which allows multiple trades to be executed in one transaction. This means we can also swap between FYT and ETH in a single transaction, although they are present on two different pairs (PT/Underlying and PT/FYT). The SDK abstracts all this for you with simple swapIn/swapOut methods.

const fytAddress = await stETHVault.getFYTofPeriod(1) // We are currently at the first period (starting from index 1)const fytBalance = await balance(sdk.provider, fytAddress, user) // Equivalent to ERC20.balanceOf()await sdk.swapIn(    {        amm: await sdk.fetchAMM(stETHVault),        from: "FYT",        to: "PT",        amount: fytBalance,    },    { autoApprove: true } // Approve automatically if needed)

After the trade, we end up with 10.36 PT, from an initial 10 ETH investment. This is equivalent to a profit of 0.36 ETH during the period, or a 34.38% APR!

The main upside of this strategy is that it allows for much bigger volumes to be traded (10 times more liquid on average).

Now, all that's left is to wait for the end of the current period, and you will be able to withdraw your stETH by burning the PT and FYT.