🔌On-Chain Integration for Flexible Term Vault (V4)

This page provides information on how to access full documentation on the flexible term vault contract, using the Flexible Term USDC Vault deployed on Ethereum as an example

Getting Started

To integrate to a loan vault contract, you can start by viewing the contract source code on Etherscan, which will provide you the contract source code, a list of functions and their descriptions, as well as all of the libraries, interfaces, factories, and controllers.

Flexible Term Vaults implement the OpenZeppelin ERC-1967 Upgradeable Beacon Proxy Pattern.

View the Proxy Contract Source Code

Read as Proxy, including all the read functions

Write as Proxy, including all the write functions

View the Implementation Contract Source Code, including the source code, interfaces, factories, controllers, and libraries

Flexible Term USD Vault Product Documentation

Using Etherscan to View Documentation

Important Functions

As a Lender, there are only a handful of functions you will ever need to call. They are to

  1. Make Investments

  2. Request Withdrawals

  3. Read contract data

These three are covered in more detail below. There are quite a number more in detailed in the full code, libraries, and interfaces on Etherscan but we present the most important below.

Flexible Term Vaults implement the ERC-1967 Beacon Proxy Pattern. The only implementation contract you will interact with as a Lender is the PoolFlex contract.

Terminology

OpenTrade implements many of the ERC-4626 and ERC-20 standards. In some cases, the terminology we use in the product is different from those in the code. They are primarily

Term in Code
Display Name

Pool

Vault

Shares

Vault Tokens

Assets

USDC, EURC

Key Data Fields and their Decimals / Formatting

Field
Decimals
Example in Code
Example in Display

assets

6

1000000

1 USDC/EURC

shares

6

1000000

1 Vault Token

exchangeRate

18

1000261149376738066

1.000261

interestRate

2

500

5.00%

1. Make Investments

The prerequisites for making an investment on mainnet are:

1) to have been onboarded to OpenTrade

2) to have had your supported digital asset wallet whitelisted and

3) to have have enough ETH in the wallet to pay for gas.

To make an investment, you must first call the function to create a spending allowance. This is the amount the the caller is authorising the pool contract to spend.

function approve(address spender, uint256 amount) external returns (bool)

...where amount is the number of assets you'd like to approve to be deposited and address is the wallet address of the lender. It will return a boolean, true if the request succesful, false if the request failed.

Once the allowance is approved, you will then need to call the function

   function deposit(
        uint256 assets,
        address lender
    ) external returns (uint256 shares, uint256 transferInDayTimestamp);

where assets is the number of assets you'd like to deposit and lender is the wallet address of the lender making the deposit.

To see a demo of this working in action, you can visit our product documentation here.

2. Request Withdrawals

There is only a single function for creating a withdrawal request. After calling this function successfully, the liquidity asset (USDC, EURC) will be send back to your wallet directly from the vault.

To make a withdrawal request, you must call the following function...

 function requestRedeem(uint256 shares) external returns (uint256 assets);

...where shares is the number of vault tokens you wish to be redeemed. It will return the withdrawal request amount in assets.

Demo of Making a Withdrawal Request

To view a live example of making a withdrawal request, you can view our product documentation here.

Previewing Requests

Preview Redeem Request

To simulate the result of a withdrawal request at the current block and see how many assets would be withdrawn for a given number of vault tokens (i.e. shares), you can call the following function...

 function previewRedeemRequest(
        uint256 shares
    ) external view returns (uint256 assets);

It will returns the amount of assets that would be requested if this entire redeem request were to be processed at the current block. Note: This is equivalent of EIP-4626 previewRedeem.

Preview Withdrawal Request

To simulate the result of a withdrawal request at the current block and see how many vault tokens (i.e. shares) would be burned if this entire withdrawal request were to be processed at the current block, you can call the following function...

 function previewWithdrawRequest(
        uint256 assets
    ) external view returns (uint256 shares);

It will returns the amount of shares that would be burned if this entire withdrawRequest were to be processed at the current block. Note: This is equivalent of EIP-4626 previewWithdraw.

Convert to Assets

To see the get the value of a certain number of vault tokens as converted to liquidity assets (USDC, EURC), you can call the following function for a specified number of assets and it will return the number of shares.

    function convertToShares(
        uint256 assets
    ) external view returns (uint256 shares);

Convert to Shares

To see the value of a certain number of assets (USDC, EURC) converted to vault tokens (i.e. shares), you can call the following function for a specified number of assets and it will return the number of shares.

 function convertToAssets(
        uint256 shares
    ) external view returns (uint256 assets);

3. Reading Contract Data

As a Lender, all of the important information regarding the vault can be called back from the contract.

When you call any read function from typescript using popular Ethereum libraries like ethers.js or web3.js, it will return a full list of fields available from the contract ABI.

Get Pool Overview State

To get the current state of the pool, you can call the function

  function getPoolOverview()
        external
        view
        returns (IPoolOverviewStateFlex memory);

and it will return the Pool Overview data in the following structure

struct IPoolOverviewStateFlex {
    address poolAddr; // the contract address for the pool
    uint256 interestRate; // the current interest rate in bps e.g. 500 = 5.00%
    uint256 dailyInterestRate; // the interest rate earned for a single day
    uint256 totalPrincipalEarningInterest; // the total amount of principal earning interest
    uint256 totalInterestAccrued; // total interest accrued to date
    uint256 totalAssetsDeposited; // total assets deposited to date
    uint256 totalAssetsWithdrawn; // total assets withdrawn to date
    uint256 exchangeRate; // the current share to assets exchange rate
    uint256 totalSupply; // the total number of shares outstanding
    uint256 totalRequestedShares; // the total number of withdrawals outstanding, denominated in shares
    uint256 totalRequestedAssets; // the total number of withdrawals outstanding, denominated in assets
    uint256 totalAssetsTransitioningIn; // the total number of pending deposits, denominate in assets
    uint256 totalSharesTransitioningIn; // the total number of pending deposits, denominated in shares
    uint256 totalAssetsDueForWithdraws; // the total amount of assets due for withdrawal
    uint256 totalFees; // total fees accrued to date
    uint256 feesOutstanding; // total fees remaining unpaid
    uint64[] nonBusinessDays; // a list of non-business days
    uint8 state; // current status of the pool
    uint256 lastDayAccrued; // the last day the accrual occured

Get Pool Configuration

To get the current parameters and configuration of the pool, you can call...

function getPoolConfiguration()
        external
        view
        returns (IPoolConfigurationStateFlex memory);

and it will return the following data

struct IPoolConfigurationStateFlex {
    address poolAddr; // the contract address of the pool
    uint256 dailyOriginationFeeRate; // the daily fee rate
    uint256 originationFee; // the annualised fee
    uint256 closeOfDepositTime; // the Advance Cut-Off Time
    uint256 closeOfWithdrawTime; // the Withdrawal Cut-Off Time
    uint256 transferInDays; // the Advance Processing Time
    uint256 transferOutDays; // the Withdrawal Processing Time
    address liquidityAssetAddr;// the contract address of the pool asset (eg. USDC/EURC)
    address poolAdminAddr; // the wallet address of the Pool Admin
    address poolControllerAddr; // the contract address of the pool controll contract
    address withdrawControllerAddr; // the address of the withdraw controller contract
    address borrowerVaultAddr; // the contract address of the borrower vault
    string name; // the name of the pool
    string symbol; // the pool token symbol
    address borrowerManagerAddr; // the wallet address of the borrower manager 
    address borrowerWalletAddr; // the wallet address of the borrower
    uint256 maxCapacity; // the maximum outstanding loan principal for the pool
    uint64[] nonBusinessDays; // a list of non-business days
    address businessDayRegistryAddr; // the contract address of the business day registry

Get Account Overview

To get data on a specific wallet address ("account"), you can call the following function...

    function getPoolAccountState(
        address accountAddr
    ) external view returns (IPoolAccountStateFlex memory)

and it will return the following data

struct IPoolAccountStateFlex {
    address poolAddr; // the contract address of the pool
    address accountAddr; // the wallet address for the account
    uint256 tokenBalance; // the current balance of vault tokens held by the wallet
    uint256 maxWithdrawRequest; // the maximum withdrawal request the wallet can make, denominated in assets
    uint256 maxRedeemRequest; //the maximum withdrawal request the wallet can make, denominated in shares
    uint256 requestedSharesOf; // outstanding withdrawal requests for the account, in shares
    uint256 requestedAssetsOf; // outstanding withdrawal requests for the account, in assets
    uint256 principalEarningInterest; // current principal earning interest
    uint256 interestAccrued; // interest accrued by the account to date
    uint256 assetsDeposited; // deposits made by the account to date
    uint256 assetsWithdrawn; // withdrawals repaid to the account to date
    uint256 sharesTransitioningIn; // current pending loans for the account, in shares
    uint256 assetsTransitioningIn; // current pending loans for the account, in assets
    uint256 assetsDueForWithdraws; // withdrawal requests due to be repaid to the account, in assets
    uint256 sharesDueForWithdraws; // withdrawal requests due to be repaid to the account, in shares

Typescript Reference Implementation

Below are typescript examples for making deposits and withdrawals from the vault using ethers.js


import * as dotenv from 'dotenv'
import { Contract, JsonRpcProvider, Wallet } from 'ethers';



const VAULT_ADDRESS = '0xa66BA7E8Cf3eD414F07c8a9847CE36Ca4fcE38D7'


export const MAINNET_URL = 'https://eth-mainnet.g.alchemy.com/v2/fcK4AEYquRkIgqn0REy8N0uOsiO3kqKI'

export const SANDBOX_URL = 'https://eth-sepolia.g.alchemy.com/v2/oHCT97GjJyLp6TwUMjZdOGPAqDnr9gu6'
const URL = SANDBOX_URL
const FlexVaultABi =
  require('./PoolFlex.json').abi

function init() {
  dotenv.config()
}

// Note the number of shares are at 6 decimals places so 1e6 is 1 share

async function withdrawSharesFlexVault(shares: BigInt) {
  const provider = new JsonRpcProvider(URL)
  const wallet = new Wallet(process.env.PRIVATE_KEY as string, provider)
  const flexVaultContract = new Contract(VAULT_ADDRESS, FlexVaultABi, wallet)


  const tx = await flexVaultContract.requestRedeem(shares)
  console.log('tx', tx)
  const receipt = await tx.wait()
  console.log('receipt', receipt)

}
// Note the assets (USDC or Eurc) are at 6 decimals places so 1e6 is 1 USDC or Eurc
async function withdrawUSDCFlexVault(assets: BigInt) {
  const provider = new JsonRpcProvider(URL)
  const wallet = new Wallet(process.env.PRIVATE_KEY as string, provider)
  const flexVaultContract = new Contract(VAULT_ADDRESS, FlexVaultABi, wallet)
  const shares = await flexVaultContract.convertToShares(assets)

  const tx = await flexVaultContract.requestRedeem(shares)
  console.log('tx', tx)
  const receipt = await tx.wait()
  console.log('receipt', receipt)

}

// Note the assets (USDC or Eurc) are at 6 decimals places so 1e6 is 1 USDC or Eurc
async function depositFlexVault(assets: BigInt) {
  const provider = new JsonRpcProvider(URL)
  const wallet = new Wallet(process.env.PRIVATE_KEY as string, provider)

  const flexVaultContract = new Contract(VAULT_ADDRESS, FlexVaultABi, wallet)


  const tx = await flexVaultContract.deposit(assets, wallet.address)
  console.log('tx', tx)
  const receipt = await tx.wait()
  console.log('receipt', receipt)

}

init()
withdrawUSDCFlexVault(1000000n)
  .then()
  .catch((err: Error) => {
    console.error('err', err)
  })

Last updated