Fortress Protocol Math
The Fortress protocol smart contracts use a system of exponential math, in order to represent fractional quantities with sufficient precision, using Exponential.sol. Most numbers are represented as a mantissa, an unsigned integer scaled by 1 * 10 ^ 18, in order to perform math at a high level of precision.
The exchange rates and the prices of assets are scaled by the decimals unique to each asset. Fortress fTokens are BEP-20 tokens with 8 decimals of precision, while their underlying tokens vary.
fToken | fToken Decimals | Underlying | Underlying Decimals |
fBNB | 8 | BNB | 18 |
fBUSD | 8 | BUSD | 18 |
fUSDC | 8 | USDC | 18 |
fUSDT | 8 | USDT | 18 |
fDAI | 8 | DAI | 18 |
fBTC | 8 | BTC | 18 |
fETH | 8 | ETH | 18 |
fBETH | 8 | BETH | 18 |
fXRP | 8 | XRP | 18 |
fFTS | 8 | FTS | 18 |
fDOT | 8 | DOT | 18 |
fLTC | 8 | LTC | 18 |
fLINK | 8 | LINK | 18 |
The fToken exchange rate is scaled by the difference in decimals between the vToken and the underlying asset.
onefTokenInUnderlying = exchangeRateCurrent / (1 * 10 ^ (18 + underlyingDecimals - fTokenDecimals)
How to find the value of 1 fBUSD in BUSD with Web3.js JavaScript.
const fTokenDecimals = 8; // all fTokens have 8 decimal places
const underlying = new web3.eth.Contract(bep20Abi, busdAddress);
const fToken = new web3.eth.Contract(fTokenAbi, fBusdAddress);
const underlyingDecimals = await underlying.methods.decimals().call();
const exchangeRateCurrent = await fToken.methods.exchangeRateCurrent().call();
const mantissa = 18 + parseInt(underlyingDecimals) - fTokenDecimals;
const onefTokenInUnderlying = exchangeRateCurrent / Math.pow(10, mantissa);
console.log('1 fBUSD can be redeemed for', oneFTokenInUnderlying, 'BUSD');
There is not an underlying contract for BNB, so to do this with vBNB, set underlyingDecimals to 18. To find the number of underlying tokens that can be redeemed for fTokens, divide the number of fTokens by the above value oneFTokenInUnderlying.
underlyingTokens = fTokenAmount * oneFTokenInUnderlying
Fortress interest rates for each money market will update on every block in which the ratio of borrowed assets to supplied assets changes. The amount of that change is determined by the Fortress interest rate model's algorithm implemented for each money market and the amount of change to the borrowed to supplied assets ratio.
Interest accrues for all borrowers and suppliers in a money market when a wallet address interacts with the fToken contract, by "calling" one of these functions; borrow, mint, redeem and repay. When the smart contract executes one of these functions, it will then trigger the "accrueInterest" method, which then sends the interest to the underlying balance of every borrower or supplier in a money market. This interest accrues for the current block as well as each and every prior block in which the "accrueInterest" method was not triggered.
The annual percentage yield (APY) for borrowing or supplying in each money market can be calculated using the value of supplyRatePerBlock (supply APY) or borrowRatePerBlock (borrow APY) in this formula:
Rate = fToken.supplyRatePerBlock(); // Integer
Rate = 37893566
BNB Mantissa = 1 * 10 ^ 18 (BNB has 18 decimal places)
Blocks Per Day = 20 * 60 * 24 (based on 20 blocks occurring every minute)
Days Per Year = 365
APY = ((((Rate / BNB Mantissa * Blocks Per Day + 1) ^ Days Per Year - 1)) - 1) * 100
Here is an example of calculating the supply and borrow APY with Web3.js JavaScript
const ethMantissa = 1e18;
const blocksPerDay = 20 * 60 * 24;
const daysPerYear = 365;
const fToken = new web3.eth.Contract(fBnbAbi, fBnbAddress);
const supplyRatePerBlock = await fToken.methods.supplyRatePerBlock().call();
const borrowRatePerBlock = await fToken.methods.borrowRatePerBlock().call();
const supplyApy = (((Math.pow((supplyRatePerBlock / bnbMantissa * blocksPerDay) + 1,
const borrowApy = (((Math.pow((borrowRatePerBlock / bnbMantissa * blocksPerDay) + 1,
console.log(`Supply APY for BNB ${supplyApy} %`);
console.log(`Borrow APY for BNB ${borrowApy} %`);
Last modified 1yr ago