0

In the MakerDao contracts, there are conversions between annualized percentage rates and per-second percentage rates. For example, their Stability Fees documentation gives this example:

If the Stability Fee is set to 2%, it will accumulate at 1.0000000006279371924910298109948‬% per second. At the end of year one, the user will owe exactly 2% on the principal.

The basic way to convert from the annualized format to the per-second format and vice-versa is like so, where 31536000 is the number of seconds in a year:

1.0000000006279371924910298109948^31536000 = 2%
1.02^(1/31536000) = 1.0000000006279371924910298109948%

Since Solidity only supports integers, one may use fixed-point arithmetic for converting between the annualized rate and the per-second rate. My first question: what is the integer representation of these two rates (in ray)?

My next question is how would one go about using the BigNumber.js utility to convert to-and-from the per-second/annualized rates?

The JavaScript code I have below is not working.

PER_SECOND_RATE.pow(toBN(SECONDS_IN_YEAR).toString())
ANNUALIZED_RATE.pow(toBN(-SECONDS_IN_YEAR).toString())

1 Answer 1

1

Maker Protocol does not use or store an annual rate and there is no representation for it internally.

All rates are in per-second which uses the RAY number format which are regular uint256 values and are always handled by special math functions like rmul or rdiv which automatically consider the 27 right most numbers as the decimal part of the RAY fixed-point number.

So the integer representation for 1.02% will be the uint256 value 10000000006279371924910298109948.

I've tried using BN.js but it never worked for either calculations possibly due to the size of the numbers and exponentiation involved.

Annualized to Per-Second Rate Conversion

There is a slightly hacky approach to doing this with the bc command from javascript that worked well for me.

Input to the function is the annual rate. Ex: 5.6432% bc outputs the value with a decimal point and 27 decimals after. The decimal point is removed from the output to get the number in the RAY format used by the smart contracts.

public async getRatePerSecond(annual: number) {
    let annRate = ((annual / 100) + 1).toFixed(4);
  
    const { stdout, stderr } = await exec(
      "bc -l <<< 'scale=27; e( l(" +
        annRate.toString() +
        ")/31557600 )'"
    );
  
    if (stderr) {
      console.error(`error: ${stderr}`);
      return;
    }
  
    let ratePerSec = BigNumber.from(
      stdout
        .toString()
        .replace(".", "")
        .replace(/(\r\n|\n|\r)/gm, "")
    );
  
    return ratePerSec;
  }

Ex: getRatePerSecond(2.75) = 1000000000859655594846036300

Per-Second to Annualized Rate Conversion

This doesn't answer your question but I'm leaving this python script here for others looking for a simple way to do this calculation,

$ python -c "print 1.000000000627507392906712186**31557600"

1.01999999956

The Intro to Rate Mechanism guide provides an introduction to rates in the Maker Protocol if you are interested in learning more.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.