@nadohq/shared 0.65.0 → 0.67.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/index.d.cts +2 -2
  2. package/dist/index.d.ts +2 -2
  3. package/dist/{interest-BEsEmpAy.d.cts → interest-BN4bYdpY.d.cts} +3 -1
  4. package/dist/{interest-BEsEmpAy.d.cts.map → interest-BN4bYdpY.d.cts.map} +1 -1
  5. package/dist/{interest-DUfNVtPJ.d.ts → interest-CcDkLJnv.d.ts} +3 -1
  6. package/dist/{interest-DUfNVtPJ.d.ts.map → interest-CcDkLJnv.d.ts.map} +1 -1
  7. package/dist/{toIntegerString-CyhOUxbE.d.ts → toIntegerString-DfbD1c3k.d.ts} +3 -2
  8. package/dist/toIntegerString-DfbD1c3k.d.ts.map +1 -0
  9. package/dist/{toIntegerString-BUESqiY1.d.cts → toIntegerString-XmD3bQt-.d.cts} +3 -2
  10. package/dist/toIntegerString-XmD3bQt-.d.cts.map +1 -0
  11. package/dist/utils/index.d.cts +2 -2
  12. package/dist/utils/index.d.ts +2 -2
  13. package/dist/utils/interest.cjs +17 -3
  14. package/dist/utils/interest.cjs.map +1 -1
  15. package/dist/utils/interest.d.cts +1 -1
  16. package/dist/utils/interest.d.ts +1 -1
  17. package/dist/utils/interest.js +17 -3
  18. package/dist/utils/interest.js.map +1 -1
  19. package/dist/utils/math/index.d.cts +1 -1
  20. package/dist/utils/math/index.d.ts +1 -1
  21. package/dist/utils/math/toIntegerString.cjs +2 -1
  22. package/dist/utils/math/toIntegerString.cjs.map +1 -1
  23. package/dist/utils/math/toIntegerString.d.cts +1 -1
  24. package/dist/utils/math/toIntegerString.d.ts +1 -1
  25. package/dist/utils/math/toIntegerString.js +2 -1
  26. package/dist/utils/math/toIntegerString.js.map +1 -1
  27. package/package.json +2 -2
  28. package/src/utils/interest.test.ts +123 -0
  29. package/src/utils/interest.ts +43 -8
  30. package/src/utils/math/toIntegerString.ts +2 -1
  31. package/dist/toIntegerString-BUESqiY1.d.cts.map +0 -1
  32. package/dist/toIntegerString-CyhOUxbE.d.ts.map +0 -1
package/dist/index.d.cts CHANGED
@@ -24,7 +24,7 @@ import { t as clampBigNumber } from "./clamp-D9lICt7X.cjs";
24
24
  import { n as addDecimals, r as removeDecimals, t as NADO_PRODUCT_DECIMALS } from "./decimalAdjustment-DCwilVOA.cjs";
25
25
  import { t as sumBigNumberBy } from "./sumBigNumberBy-BvDeov4-.cjs";
26
26
  import { t as toBigInt } from "./toBigInt-BDiOjNl-.cjs";
27
- import { t as toIntegerString } from "./toIntegerString-BUESqiY1.cjs";
27
+ import { t as toIntegerString } from "./toIntegerString-XmD3bQt-.cjs";
28
28
  import { t as OrderExecutionType } from "./OrderExecutionType-5C8Yad5R.cjs";
29
29
  import { a as OrderAppendixTwapFields, i as OrderAppendixTriggerType, n as OrderAppendixBuilderFields, r as OrderAppendixIsolatedFields, t as OrderAppendix } from "./orderAppendixTypes-BUU6xlgS.cjs";
30
30
  import { t as SignatureParams } from "./signatureTypes-3nTqmq1y.cjs";
@@ -41,7 +41,7 @@ import { t as WalletNotProvidedError } from "./WalletNotProvidedError-Ba9SiWnP.c
41
41
  import { t as getNadoClientTypeHeaders } from "./getNadoClientTypeHeaders-E-LLWL7-.cjs";
42
42
  import { t as getValidatedAddress } from "./getValidatedAddress-CEK3POgJ.cjs";
43
43
  import { t as getValidatedHex } from "./getValidatedHex-DIJn4BMz.cjs";
44
- import { a as calcTotalDeposited, i as calcTotalBorrowed, n as calcBorrowRatePerSecond, o as calcUtilizationRatio, r as calcRealizedDepositRateForTimeRange, t as calcBorrowRateForTimeRange } from "./interest-BEsEmpAy.cjs";
44
+ import { a as calcTotalDeposited, i as calcTotalBorrowed, n as calcBorrowRatePerSecond, o as calcUtilizationRatio, r as calcRealizedDepositRateForTimeRange, t as calcBorrowRateForTimeRange } from "./interest-BN4bYdpY.cjs";
45
45
  import { t as isWriteableContract } from "./isWriteableContract-C9lzUJ7s.cjs";
46
46
  import { t as mapValues } from "./mapValues-H9D_81GC.cjs";
47
47
  import { n as unpackTwapOrderAppendixValue, t as packTwapOrderAppendixValue } from "./appendixTwapValue-DNudvbI5.cjs";
package/dist/index.d.ts CHANGED
@@ -24,7 +24,7 @@ import { t as clampBigNumber } from "./clamp-D9lICt7X.js";
24
24
  import { n as addDecimals, r as removeDecimals, t as NADO_PRODUCT_DECIMALS } from "./decimalAdjustment-pLqKqD8x.js";
25
25
  import { t as sumBigNumberBy } from "./sumBigNumberBy-BvDeov4-.js";
26
26
  import { t as toBigInt } from "./toBigInt-CBRk3mgz.js";
27
- import { t as toIntegerString } from "./toIntegerString-CyhOUxbE.js";
27
+ import { t as toIntegerString } from "./toIntegerString-DfbD1c3k.js";
28
28
  import { t as OrderExecutionType } from "./OrderExecutionType-5C8Yad5R.js";
29
29
  import { a as OrderAppendixTwapFields, i as OrderAppendixTriggerType, n as OrderAppendixBuilderFields, r as OrderAppendixIsolatedFields, t as OrderAppendix } from "./orderAppendixTypes-pxsI3qLs.js";
30
30
  import { t as SignatureParams } from "./signatureTypes-3nTqmq1y.js";
@@ -41,7 +41,7 @@ import { t as WalletNotProvidedError } from "./WalletNotProvidedError-Ba9SiWnP.j
41
41
  import { t as getNadoClientTypeHeaders } from "./getNadoClientTypeHeaders-E-LLWL7-.js";
42
42
  import { t as getValidatedAddress } from "./getValidatedAddress-CEK3POgJ.js";
43
43
  import { t as getValidatedHex } from "./getValidatedHex-DIJn4BMz.js";
44
- import { a as calcTotalDeposited, i as calcTotalBorrowed, n as calcBorrowRatePerSecond, o as calcUtilizationRatio, r as calcRealizedDepositRateForTimeRange, t as calcBorrowRateForTimeRange } from "./interest-DUfNVtPJ.js";
44
+ import { a as calcTotalDeposited, i as calcTotalBorrowed, n as calcBorrowRatePerSecond, o as calcUtilizationRatio, r as calcRealizedDepositRateForTimeRange, t as calcBorrowRateForTimeRange } from "./interest-CcDkLJnv.js";
45
45
  import { t as isWriteableContract } from "./isWriteableContract-X1rD5O8a.js";
46
46
  import { t as mapValues } from "./mapValues-H9D_81GC.js";
47
47
  import { n as unpackTwapOrderAppendixValue, t as packTwapOrderAppendixValue } from "./appendixTwapValue-Bb2ukj7A.js";
@@ -37,6 +37,7 @@ declare function calcBorrowRatePerSecond(product: SpotProduct): BigNumber;
37
37
  *
38
38
  * @param product Spot product
39
39
  * @param seconds Number of seconds for the time period
40
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
40
41
  */
41
42
  declare function calcBorrowRateForTimeRange(product: SpotProduct, seconds: BigNumberish, minDepositRate: BigNumberish): BigNumber;
42
43
  /**
@@ -45,8 +46,9 @@ declare function calcBorrowRateForTimeRange(product: SpotProduct, seconds: BigNu
45
46
  * @param product Spot product
46
47
  * @param seconds Number of seconds for the time period
47
48
  * @param interestFeeFrac Fraction of paid borrower interest that is paid as a fee (0.2 = 20% fee)
49
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
48
50
  */
49
51
  declare function calcRealizedDepositRateForTimeRange(product: SpotProduct, seconds: BigNumberish, interestFeeFrac: BigNumberish, minDepositRate: BigNumberish): BigNumber;
50
52
  //#endregion
51
53
  export { calcTotalDeposited as a, calcTotalBorrowed as i, calcBorrowRatePerSecond as n, calcUtilizationRatio as o, calcRealizedDepositRateForTimeRange as r, calcBorrowRateForTimeRange as t };
52
- //# sourceMappingURL=interest-BEsEmpAy.d.cts.map
54
+ //# sourceMappingURL=interest-BN4bYdpY.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"interest-BEsEmpAy.d.cts","names":[],"sources":["../src/utils/src/utils/interest.ts"],"mappings":";;;;;;;;iBAQgB,iBAAA,CACd,sBAAA,EAAwB,YAAA,EACxB,8BAAA,EAAgC,YAAA,GAC/B,SAAA;;;;iBASa,kBAAA,CACd,uBAAA,EAAyB,YAAA,EACzB,+BAAA,EAAiC,YAAA,GAChC,SAAA;;;;;;iBAWa,oBAAA,CAAqB,OAAA,EAAS,WAAA,GAAW,SAAA;;;;AAvB7C;AASZ;;;;;;;;;;iBAmCgB,uBAAA,CAAwB,OAAA,EAAS,WAAA,GAAW,SAAA;;;;AAhChD;AAWZ;;iBA0DgB,0BAAA,CACd,OAAA,EAAS,WAAA,EACT,OAAA,EAAS,YAAA,EACT,cAAA,EAAgB,YAAA,GAAY,SAAA;;;;;;AA7D2B;AAqBzD;iBA0DgB,mCAAA,CACd,OAAA,EAAS,WAAA,EACT,OAAA,EAAS,YAAA,EACT,eAAA,EAAiB,YAAA,EACjB,cAAA,EAAgB,YAAA,GAAY,SAAA"}
1
+ {"version":3,"file":"interest-BN4bYdpY.d.cts","names":[],"sources":["../src/utils/src/utils/interest.ts"],"mappings":";;;;;;;;iBAQgB,iBAAA,CACd,sBAAA,EAAwB,YAAA,EACxB,8BAAA,EAAgC,YAAA,GAC/B,SAAA;;;;iBASa,kBAAA,CACd,uBAAA,EAAyB,YAAA,EACzB,+BAAA,EAAiC,YAAA,GAChC,SAAA;;;;;;iBAWa,oBAAA,CAAqB,OAAA,EAAS,WAAA,GAAW,SAAA;;;;AAvB7C;AASZ;;;;;;;;;;iBAmCgB,uBAAA,CAAwB,OAAA,EAAS,WAAA,GAAW,SAAA;;;;AAhChD;AAWZ;;;iBA6EgB,0BAAA,CACd,OAAA,EAAS,WAAA,EACT,OAAA,EAAS,YAAA,EACT,cAAA,EAAgB,YAAA,GAAY,SAAA;;;;;AAhF2B;AAqBzD;;;iBAoFgB,mCAAA,CACd,OAAA,EAAS,WAAA,EACT,OAAA,EAAS,YAAA,EACT,eAAA,EAAiB,YAAA,EACjB,cAAA,EAAgB,YAAA,GAAY,SAAA"}
@@ -37,6 +37,7 @@ declare function calcBorrowRatePerSecond(product: SpotProduct): BigNumber;
37
37
  *
38
38
  * @param product Spot product
39
39
  * @param seconds Number of seconds for the time period
40
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
40
41
  */
41
42
  declare function calcBorrowRateForTimeRange(product: SpotProduct, seconds: BigNumberish, minDepositRate: BigNumberish): BigNumber;
42
43
  /**
@@ -45,8 +46,9 @@ declare function calcBorrowRateForTimeRange(product: SpotProduct, seconds: BigNu
45
46
  * @param product Spot product
46
47
  * @param seconds Number of seconds for the time period
47
48
  * @param interestFeeFrac Fraction of paid borrower interest that is paid as a fee (0.2 = 20% fee)
49
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
48
50
  */
49
51
  declare function calcRealizedDepositRateForTimeRange(product: SpotProduct, seconds: BigNumberish, interestFeeFrac: BigNumberish, minDepositRate: BigNumberish): BigNumber;
50
52
  //#endregion
51
53
  export { calcTotalDeposited as a, calcTotalBorrowed as i, calcBorrowRatePerSecond as n, calcUtilizationRatio as o, calcRealizedDepositRateForTimeRange as r, calcBorrowRateForTimeRange as t };
52
- //# sourceMappingURL=interest-DUfNVtPJ.d.ts.map
54
+ //# sourceMappingURL=interest-CcDkLJnv.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"interest-DUfNVtPJ.d.ts","names":[],"sources":["../src/utils/src/utils/interest.ts"],"mappings":";;;;;;;;iBAQgB,iBAAA,CACd,sBAAA,EAAwB,YAAA,EACxB,8BAAA,EAAgC,YAAA,GAC/B,SAAA;;;;iBASa,kBAAA,CACd,uBAAA,EAAyB,YAAA,EACzB,+BAAA,EAAiC,YAAA,GAChC,SAAA;;;;;;iBAWa,oBAAA,CAAqB,OAAA,EAAS,WAAA,GAAW,SAAA;;;;AAvB7C;AASZ;;;;;;;;;;iBAmCgB,uBAAA,CAAwB,OAAA,EAAS,WAAA,GAAW,SAAA;;;;AAhChD;AAWZ;;iBA0DgB,0BAAA,CACd,OAAA,EAAS,WAAA,EACT,OAAA,EAAS,YAAA,EACT,cAAA,EAAgB,YAAA,GAAY,SAAA;;;;;;AA7D2B;AAqBzD;iBA0DgB,mCAAA,CACd,OAAA,EAAS,WAAA,EACT,OAAA,EAAS,YAAA,EACT,eAAA,EAAiB,YAAA,EACjB,cAAA,EAAgB,YAAA,GAAY,SAAA"}
1
+ {"version":3,"file":"interest-CcDkLJnv.d.ts","names":[],"sources":["../src/utils/src/utils/interest.ts"],"mappings":";;;;;;;;iBAQgB,iBAAA,CACd,sBAAA,EAAwB,YAAA,EACxB,8BAAA,EAAgC,YAAA,GAC/B,SAAA;;;;iBASa,kBAAA,CACd,uBAAA,EAAyB,YAAA,EACzB,+BAAA,EAAiC,YAAA,GAChC,SAAA;;;;;;iBAWa,oBAAA,CAAqB,OAAA,EAAS,WAAA,GAAW,SAAA;;;;AAvB7C;AASZ;;;;;;;;;;iBAmCgB,uBAAA,CAAwB,OAAA,EAAS,WAAA,GAAW,SAAA;;;;AAhChD;AAWZ;;;iBA6EgB,0BAAA,CACd,OAAA,EAAS,WAAA,EACT,OAAA,EAAS,YAAA,EACT,cAAA,EAAgB,YAAA,GAAY,SAAA;;;;;AAhF2B;AAqBzD;;;iBAoFgB,mCAAA,CACd,OAAA,EAAS,WAAA,EACT,OAAA,EAAS,YAAA,EACT,eAAA,EAAiB,YAAA,EACjB,cAAA,EAAgB,YAAA,GAAY,SAAA"}
@@ -2,11 +2,12 @@ import { t as BigNumberish } from "./bigNumber-BqgJEjFs.js";
2
2
 
3
3
  //#region src/utils/math/toIntegerString.d.ts
4
4
  /**
5
- * Converts a BigNumberish value to string
5
+ * Converts a BigNumberish value to an integer string, rounding any fractional
6
+ * part to the nearest integer (bignumber.js `.toFixed(0)` default rounding mode).
6
7
  *
7
8
  * @param val
8
9
  */
9
10
  declare function toIntegerString(val: BigNumberish): string;
10
11
  //#endregion
11
12
  export { toIntegerString as t };
12
- //# sourceMappingURL=toIntegerString-CyhOUxbE.d.ts.map
13
+ //# sourceMappingURL=toIntegerString-DfbD1c3k.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toIntegerString-DfbD1c3k.d.ts","names":[],"sources":["../src/utils/math/src/utils/math/toIntegerString.ts"],"mappings":";;;;;AAQA;;;;iBAAgB,eAAA,CAAgB,GAAiB,EAAZ,YAAY"}
@@ -2,11 +2,12 @@ import { t as BigNumberish } from "./bigNumber-BqgJEjFs.cjs";
2
2
 
3
3
  //#region src/utils/math/toIntegerString.d.ts
4
4
  /**
5
- * Converts a BigNumberish value to string
5
+ * Converts a BigNumberish value to an integer string, rounding any fractional
6
+ * part to the nearest integer (bignumber.js `.toFixed(0)` default rounding mode).
6
7
  *
7
8
  * @param val
8
9
  */
9
10
  declare function toIntegerString(val: BigNumberish): string;
10
11
  //#endregion
11
12
  export { toIntegerString as t };
12
- //# sourceMappingURL=toIntegerString-BUESqiY1.d.cts.map
13
+ //# sourceMappingURL=toIntegerString-XmD3bQt-.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"toIntegerString-XmD3bQt-.d.cts","names":[],"sources":["../src/utils/math/src/utils/math/toIntegerString.ts"],"mappings":";;;;;AAQA;;;;iBAAgB,eAAA,CAAgB,GAAiB,EAAZ,YAAY"}
@@ -4,7 +4,7 @@ import { t as clampBigNumber } from "../clamp-D9lICt7X.cjs";
4
4
  import { n as addDecimals, r as removeDecimals, t as NADO_PRODUCT_DECIMALS } from "../decimalAdjustment-DCwilVOA.cjs";
5
5
  import { t as sumBigNumberBy } from "../sumBigNumberBy-BvDeov4-.cjs";
6
6
  import { t as toBigInt } from "../toBigInt-BDiOjNl-.cjs";
7
- import { t as toIntegerString } from "../toIntegerString-BUESqiY1.cjs";
7
+ import { t as toIntegerString } from "../toIntegerString-XmD3bQt-.cjs";
8
8
  import { n as asyncResult, t as AsyncResult } from "../asyncResult-BL5RY5sb.cjs";
9
9
  import { n as calcPerpBalanceValue, r as calcSpotBalanceValue, t as calcPerpBalanceNotionalValue } from "../balanceValue-1rQfF7-m.cjs";
10
10
  import { a as subaccountToBytes32, i as subaccountNameToBytes12, n as subaccountFromHex, o as subaccountToHex, r as subaccountNameBytesToStr, t as subaccountFromBytes32 } from "../bytes32-RGr1nYVD.cjs";
@@ -13,7 +13,7 @@ import { t as WalletNotProvidedError } from "../WalletNotProvidedError-Ba9SiWnP.
13
13
  import { t as getNadoClientTypeHeaders } from "../getNadoClientTypeHeaders-E-LLWL7-.cjs";
14
14
  import { t as getValidatedAddress } from "../getValidatedAddress-CEK3POgJ.cjs";
15
15
  import { t as getValidatedHex } from "../getValidatedHex-DIJn4BMz.cjs";
16
- import { a as calcTotalDeposited, i as calcTotalBorrowed, n as calcBorrowRatePerSecond, o as calcUtilizationRatio, r as calcRealizedDepositRateForTimeRange, t as calcBorrowRateForTimeRange } from "../interest-BEsEmpAy.cjs";
16
+ import { a as calcTotalDeposited, i as calcTotalBorrowed, n as calcBorrowRatePerSecond, o as calcUtilizationRatio, r as calcRealizedDepositRateForTimeRange, t as calcBorrowRateForTimeRange } from "../interest-BN4bYdpY.cjs";
17
17
  import { t as isWriteableContract } from "../isWriteableContract-C9lzUJ7s.cjs";
18
18
  import { t as mapValues } from "../mapValues-H9D_81GC.cjs";
19
19
  import { n as unpackTwapOrderAppendixValue, t as packTwapOrderAppendixValue } from "../appendixTwapValue-DNudvbI5.cjs";
@@ -4,7 +4,7 @@ import { t as clampBigNumber } from "../clamp-D9lICt7X.js";
4
4
  import { n as addDecimals, r as removeDecimals, t as NADO_PRODUCT_DECIMALS } from "../decimalAdjustment-pLqKqD8x.js";
5
5
  import { t as sumBigNumberBy } from "../sumBigNumberBy-BvDeov4-.js";
6
6
  import { t as toBigInt } from "../toBigInt-CBRk3mgz.js";
7
- import { t as toIntegerString } from "../toIntegerString-CyhOUxbE.js";
7
+ import { t as toIntegerString } from "../toIntegerString-DfbD1c3k.js";
8
8
  import { n as asyncResult, t as AsyncResult } from "../asyncResult-BL5RY5sb.js";
9
9
  import { n as calcPerpBalanceValue, r as calcSpotBalanceValue, t as calcPerpBalanceNotionalValue } from "../balanceValue-BYBNPNZD.js";
10
10
  import { a as subaccountToBytes32, i as subaccountNameToBytes12, n as subaccountFromHex, o as subaccountToHex, r as subaccountNameBytesToStr, t as subaccountFromBytes32 } from "../bytes32-DsSNgVyG.js";
@@ -13,7 +13,7 @@ import { t as WalletNotProvidedError } from "../WalletNotProvidedError-Ba9SiWnP.
13
13
  import { t as getNadoClientTypeHeaders } from "../getNadoClientTypeHeaders-E-LLWL7-.js";
14
14
  import { t as getValidatedAddress } from "../getValidatedAddress-CEK3POgJ.js";
15
15
  import { t as getValidatedHex } from "../getValidatedHex-DIJn4BMz.js";
16
- import { a as calcTotalDeposited, i as calcTotalBorrowed, n as calcBorrowRatePerSecond, o as calcUtilizationRatio, r as calcRealizedDepositRateForTimeRange, t as calcBorrowRateForTimeRange } from "../interest-DUfNVtPJ.js";
16
+ import { a as calcTotalDeposited, i as calcTotalBorrowed, n as calcBorrowRatePerSecond, o as calcUtilizationRatio, r as calcRealizedDepositRateForTimeRange, t as calcBorrowRateForTimeRange } from "../interest-CcDkLJnv.js";
17
17
  import { t as isWriteableContract } from "../isWriteableContract-X1rD5O8a.js";
18
18
  import { t as mapValues } from "../mapValues-H9D_81GC.js";
19
19
  import { n as unpackTwapOrderAppendixValue, t as packTwapOrderAppendixValue } from "../appendixTwapValue-Bb2ukj7A.js";
@@ -55,13 +55,25 @@ function calcBorrowRatePerSecond(product) {
55
55
  return annualRate.div(31536e3);
56
56
  }
57
57
  /**
58
+ * Compounds an annual min deposit rate over a period, giving the multiplier
59
+ * `SpotEngine._updateState` applies to both the borrow and the deposit
60
+ * multiplier: `(1 + minDepositRate / secondsPerYear) ** seconds`.
61
+ *
62
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
63
+ * @param seconds Number of seconds for the time period
64
+ */
65
+ function calcMinDepositRateMultiplier(minDepositRate, seconds) {
66
+ return (1 + require_utils_math_bigNumber.toBigNumber(minDepositRate).div(31536e3).toNumber()) ** require_utils_math_bigNumber.toBigNumber(seconds).toNumber();
67
+ }
68
+ /**
58
69
  * Calculates borrower interest rate compounded for a period of time.
59
70
  *
60
71
  * @param product Spot product
61
72
  * @param seconds Number of seconds for the time period
73
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
62
74
  */
63
75
  function calcBorrowRateForTimeRange(product, seconds, minDepositRate) {
64
- return require_utils_math_bigNumber.toBigNumber(calcBorrowRatePerSecond(product).plus(1).toNumber() ** require_utils_math_bigNumber.toBigNumber(seconds).toNumber() - 1).plus(require_utils_math_bigNumber.toBigNumber(minDepositRate));
76
+ return require_utils_math_bigNumber.toBigNumber(calcBorrowRatePerSecond(product).plus(1).toNumber() ** require_utils_math_bigNumber.toBigNumber(seconds).toNumber() * calcMinDepositRateMultiplier(minDepositRate, seconds) - 1);
65
77
  }
66
78
  /**
67
79
  * Calculate depositor interest rate compounded for a period of time.
@@ -69,11 +81,13 @@ function calcBorrowRateForTimeRange(product, seconds, minDepositRate) {
69
81
  * @param product Spot product
70
82
  * @param seconds Number of seconds for the time period
71
83
  * @param interestFeeFrac Fraction of paid borrower interest that is paid as a fee (0.2 = 20% fee)
84
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
72
85
  */
73
86
  function calcRealizedDepositRateForTimeRange(product, seconds, interestFeeFrac, minDepositRate) {
87
+ const minDepositRateMultiplier = calcMinDepositRateMultiplier(minDepositRate, seconds);
74
88
  const utilization = calcUtilizationRatio(product);
75
- if (utilization.eq(0)) return require_utils_math_bigNumber.toBigNumber(0);
76
- return utilization.times(calcBorrowRateForTimeRange(product, seconds, require_utils_math_bigNumber.toBigNumber(0))).times(require_utils_math_BigNumbers.BigNumbers.ONE.minus(require_utils_math_bigNumber.toBigNumber(interestFeeFrac))).plus(require_utils_math_bigNumber.toBigNumber(minDepositRate));
89
+ if (utilization.eq(0)) return require_utils_math_bigNumber.toBigNumber(minDepositRateMultiplier - 1);
90
+ return utilization.times(calcBorrowRateForTimeRange(product, seconds, require_utils_math_bigNumber.toBigNumber(0))).times(require_utils_math_BigNumbers.BigNumbers.ONE.minus(require_utils_math_bigNumber.toBigNumber(interestFeeFrac))).plus(require_utils_math_BigNumbers.BigNumbers.ONE).times(minDepositRateMultiplier).minus(require_utils_math_BigNumbers.BigNumbers.ONE);
77
91
  }
78
92
  //#endregion
79
93
  exports.calcBorrowRateForTimeRange = calcBorrowRateForTimeRange;
@@ -1 +1 @@
1
- {"version":3,"file":"interest.cjs","names":["toBigNumber","removeDecimals","BigNumbers"],"sources":["../../src/utils/interest.ts"],"sourcesContent":["import BigNumber from 'bignumber.js';\nimport { SpotProduct } from '../types/productTypes';\nimport { BigNumberish, BigNumbers, removeDecimals, toBigNumber } from './math';\nimport { TimeInSeconds } from './time';\n\n/**\n * Calculate amount total borrowed for a product\n */\nexport function calcTotalBorrowed(\n totalBorrowsNormalized: BigNumberish,\n cumulativeBorrowsMultiplierX18: BigNumberish,\n): BigNumber {\n return toBigNumber(totalBorrowsNormalized).multipliedBy(\n removeDecimals(cumulativeBorrowsMultiplierX18),\n );\n}\n\n/**\n * Calculate amount total deposited for a product.\n */\nexport function calcTotalDeposited(\n totalDepositsNormalized: BigNumberish,\n cumulativeDepositsMultiplierX18: BigNumberish,\n): BigNumber {\n return toBigNumber(totalDepositsNormalized).multipliedBy(\n removeDecimals(cumulativeDepositsMultiplierX18),\n );\n}\n\n/**\n * Calculates utilization ratio = abs(total borrowed / total deposited)\n *\n * @param product Spot product\n */\nexport function calcUtilizationRatio(product: SpotProduct) {\n if (product.totalDeposited.eq(0) || product.totalBorrowed.eq(0)) {\n return toBigNumber(0);\n }\n return product.totalBorrowed.abs().div(product.totalDeposited);\n}\n\n/**\n * Calculates per-second borrow interest rate for a product. For example, a returned rate of 0.1 indicates 10% borrower\n * interest. The calculation for interest rate is as follows:\n *\n * If utilization ratio > inflection:\n * annual rate = (utilization ratio - inflection) / (1 - inflection) * interestLargeCap + interestFloor + interestSmallCap\n *\n * If utilization ratio <= inflection:\n * annual rate = utilization ratio * interestSmallCap / inflection + interestFloor\n *\n * The returned rate is annual rate / 31536000 seconds per year.\n *\n * @param product Spot product\n */\nexport function calcBorrowRatePerSecond(product: SpotProduct) {\n const {\n interestFloor,\n interestInflectionUtil,\n interestSmallCap,\n interestLargeCap,\n } = product;\n const utilization = calcUtilizationRatio(product);\n if (utilization.eq(0)) {\n return toBigNumber(0);\n }\n const pastInflection = utilization.gt(interestInflectionUtil);\n\n let annualRate: BigNumber;\n if (pastInflection) {\n const utilizationTerm = interestLargeCap.times(\n toBigNumber(utilization)\n .minus(interestInflectionUtil)\n .div(BigNumbers.ONE.minus(interestInflectionUtil)),\n );\n annualRate = interestFloor.plus(interestSmallCap).plus(utilizationTerm);\n } else {\n const utilizationTerm = utilization\n .div(interestInflectionUtil)\n .times(interestSmallCap);\n annualRate = interestFloor.plus(utilizationTerm);\n }\n\n return annualRate.div(TimeInSeconds.YEAR);\n}\n\n/**\n * Calculates borrower interest rate compounded for a period of time.\n *\n * @param product Spot product\n * @param seconds Number of seconds for the time period\n */\nexport function calcBorrowRateForTimeRange(\n product: SpotProduct,\n seconds: BigNumberish,\n minDepositRate: BigNumberish,\n) {\n const borrowRatePerSecond = calcBorrowRatePerSecond(product);\n\n // Convert to number for this, with some loss of precision, but using `.pow()` causes us to hit browser resource limits\n const borrowRateForTime =\n borrowRatePerSecond.plus(1).toNumber() ** toBigNumber(seconds).toNumber() -\n 1;\n return toBigNumber(borrowRateForTime).plus(toBigNumber(minDepositRate));\n}\n\n/**\n * Calculate depositor interest rate compounded for a period of time.\n *\n * @param product Spot product\n * @param seconds Number of seconds for the time period\n * @param interestFeeFrac Fraction of paid borrower interest that is paid as a fee (0.2 = 20% fee)\n */\nexport function calcRealizedDepositRateForTimeRange(\n product: SpotProduct,\n seconds: BigNumberish,\n interestFeeFrac: BigNumberish,\n minDepositRate: BigNumberish,\n) {\n const utilization = calcUtilizationRatio(product);\n if (utilization.eq(0)) {\n return toBigNumber(0);\n }\n return utilization\n .times(calcBorrowRateForTimeRange(product, seconds, toBigNumber(0)))\n .times(BigNumbers.ONE.minus(toBigNumber(interestFeeFrac)))\n .plus(toBigNumber(minDepositRate));\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,kBACd,wBACA,gCACW;CACX,OAAOA,6BAAAA,YAAY,sBAAsB,EAAE,aACzCC,qCAAAA,eAAe,8BAA8B,CAC/C;AACF;;;;AAKA,SAAgB,mBACd,yBACA,iCACW;CACX,OAAOD,6BAAAA,YAAY,uBAAuB,EAAE,aAC1CC,qCAAAA,eAAe,+BAA+B,CAChD;AACF;;;;;;AAOA,SAAgB,qBAAqB,SAAsB;CACzD,IAAI,QAAQ,eAAe,GAAG,CAAC,KAAK,QAAQ,cAAc,GAAG,CAAC,GAC5D,OAAOD,6BAAAA,YAAY,CAAC;CAEtB,OAAO,QAAQ,cAAc,IAAI,EAAE,IAAI,QAAQ,cAAc;AAC/D;;;;;;;;;;;;;;;AAgBA,SAAgB,wBAAwB,SAAsB;CAC5D,MAAM,EACJ,eACA,wBACA,kBACA,qBACE;CACJ,MAAM,cAAc,qBAAqB,OAAO;CAChD,IAAI,YAAY,GAAG,CAAC,GAClB,OAAOA,6BAAAA,YAAY,CAAC;CAEtB,MAAM,iBAAiB,YAAY,GAAG,sBAAsB;CAE5D,IAAI;CACJ,IAAI,gBAAgB;EAClB,MAAM,kBAAkB,iBAAiB,MACvCA,6BAAAA,YAAY,WAAW,EACpB,MAAM,sBAAsB,EAC5B,IAAIE,8BAAAA,WAAW,IAAI,MAAM,sBAAsB,CAAC,CACrD;EACA,aAAa,cAAc,KAAK,gBAAgB,EAAE,KAAK,eAAe;CACxE,OAAO;EACL,MAAM,kBAAkB,YACrB,IAAI,sBAAsB,EAC1B,MAAM,gBAAgB;EACzB,aAAa,cAAc,KAAK,eAAe;CACjD;CAEA,OAAO,WAAW,IAAA,OAAsB;AAC1C;;;;;;;AAQA,SAAgB,2BACd,SACA,SACA,gBACA;CAOA,OAAOF,6BAAAA,YANqB,wBAAwB,OAIhC,EAAE,KAAK,CAAC,EAAE,SAAS,KAAKA,6BAAAA,YAAY,OAAO,EAAE,SAAS,IACxE,CACkC,EAAE,KAAKA,6BAAAA,YAAY,cAAc,CAAC;AACxE;;;;;;;;AASA,SAAgB,oCACd,SACA,SACA,iBACA,gBACA;CACA,MAAM,cAAc,qBAAqB,OAAO;CAChD,IAAI,YAAY,GAAG,CAAC,GAClB,OAAOA,6BAAAA,YAAY,CAAC;CAEtB,OAAO,YACJ,MAAM,2BAA2B,SAAS,SAASA,6BAAAA,YAAY,CAAC,CAAC,CAAC,EAClE,MAAME,8BAAAA,WAAW,IAAI,MAAMF,6BAAAA,YAAY,eAAe,CAAC,CAAC,EACxD,KAAKA,6BAAAA,YAAY,cAAc,CAAC;AACrC"}
1
+ {"version":3,"file":"interest.cjs","names":["toBigNumber","removeDecimals","BigNumbers"],"sources":["../../src/utils/interest.ts"],"sourcesContent":["import BigNumber from 'bignumber.js';\nimport { SpotProduct } from '../types/productTypes';\nimport { BigNumberish, BigNumbers, removeDecimals, toBigNumber } from './math';\nimport { TimeInSeconds } from './time';\n\n/**\n * Calculate amount total borrowed for a product\n */\nexport function calcTotalBorrowed(\n totalBorrowsNormalized: BigNumberish,\n cumulativeBorrowsMultiplierX18: BigNumberish,\n): BigNumber {\n return toBigNumber(totalBorrowsNormalized).multipliedBy(\n removeDecimals(cumulativeBorrowsMultiplierX18),\n );\n}\n\n/**\n * Calculate amount total deposited for a product.\n */\nexport function calcTotalDeposited(\n totalDepositsNormalized: BigNumberish,\n cumulativeDepositsMultiplierX18: BigNumberish,\n): BigNumber {\n return toBigNumber(totalDepositsNormalized).multipliedBy(\n removeDecimals(cumulativeDepositsMultiplierX18),\n );\n}\n\n/**\n * Calculates utilization ratio = abs(total borrowed / total deposited)\n *\n * @param product Spot product\n */\nexport function calcUtilizationRatio(product: SpotProduct) {\n if (product.totalDeposited.eq(0) || product.totalBorrowed.eq(0)) {\n return toBigNumber(0);\n }\n return product.totalBorrowed.abs().div(product.totalDeposited);\n}\n\n/**\n * Calculates per-second borrow interest rate for a product. For example, a returned rate of 0.1 indicates 10% borrower\n * interest. The calculation for interest rate is as follows:\n *\n * If utilization ratio > inflection:\n * annual rate = (utilization ratio - inflection) / (1 - inflection) * interestLargeCap + interestFloor + interestSmallCap\n *\n * If utilization ratio <= inflection:\n * annual rate = utilization ratio * interestSmallCap / inflection + interestFloor\n *\n * The returned rate is annual rate / 31536000 seconds per year.\n *\n * @param product Spot product\n */\nexport function calcBorrowRatePerSecond(product: SpotProduct) {\n const {\n interestFloor,\n interestInflectionUtil,\n interestSmallCap,\n interestLargeCap,\n } = product;\n const utilization = calcUtilizationRatio(product);\n if (utilization.eq(0)) {\n return toBigNumber(0);\n }\n const pastInflection = utilization.gt(interestInflectionUtil);\n\n let annualRate: BigNumber;\n if (pastInflection) {\n const utilizationTerm = interestLargeCap.times(\n toBigNumber(utilization)\n .minus(interestInflectionUtil)\n .div(BigNumbers.ONE.minus(interestInflectionUtil)),\n );\n annualRate = interestFloor.plus(interestSmallCap).plus(utilizationTerm);\n } else {\n const utilizationTerm = utilization\n .div(interestInflectionUtil)\n .times(interestSmallCap);\n annualRate = interestFloor.plus(utilizationTerm);\n }\n\n return annualRate.div(TimeInSeconds.YEAR);\n}\n\n/**\n * Compounds an annual min deposit rate over a period, giving the multiplier\n * `SpotEngine._updateState` applies to both the borrow and the deposit\n * multiplier: `(1 + minDepositRate / secondsPerYear) ** seconds`.\n *\n * @param minDepositRate Annual min deposit rate, as stored in the product config\n * @param seconds Number of seconds for the time period\n */\nfunction calcMinDepositRateMultiplier(\n minDepositRate: BigNumberish,\n seconds: BigNumberish,\n): number {\n const perSecond = toBigNumber(minDepositRate)\n .div(TimeInSeconds.YEAR)\n .toNumber();\n return (1 + perSecond) ** toBigNumber(seconds).toNumber();\n}\n\n/**\n * Calculates borrower interest rate compounded for a period of time.\n *\n * @param product Spot product\n * @param seconds Number of seconds for the time period\n * @param minDepositRate Annual min deposit rate, as stored in the product config\n */\nexport function calcBorrowRateForTimeRange(\n product: SpotProduct,\n seconds: BigNumberish,\n minDepositRate: BigNumberish,\n) {\n const borrowRatePerSecond = calcBorrowRatePerSecond(product);\n\n // Convert to number for this, with some loss of precision, but using `.pow()` causes us to hit browser resource limits\n const borrowRateMultiplier =\n borrowRatePerSecond.plus(1).toNumber() ** toBigNumber(seconds).toNumber();\n // The engine multiplies the borrow multiplier by the min deposit multiplier,\n // so the min deposit rate has to be compounded over the same period rather\n // than added as an annual figure.\n return toBigNumber(\n borrowRateMultiplier *\n calcMinDepositRateMultiplier(minDepositRate, seconds) -\n 1,\n );\n}\n\n/**\n * Calculate depositor interest rate compounded for a period of time.\n *\n * @param product Spot product\n * @param seconds Number of seconds for the time period\n * @param interestFeeFrac Fraction of paid borrower interest that is paid as a fee (0.2 = 20% fee)\n * @param minDepositRate Annual min deposit rate, as stored in the product config\n */\nexport function calcRealizedDepositRateForTimeRange(\n product: SpotProduct,\n seconds: BigNumberish,\n interestFeeFrac: BigNumberish,\n minDepositRate: BigNumberish,\n) {\n const minDepositRateMultiplier = calcMinDepositRateMultiplier(\n minDepositRate,\n seconds,\n );\n const utilization = calcUtilizationRatio(product);\n // Nothing is borrowed, so no interest is paid — but the engine still applies\n // the min deposit multiplier, which is the whole point of a rate floor.\n if (utilization.eq(0)) {\n return toBigNumber(minDepositRateMultiplier - 1);\n }\n const realizedDepositRate = utilization\n .times(calcBorrowRateForTimeRange(product, seconds, toBigNumber(0)))\n .times(BigNumbers.ONE.minus(toBigNumber(interestFeeFrac)));\n return realizedDepositRate\n .plus(BigNumbers.ONE)\n .times(minDepositRateMultiplier)\n .minus(BigNumbers.ONE);\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,kBACd,wBACA,gCACW;CACX,OAAOA,6BAAAA,YAAY,sBAAsB,EAAE,aACzCC,qCAAAA,eAAe,8BAA8B,CAC/C;AACF;;;;AAKA,SAAgB,mBACd,yBACA,iCACW;CACX,OAAOD,6BAAAA,YAAY,uBAAuB,EAAE,aAC1CC,qCAAAA,eAAe,+BAA+B,CAChD;AACF;;;;;;AAOA,SAAgB,qBAAqB,SAAsB;CACzD,IAAI,QAAQ,eAAe,GAAG,CAAC,KAAK,QAAQ,cAAc,GAAG,CAAC,GAC5D,OAAOD,6BAAAA,YAAY,CAAC;CAEtB,OAAO,QAAQ,cAAc,IAAI,EAAE,IAAI,QAAQ,cAAc;AAC/D;;;;;;;;;;;;;;;AAgBA,SAAgB,wBAAwB,SAAsB;CAC5D,MAAM,EACJ,eACA,wBACA,kBACA,qBACE;CACJ,MAAM,cAAc,qBAAqB,OAAO;CAChD,IAAI,YAAY,GAAG,CAAC,GAClB,OAAOA,6BAAAA,YAAY,CAAC;CAEtB,MAAM,iBAAiB,YAAY,GAAG,sBAAsB;CAE5D,IAAI;CACJ,IAAI,gBAAgB;EAClB,MAAM,kBAAkB,iBAAiB,MACvCA,6BAAAA,YAAY,WAAW,EACpB,MAAM,sBAAsB,EAC5B,IAAIE,8BAAAA,WAAW,IAAI,MAAM,sBAAsB,CAAC,CACrD;EACA,aAAa,cAAc,KAAK,gBAAgB,EAAE,KAAK,eAAe;CACxE,OAAO;EACL,MAAM,kBAAkB,YACrB,IAAI,sBAAsB,EAC1B,MAAM,gBAAgB;EACzB,aAAa,cAAc,KAAK,eAAe;CACjD;CAEA,OAAO,WAAW,IAAA,OAAsB;AAC1C;;;;;;;;;AAUA,SAAS,6BACP,gBACA,SACQ;CAIR,QAAQ,IAHUF,6BAAAA,YAAY,cAAc,EACzC,IAAA,OAAsB,EACtB,SACiB,MAAMA,6BAAAA,YAAY,OAAO,EAAE,SAAS;AAC1D;;;;;;;;AASA,SAAgB,2BACd,SACA,SACA,gBACA;CASA,OAAOA,6BAAAA,YARqB,wBAAwB,OAIhC,EAAE,KAAK,CAAC,EAAE,SAAS,KAAKA,6BAAAA,YAAY,OAAO,EAAE,SAAS,IAMtE,6BAA6B,gBAAgB,OAAO,IACpD,CACJ;AACF;;;;;;;;;AAUA,SAAgB,oCACd,SACA,SACA,iBACA,gBACA;CACA,MAAM,2BAA2B,6BAC/B,gBACA,OACF;CACA,MAAM,cAAc,qBAAqB,OAAO;CAGhD,IAAI,YAAY,GAAG,CAAC,GAClB,OAAOA,6BAAAA,YAAY,2BAA2B,CAAC;CAKjD,OAH4B,YACzB,MAAM,2BAA2B,SAAS,SAASA,6BAAAA,YAAY,CAAC,CAAC,CAAC,EAClE,MAAME,8BAAAA,WAAW,IAAI,MAAMF,6BAAAA,YAAY,eAAe,CAAC,CACjC,EACtB,KAAKE,8BAAAA,WAAW,GAAG,EACnB,MAAM,wBAAwB,EAC9B,MAAMA,8BAAAA,WAAW,GAAG;AACzB"}
@@ -1,2 +1,2 @@
1
- import { a as calcTotalDeposited, i as calcTotalBorrowed, n as calcBorrowRatePerSecond, o as calcUtilizationRatio, r as calcRealizedDepositRateForTimeRange, t as calcBorrowRateForTimeRange } from "../interest-BEsEmpAy.cjs";
1
+ import { a as calcTotalDeposited, i as calcTotalBorrowed, n as calcBorrowRatePerSecond, o as calcUtilizationRatio, r as calcRealizedDepositRateForTimeRange, t as calcBorrowRateForTimeRange } from "../interest-BN4bYdpY.cjs";
2
2
  export { calcBorrowRateForTimeRange, calcBorrowRatePerSecond, calcRealizedDepositRateForTimeRange, calcTotalBorrowed, calcTotalDeposited, calcUtilizationRatio };
@@ -1,2 +1,2 @@
1
- import { a as calcTotalDeposited, i as calcTotalBorrowed, n as calcBorrowRatePerSecond, o as calcUtilizationRatio, r as calcRealizedDepositRateForTimeRange, t as calcBorrowRateForTimeRange } from "../interest-DUfNVtPJ.js";
1
+ import { a as calcTotalDeposited, i as calcTotalBorrowed, n as calcBorrowRatePerSecond, o as calcUtilizationRatio, r as calcRealizedDepositRateForTimeRange, t as calcBorrowRateForTimeRange } from "../interest-CcDkLJnv.js";
2
2
  export { calcBorrowRateForTimeRange, calcBorrowRatePerSecond, calcRealizedDepositRateForTimeRange, calcTotalBorrowed, calcTotalDeposited, calcUtilizationRatio };
@@ -54,13 +54,25 @@ function calcBorrowRatePerSecond(product) {
54
54
  return annualRate.div(31536e3);
55
55
  }
56
56
  /**
57
+ * Compounds an annual min deposit rate over a period, giving the multiplier
58
+ * `SpotEngine._updateState` applies to both the borrow and the deposit
59
+ * multiplier: `(1 + minDepositRate / secondsPerYear) ** seconds`.
60
+ *
61
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
62
+ * @param seconds Number of seconds for the time period
63
+ */
64
+ function calcMinDepositRateMultiplier(minDepositRate, seconds) {
65
+ return (1 + toBigNumber(minDepositRate).div(31536e3).toNumber()) ** toBigNumber(seconds).toNumber();
66
+ }
67
+ /**
57
68
  * Calculates borrower interest rate compounded for a period of time.
58
69
  *
59
70
  * @param product Spot product
60
71
  * @param seconds Number of seconds for the time period
72
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
61
73
  */
62
74
  function calcBorrowRateForTimeRange(product, seconds, minDepositRate) {
63
- return toBigNumber(calcBorrowRatePerSecond(product).plus(1).toNumber() ** toBigNumber(seconds).toNumber() - 1).plus(toBigNumber(minDepositRate));
75
+ return toBigNumber(calcBorrowRatePerSecond(product).plus(1).toNumber() ** toBigNumber(seconds).toNumber() * calcMinDepositRateMultiplier(minDepositRate, seconds) - 1);
64
76
  }
65
77
  /**
66
78
  * Calculate depositor interest rate compounded for a period of time.
@@ -68,11 +80,13 @@ function calcBorrowRateForTimeRange(product, seconds, minDepositRate) {
68
80
  * @param product Spot product
69
81
  * @param seconds Number of seconds for the time period
70
82
  * @param interestFeeFrac Fraction of paid borrower interest that is paid as a fee (0.2 = 20% fee)
83
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
71
84
  */
72
85
  function calcRealizedDepositRateForTimeRange(product, seconds, interestFeeFrac, minDepositRate) {
86
+ const minDepositRateMultiplier = calcMinDepositRateMultiplier(minDepositRate, seconds);
73
87
  const utilization = calcUtilizationRatio(product);
74
- if (utilization.eq(0)) return toBigNumber(0);
75
- return utilization.times(calcBorrowRateForTimeRange(product, seconds, toBigNumber(0))).times(BigNumbers.ONE.minus(toBigNumber(interestFeeFrac))).plus(toBigNumber(minDepositRate));
88
+ if (utilization.eq(0)) return toBigNumber(minDepositRateMultiplier - 1);
89
+ return utilization.times(calcBorrowRateForTimeRange(product, seconds, toBigNumber(0))).times(BigNumbers.ONE.minus(toBigNumber(interestFeeFrac))).plus(BigNumbers.ONE).times(minDepositRateMultiplier).minus(BigNumbers.ONE);
76
90
  }
77
91
  //#endregion
78
92
  export { calcBorrowRateForTimeRange, calcBorrowRatePerSecond, calcRealizedDepositRateForTimeRange, calcTotalBorrowed, calcTotalDeposited, calcUtilizationRatio };
@@ -1 +1 @@
1
- {"version":3,"file":"interest.js","names":[],"sources":["../../src/utils/interest.ts"],"sourcesContent":["import BigNumber from 'bignumber.js';\nimport { SpotProduct } from '../types/productTypes';\nimport { BigNumberish, BigNumbers, removeDecimals, toBigNumber } from './math';\nimport { TimeInSeconds } from './time';\n\n/**\n * Calculate amount total borrowed for a product\n */\nexport function calcTotalBorrowed(\n totalBorrowsNormalized: BigNumberish,\n cumulativeBorrowsMultiplierX18: BigNumberish,\n): BigNumber {\n return toBigNumber(totalBorrowsNormalized).multipliedBy(\n removeDecimals(cumulativeBorrowsMultiplierX18),\n );\n}\n\n/**\n * Calculate amount total deposited for a product.\n */\nexport function calcTotalDeposited(\n totalDepositsNormalized: BigNumberish,\n cumulativeDepositsMultiplierX18: BigNumberish,\n): BigNumber {\n return toBigNumber(totalDepositsNormalized).multipliedBy(\n removeDecimals(cumulativeDepositsMultiplierX18),\n );\n}\n\n/**\n * Calculates utilization ratio = abs(total borrowed / total deposited)\n *\n * @param product Spot product\n */\nexport function calcUtilizationRatio(product: SpotProduct) {\n if (product.totalDeposited.eq(0) || product.totalBorrowed.eq(0)) {\n return toBigNumber(0);\n }\n return product.totalBorrowed.abs().div(product.totalDeposited);\n}\n\n/**\n * Calculates per-second borrow interest rate for a product. For example, a returned rate of 0.1 indicates 10% borrower\n * interest. The calculation for interest rate is as follows:\n *\n * If utilization ratio > inflection:\n * annual rate = (utilization ratio - inflection) / (1 - inflection) * interestLargeCap + interestFloor + interestSmallCap\n *\n * If utilization ratio <= inflection:\n * annual rate = utilization ratio * interestSmallCap / inflection + interestFloor\n *\n * The returned rate is annual rate / 31536000 seconds per year.\n *\n * @param product Spot product\n */\nexport function calcBorrowRatePerSecond(product: SpotProduct) {\n const {\n interestFloor,\n interestInflectionUtil,\n interestSmallCap,\n interestLargeCap,\n } = product;\n const utilization = calcUtilizationRatio(product);\n if (utilization.eq(0)) {\n return toBigNumber(0);\n }\n const pastInflection = utilization.gt(interestInflectionUtil);\n\n let annualRate: BigNumber;\n if (pastInflection) {\n const utilizationTerm = interestLargeCap.times(\n toBigNumber(utilization)\n .minus(interestInflectionUtil)\n .div(BigNumbers.ONE.minus(interestInflectionUtil)),\n );\n annualRate = interestFloor.plus(interestSmallCap).plus(utilizationTerm);\n } else {\n const utilizationTerm = utilization\n .div(interestInflectionUtil)\n .times(interestSmallCap);\n annualRate = interestFloor.plus(utilizationTerm);\n }\n\n return annualRate.div(TimeInSeconds.YEAR);\n}\n\n/**\n * Calculates borrower interest rate compounded for a period of time.\n *\n * @param product Spot product\n * @param seconds Number of seconds for the time period\n */\nexport function calcBorrowRateForTimeRange(\n product: SpotProduct,\n seconds: BigNumberish,\n minDepositRate: BigNumberish,\n) {\n const borrowRatePerSecond = calcBorrowRatePerSecond(product);\n\n // Convert to number for this, with some loss of precision, but using `.pow()` causes us to hit browser resource limits\n const borrowRateForTime =\n borrowRatePerSecond.plus(1).toNumber() ** toBigNumber(seconds).toNumber() -\n 1;\n return toBigNumber(borrowRateForTime).plus(toBigNumber(minDepositRate));\n}\n\n/**\n * Calculate depositor interest rate compounded for a period of time.\n *\n * @param product Spot product\n * @param seconds Number of seconds for the time period\n * @param interestFeeFrac Fraction of paid borrower interest that is paid as a fee (0.2 = 20% fee)\n */\nexport function calcRealizedDepositRateForTimeRange(\n product: SpotProduct,\n seconds: BigNumberish,\n interestFeeFrac: BigNumberish,\n minDepositRate: BigNumberish,\n) {\n const utilization = calcUtilizationRatio(product);\n if (utilization.eq(0)) {\n return toBigNumber(0);\n }\n return utilization\n .times(calcBorrowRateForTimeRange(product, seconds, toBigNumber(0)))\n .times(BigNumbers.ONE.minus(toBigNumber(interestFeeFrac)))\n .plus(toBigNumber(minDepositRate));\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,kBACd,wBACA,gCACW;CACX,OAAO,YAAY,sBAAsB,EAAE,aACzC,eAAe,8BAA8B,CAC/C;AACF;;;;AAKA,SAAgB,mBACd,yBACA,iCACW;CACX,OAAO,YAAY,uBAAuB,EAAE,aAC1C,eAAe,+BAA+B,CAChD;AACF;;;;;;AAOA,SAAgB,qBAAqB,SAAsB;CACzD,IAAI,QAAQ,eAAe,GAAG,CAAC,KAAK,QAAQ,cAAc,GAAG,CAAC,GAC5D,OAAO,YAAY,CAAC;CAEtB,OAAO,QAAQ,cAAc,IAAI,EAAE,IAAI,QAAQ,cAAc;AAC/D;;;;;;;;;;;;;;;AAgBA,SAAgB,wBAAwB,SAAsB;CAC5D,MAAM,EACJ,eACA,wBACA,kBACA,qBACE;CACJ,MAAM,cAAc,qBAAqB,OAAO;CAChD,IAAI,YAAY,GAAG,CAAC,GAClB,OAAO,YAAY,CAAC;CAEtB,MAAM,iBAAiB,YAAY,GAAG,sBAAsB;CAE5D,IAAI;CACJ,IAAI,gBAAgB;EAClB,MAAM,kBAAkB,iBAAiB,MACvC,YAAY,WAAW,EACpB,MAAM,sBAAsB,EAC5B,IAAI,WAAW,IAAI,MAAM,sBAAsB,CAAC,CACrD;EACA,aAAa,cAAc,KAAK,gBAAgB,EAAE,KAAK,eAAe;CACxE,OAAO;EACL,MAAM,kBAAkB,YACrB,IAAI,sBAAsB,EAC1B,MAAM,gBAAgB;EACzB,aAAa,cAAc,KAAK,eAAe;CACjD;CAEA,OAAO,WAAW,IAAA,OAAsB;AAC1C;;;;;;;AAQA,SAAgB,2BACd,SACA,SACA,gBACA;CAOA,OAAO,YANqB,wBAAwB,OAIhC,EAAE,KAAK,CAAC,EAAE,SAAS,KAAK,YAAY,OAAO,EAAE,SAAS,IACxE,CACkC,EAAE,KAAK,YAAY,cAAc,CAAC;AACxE;;;;;;;;AASA,SAAgB,oCACd,SACA,SACA,iBACA,gBACA;CACA,MAAM,cAAc,qBAAqB,OAAO;CAChD,IAAI,YAAY,GAAG,CAAC,GAClB,OAAO,YAAY,CAAC;CAEtB,OAAO,YACJ,MAAM,2BAA2B,SAAS,SAAS,YAAY,CAAC,CAAC,CAAC,EAClE,MAAM,WAAW,IAAI,MAAM,YAAY,eAAe,CAAC,CAAC,EACxD,KAAK,YAAY,cAAc,CAAC;AACrC"}
1
+ {"version":3,"file":"interest.js","names":[],"sources":["../../src/utils/interest.ts"],"sourcesContent":["import BigNumber from 'bignumber.js';\nimport { SpotProduct } from '../types/productTypes';\nimport { BigNumberish, BigNumbers, removeDecimals, toBigNumber } from './math';\nimport { TimeInSeconds } from './time';\n\n/**\n * Calculate amount total borrowed for a product\n */\nexport function calcTotalBorrowed(\n totalBorrowsNormalized: BigNumberish,\n cumulativeBorrowsMultiplierX18: BigNumberish,\n): BigNumber {\n return toBigNumber(totalBorrowsNormalized).multipliedBy(\n removeDecimals(cumulativeBorrowsMultiplierX18),\n );\n}\n\n/**\n * Calculate amount total deposited for a product.\n */\nexport function calcTotalDeposited(\n totalDepositsNormalized: BigNumberish,\n cumulativeDepositsMultiplierX18: BigNumberish,\n): BigNumber {\n return toBigNumber(totalDepositsNormalized).multipliedBy(\n removeDecimals(cumulativeDepositsMultiplierX18),\n );\n}\n\n/**\n * Calculates utilization ratio = abs(total borrowed / total deposited)\n *\n * @param product Spot product\n */\nexport function calcUtilizationRatio(product: SpotProduct) {\n if (product.totalDeposited.eq(0) || product.totalBorrowed.eq(0)) {\n return toBigNumber(0);\n }\n return product.totalBorrowed.abs().div(product.totalDeposited);\n}\n\n/**\n * Calculates per-second borrow interest rate for a product. For example, a returned rate of 0.1 indicates 10% borrower\n * interest. The calculation for interest rate is as follows:\n *\n * If utilization ratio > inflection:\n * annual rate = (utilization ratio - inflection) / (1 - inflection) * interestLargeCap + interestFloor + interestSmallCap\n *\n * If utilization ratio <= inflection:\n * annual rate = utilization ratio * interestSmallCap / inflection + interestFloor\n *\n * The returned rate is annual rate / 31536000 seconds per year.\n *\n * @param product Spot product\n */\nexport function calcBorrowRatePerSecond(product: SpotProduct) {\n const {\n interestFloor,\n interestInflectionUtil,\n interestSmallCap,\n interestLargeCap,\n } = product;\n const utilization = calcUtilizationRatio(product);\n if (utilization.eq(0)) {\n return toBigNumber(0);\n }\n const pastInflection = utilization.gt(interestInflectionUtil);\n\n let annualRate: BigNumber;\n if (pastInflection) {\n const utilizationTerm = interestLargeCap.times(\n toBigNumber(utilization)\n .minus(interestInflectionUtil)\n .div(BigNumbers.ONE.minus(interestInflectionUtil)),\n );\n annualRate = interestFloor.plus(interestSmallCap).plus(utilizationTerm);\n } else {\n const utilizationTerm = utilization\n .div(interestInflectionUtil)\n .times(interestSmallCap);\n annualRate = interestFloor.plus(utilizationTerm);\n }\n\n return annualRate.div(TimeInSeconds.YEAR);\n}\n\n/**\n * Compounds an annual min deposit rate over a period, giving the multiplier\n * `SpotEngine._updateState` applies to both the borrow and the deposit\n * multiplier: `(1 + minDepositRate / secondsPerYear) ** seconds`.\n *\n * @param minDepositRate Annual min deposit rate, as stored in the product config\n * @param seconds Number of seconds for the time period\n */\nfunction calcMinDepositRateMultiplier(\n minDepositRate: BigNumberish,\n seconds: BigNumberish,\n): number {\n const perSecond = toBigNumber(minDepositRate)\n .div(TimeInSeconds.YEAR)\n .toNumber();\n return (1 + perSecond) ** toBigNumber(seconds).toNumber();\n}\n\n/**\n * Calculates borrower interest rate compounded for a period of time.\n *\n * @param product Spot product\n * @param seconds Number of seconds for the time period\n * @param minDepositRate Annual min deposit rate, as stored in the product config\n */\nexport function calcBorrowRateForTimeRange(\n product: SpotProduct,\n seconds: BigNumberish,\n minDepositRate: BigNumberish,\n) {\n const borrowRatePerSecond = calcBorrowRatePerSecond(product);\n\n // Convert to number for this, with some loss of precision, but using `.pow()` causes us to hit browser resource limits\n const borrowRateMultiplier =\n borrowRatePerSecond.plus(1).toNumber() ** toBigNumber(seconds).toNumber();\n // The engine multiplies the borrow multiplier by the min deposit multiplier,\n // so the min deposit rate has to be compounded over the same period rather\n // than added as an annual figure.\n return toBigNumber(\n borrowRateMultiplier *\n calcMinDepositRateMultiplier(minDepositRate, seconds) -\n 1,\n );\n}\n\n/**\n * Calculate depositor interest rate compounded for a period of time.\n *\n * @param product Spot product\n * @param seconds Number of seconds for the time period\n * @param interestFeeFrac Fraction of paid borrower interest that is paid as a fee (0.2 = 20% fee)\n * @param minDepositRate Annual min deposit rate, as stored in the product config\n */\nexport function calcRealizedDepositRateForTimeRange(\n product: SpotProduct,\n seconds: BigNumberish,\n interestFeeFrac: BigNumberish,\n minDepositRate: BigNumberish,\n) {\n const minDepositRateMultiplier = calcMinDepositRateMultiplier(\n minDepositRate,\n seconds,\n );\n const utilization = calcUtilizationRatio(product);\n // Nothing is borrowed, so no interest is paid — but the engine still applies\n // the min deposit multiplier, which is the whole point of a rate floor.\n if (utilization.eq(0)) {\n return toBigNumber(minDepositRateMultiplier - 1);\n }\n const realizedDepositRate = utilization\n .times(calcBorrowRateForTimeRange(product, seconds, toBigNumber(0)))\n .times(BigNumbers.ONE.minus(toBigNumber(interestFeeFrac)));\n return realizedDepositRate\n .plus(BigNumbers.ONE)\n .times(minDepositRateMultiplier)\n .minus(BigNumbers.ONE);\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,kBACd,wBACA,gCACW;CACX,OAAO,YAAY,sBAAsB,EAAE,aACzC,eAAe,8BAA8B,CAC/C;AACF;;;;AAKA,SAAgB,mBACd,yBACA,iCACW;CACX,OAAO,YAAY,uBAAuB,EAAE,aAC1C,eAAe,+BAA+B,CAChD;AACF;;;;;;AAOA,SAAgB,qBAAqB,SAAsB;CACzD,IAAI,QAAQ,eAAe,GAAG,CAAC,KAAK,QAAQ,cAAc,GAAG,CAAC,GAC5D,OAAO,YAAY,CAAC;CAEtB,OAAO,QAAQ,cAAc,IAAI,EAAE,IAAI,QAAQ,cAAc;AAC/D;;;;;;;;;;;;;;;AAgBA,SAAgB,wBAAwB,SAAsB;CAC5D,MAAM,EACJ,eACA,wBACA,kBACA,qBACE;CACJ,MAAM,cAAc,qBAAqB,OAAO;CAChD,IAAI,YAAY,GAAG,CAAC,GAClB,OAAO,YAAY,CAAC;CAEtB,MAAM,iBAAiB,YAAY,GAAG,sBAAsB;CAE5D,IAAI;CACJ,IAAI,gBAAgB;EAClB,MAAM,kBAAkB,iBAAiB,MACvC,YAAY,WAAW,EACpB,MAAM,sBAAsB,EAC5B,IAAI,WAAW,IAAI,MAAM,sBAAsB,CAAC,CACrD;EACA,aAAa,cAAc,KAAK,gBAAgB,EAAE,KAAK,eAAe;CACxE,OAAO;EACL,MAAM,kBAAkB,YACrB,IAAI,sBAAsB,EAC1B,MAAM,gBAAgB;EACzB,aAAa,cAAc,KAAK,eAAe;CACjD;CAEA,OAAO,WAAW,IAAA,OAAsB;AAC1C;;;;;;;;;AAUA,SAAS,6BACP,gBACA,SACQ;CAIR,QAAQ,IAHU,YAAY,cAAc,EACzC,IAAA,OAAsB,EACtB,SACiB,MAAM,YAAY,OAAO,EAAE,SAAS;AAC1D;;;;;;;;AASA,SAAgB,2BACd,SACA,SACA,gBACA;CASA,OAAO,YARqB,wBAAwB,OAIhC,EAAE,KAAK,CAAC,EAAE,SAAS,KAAK,YAAY,OAAO,EAAE,SAAS,IAMtE,6BAA6B,gBAAgB,OAAO,IACpD,CACJ;AACF;;;;;;;;;AAUA,SAAgB,oCACd,SACA,SACA,iBACA,gBACA;CACA,MAAM,2BAA2B,6BAC/B,gBACA,OACF;CACA,MAAM,cAAc,qBAAqB,OAAO;CAGhD,IAAI,YAAY,GAAG,CAAC,GAClB,OAAO,YAAY,2BAA2B,CAAC;CAKjD,OAH4B,YACzB,MAAM,2BAA2B,SAAS,SAAS,YAAY,CAAC,CAAC,CAAC,EAClE,MAAM,WAAW,IAAI,MAAM,YAAY,eAAe,CAAC,CACjC,EACtB,KAAK,WAAW,GAAG,EACnB,MAAM,wBAAwB,EAC9B,MAAM,WAAW,GAAG;AACzB"}
@@ -4,5 +4,5 @@ import { t as clampBigNumber } from "../../clamp-D9lICt7X.cjs";
4
4
  import { n as addDecimals, r as removeDecimals, t as NADO_PRODUCT_DECIMALS } from "../../decimalAdjustment-DCwilVOA.cjs";
5
5
  import { t as sumBigNumberBy } from "../../sumBigNumberBy-BvDeov4-.cjs";
6
6
  import { t as toBigInt } from "../../toBigInt-BDiOjNl-.cjs";
7
- import { t as toIntegerString } from "../../toIntegerString-BUESqiY1.cjs";
7
+ import { t as toIntegerString } from "../../toIntegerString-XmD3bQt-.cjs";
8
8
  export { BigNumberish, BigNumbers, NADO_PRODUCT_DECIMALS, addDecimals, clampBigNumber, removeDecimals, sumBigNumberBy, toBigInt, toBigNumber, toIntegerString };
@@ -4,5 +4,5 @@ import { t as clampBigNumber } from "../../clamp-D9lICt7X.js";
4
4
  import { n as addDecimals, r as removeDecimals, t as NADO_PRODUCT_DECIMALS } from "../../decimalAdjustment-pLqKqD8x.js";
5
5
  import { t as sumBigNumberBy } from "../../sumBigNumberBy-BvDeov4-.js";
6
6
  import { t as toBigInt } from "../../toBigInt-CBRk3mgz.js";
7
- import { t as toIntegerString } from "../../toIntegerString-CyhOUxbE.js";
7
+ import { t as toIntegerString } from "../../toIntegerString-DfbD1c3k.js";
8
8
  export { BigNumberish, BigNumbers, NADO_PRODUCT_DECIMALS, addDecimals, clampBigNumber, removeDecimals, sumBigNumberBy, toBigInt, toBigNumber, toIntegerString };
@@ -2,7 +2,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_utils_math_bigNumber = require("./bigNumber.cjs");
3
3
  //#region src/utils/math/toIntegerString.ts
4
4
  /**
5
- * Converts a BigNumberish value to string
5
+ * Converts a BigNumberish value to an integer string, rounding any fractional
6
+ * part to the nearest integer (bignumber.js `.toFixed(0)` default rounding mode).
6
7
  *
7
8
  * @param val
8
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"toIntegerString.cjs","names":["toBigNumber"],"sources":["../../../src/utils/math/toIntegerString.ts"],"sourcesContent":["import { BigNumberish, toBigNumber } from './bigNumber';\n\n/**\n * Converts a BigNumberish value to string\n *\n * @param val\n */\nexport function toIntegerString(val: BigNumberish): string {\n // toFixed is required as toString gives values with `e`\n return toBigNumber(val).toFixed(0);\n}\n"],"mappings":";;;;;;;;AAOA,SAAgB,gBAAgB,KAA2B;CAEzD,OAAOA,6BAAAA,YAAY,GAAG,EAAE,QAAQ,CAAC;AACnC"}
1
+ {"version":3,"file":"toIntegerString.cjs","names":["toBigNumber"],"sources":["../../../src/utils/math/toIntegerString.ts"],"sourcesContent":["import { BigNumberish, toBigNumber } from './bigNumber';\n\n/**\n * Converts a BigNumberish value to an integer string, rounding any fractional\n * part to the nearest integer (bignumber.js `.toFixed(0)` default rounding mode).\n *\n * @param val\n */\nexport function toIntegerString(val: BigNumberish): string {\n // toFixed is required as toString gives values with `e`\n return toBigNumber(val).toFixed(0);\n}\n"],"mappings":";;;;;;;;;AAQA,SAAgB,gBAAgB,KAA2B;CAEzD,OAAOA,6BAAAA,YAAY,GAAG,EAAE,QAAQ,CAAC;AACnC"}
@@ -1,2 +1,2 @@
1
- import { t as toIntegerString } from "../../toIntegerString-BUESqiY1.cjs";
1
+ import { t as toIntegerString } from "../../toIntegerString-XmD3bQt-.cjs";
2
2
  export { toIntegerString };
@@ -1,2 +1,2 @@
1
- import { t as toIntegerString } from "../../toIntegerString-CyhOUxbE.js";
1
+ import { t as toIntegerString } from "../../toIntegerString-DfbD1c3k.js";
2
2
  export { toIntegerString };
@@ -1,7 +1,8 @@
1
1
  import { toBigNumber } from "./bigNumber.js";
2
2
  //#region src/utils/math/toIntegerString.ts
3
3
  /**
4
- * Converts a BigNumberish value to string
4
+ * Converts a BigNumberish value to an integer string, rounding any fractional
5
+ * part to the nearest integer (bignumber.js `.toFixed(0)` default rounding mode).
5
6
  *
6
7
  * @param val
7
8
  */
@@ -1 +1 @@
1
- {"version":3,"file":"toIntegerString.js","names":[],"sources":["../../../src/utils/math/toIntegerString.ts"],"sourcesContent":["import { BigNumberish, toBigNumber } from './bigNumber';\n\n/**\n * Converts a BigNumberish value to string\n *\n * @param val\n */\nexport function toIntegerString(val: BigNumberish): string {\n // toFixed is required as toString gives values with `e`\n return toBigNumber(val).toFixed(0);\n}\n"],"mappings":";;;;;;;AAOA,SAAgB,gBAAgB,KAA2B;CAEzD,OAAO,YAAY,GAAG,EAAE,QAAQ,CAAC;AACnC"}
1
+ {"version":3,"file":"toIntegerString.js","names":[],"sources":["../../../src/utils/math/toIntegerString.ts"],"sourcesContent":["import { BigNumberish, toBigNumber } from './bigNumber';\n\n/**\n * Converts a BigNumberish value to an integer string, rounding any fractional\n * part to the nearest integer (bignumber.js `.toFixed(0)` default rounding mode).\n *\n * @param val\n */\nexport function toIntegerString(val: BigNumberish): string {\n // toFixed is required as toString gives values with `e`\n return toBigNumber(val).toFixed(0);\n}\n"],"mappings":";;;;;;;;AAQA,SAAgB,gBAAgB,KAA2B;CAEzD,OAAO,YAAY,GAAG,EAAE,QAAQ,CAAC;AACnC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nadohq/shared",
3
- "version": "0.65.0",
3
+ "version": "0.67.0",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "description": "Shared utilities, types, and contract helpers for Nado SDK",
@@ -52,5 +52,5 @@
52
52
  "bignumber.js": "11.1.1",
53
53
  "viem": "2.52.0"
54
54
  },
55
- "gitHead": "12b716e33133073ea5012fcbcf8f4ba2fd7335c5"
55
+ "gitHead": "58263d0bb5d08e10371a917e5c16d61cc81bb937"
56
56
  }
@@ -0,0 +1,123 @@
1
+ import { describe, expect, it } from '@jest/globals';
2
+ import BigNumber from 'bignumber.js';
3
+ import { SpotProduct } from '../types/productTypes';
4
+ import {
5
+ calcBorrowRateForTimeRange,
6
+ calcBorrowRatePerSecond,
7
+ calcRealizedDepositRateForTimeRange,
8
+ } from './interest';
9
+ import { TimeInSeconds } from './time';
10
+
11
+ const MIN_DEPOSIT_RATE = 0.05;
12
+
13
+ function spotProduct(overrides: Partial<SpotProduct> = {}) {
14
+ return {
15
+ interestFloor: new BigNumber(0.01),
16
+ interestInflectionUtil: new BigNumber(0.8),
17
+ interestSmallCap: new BigNumber(0.04),
18
+ interestLargeCap: new BigNumber(0.4),
19
+ minDepositRate: new BigNumber(MIN_DEPOSIT_RATE),
20
+ totalDeposited: new BigNumber(1_000_000),
21
+ totalBorrowed: new BigNumber(-500_000),
22
+ ...overrides,
23
+ } as SpotProduct;
24
+ }
25
+
26
+ /**
27
+ * `SpotEngine._updateState` compounds the min deposit rate over the period and
28
+ * multiplies it into the borrow multiplier, rather than adding the annual rate.
29
+ */
30
+ function onChainBorrowRate(product: SpotProduct, seconds: number) {
31
+ const borrowRatePerSecond = calcBorrowRatePerSecond(product).toNumber();
32
+ const minDepositRatePerSecond = MIN_DEPOSIT_RATE / TimeInSeconds.YEAR;
33
+ return (
34
+ (1 + borrowRatePerSecond) ** seconds *
35
+ (1 + minDepositRatePerSecond) ** seconds -
36
+ 1
37
+ );
38
+ }
39
+
40
+ describe('interest', () => {
41
+ const periods: [string, number][] = [
42
+ ['an hour', TimeInSeconds.HOUR],
43
+ ['a day', TimeInSeconds.DAY],
44
+ ['thirty days', TimeInSeconds.DAY * 30],
45
+ ['a year', TimeInSeconds.YEAR],
46
+ ];
47
+
48
+ describe('calcBorrowRateForTimeRange', () => {
49
+ it.each(periods)(
50
+ 'compounds the min deposit rate over %s rather than adding it',
51
+ (_label, seconds) => {
52
+ const product = spotProduct();
53
+ expect(
54
+ calcBorrowRateForTimeRange(
55
+ product,
56
+ seconds,
57
+ product.minDepositRate,
58
+ ).toNumber(),
59
+ ).toBeCloseTo(onChainBorrowRate(product, seconds), 10);
60
+ },
61
+ );
62
+
63
+ it('stays below the annual min deposit rate for a short period', () => {
64
+ const product = spotProduct();
65
+ // Adding the annual rate unscaled put an hour of borrowing above 5%.
66
+ expect(
67
+ calcBorrowRateForTimeRange(
68
+ product,
69
+ TimeInSeconds.HOUR,
70
+ product.minDepositRate,
71
+ ).toNumber(),
72
+ ).toBeLessThan(MIN_DEPOSIT_RATE / 100);
73
+ });
74
+
75
+ it('returns only the borrow rate when there is no min deposit rate', () => {
76
+ const product = spotProduct();
77
+ const borrowRatePerSecond = calcBorrowRatePerSecond(product).toNumber();
78
+ expect(
79
+ calcBorrowRateForTimeRange(product, TimeInSeconds.DAY, 0).toNumber(),
80
+ ).toBeCloseTo((1 + borrowRatePerSecond) ** TimeInSeconds.DAY - 1, 12);
81
+ });
82
+ });
83
+
84
+ describe('calcRealizedDepositRateForTimeRange', () => {
85
+ it('still pays the min deposit rate when nothing is borrowed', () => {
86
+ const product = spotProduct({ totalBorrowed: new BigNumber(0) });
87
+ const expected =
88
+ (1 + MIN_DEPOSIT_RATE / TimeInSeconds.YEAR) ** TimeInSeconds.DAY - 1;
89
+ expect(
90
+ calcRealizedDepositRateForTimeRange(
91
+ product,
92
+ TimeInSeconds.DAY,
93
+ 0.2,
94
+ product.minDepositRate,
95
+ ).toNumber(),
96
+ ).toBeCloseTo(expected, 12);
97
+ });
98
+
99
+ it('compounds the min deposit rate on top of the realized rate', () => {
100
+ const product = spotProduct();
101
+ const seconds = TimeInSeconds.DAY;
102
+ const utilization = 0.5;
103
+ const borrowRate = calcBorrowRateForTimeRange(
104
+ product,
105
+ seconds,
106
+ 0,
107
+ ).toNumber();
108
+ const realized = utilization * borrowRate * (1 - 0.2);
109
+ const expected =
110
+ (1 + realized) *
111
+ (1 + MIN_DEPOSIT_RATE / TimeInSeconds.YEAR) ** seconds -
112
+ 1;
113
+ expect(
114
+ calcRealizedDepositRateForTimeRange(
115
+ product,
116
+ seconds,
117
+ 0.2,
118
+ product.minDepositRate,
119
+ ).toNumber(),
120
+ ).toBeCloseTo(expected, 12);
121
+ });
122
+ });
123
+ });
@@ -84,11 +84,30 @@ export function calcBorrowRatePerSecond(product: SpotProduct) {
84
84
  return annualRate.div(TimeInSeconds.YEAR);
85
85
  }
86
86
 
87
+ /**
88
+ * Compounds an annual min deposit rate over a period, giving the multiplier
89
+ * `SpotEngine._updateState` applies to both the borrow and the deposit
90
+ * multiplier: `(1 + minDepositRate / secondsPerYear) ** seconds`.
91
+ *
92
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
93
+ * @param seconds Number of seconds for the time period
94
+ */
95
+ function calcMinDepositRateMultiplier(
96
+ minDepositRate: BigNumberish,
97
+ seconds: BigNumberish,
98
+ ): number {
99
+ const perSecond = toBigNumber(minDepositRate)
100
+ .div(TimeInSeconds.YEAR)
101
+ .toNumber();
102
+ return (1 + perSecond) ** toBigNumber(seconds).toNumber();
103
+ }
104
+
87
105
  /**
88
106
  * Calculates borrower interest rate compounded for a period of time.
89
107
  *
90
108
  * @param product Spot product
91
109
  * @param seconds Number of seconds for the time period
110
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
92
111
  */
93
112
  export function calcBorrowRateForTimeRange(
94
113
  product: SpotProduct,
@@ -98,10 +117,16 @@ export function calcBorrowRateForTimeRange(
98
117
  const borrowRatePerSecond = calcBorrowRatePerSecond(product);
99
118
 
100
119
  // Convert to number for this, with some loss of precision, but using `.pow()` causes us to hit browser resource limits
101
- const borrowRateForTime =
102
- borrowRatePerSecond.plus(1).toNumber() ** toBigNumber(seconds).toNumber() -
103
- 1;
104
- return toBigNumber(borrowRateForTime).plus(toBigNumber(minDepositRate));
120
+ const borrowRateMultiplier =
121
+ borrowRatePerSecond.plus(1).toNumber() ** toBigNumber(seconds).toNumber();
122
+ // The engine multiplies the borrow multiplier by the min deposit multiplier,
123
+ // so the min deposit rate has to be compounded over the same period rather
124
+ // than added as an annual figure.
125
+ return toBigNumber(
126
+ borrowRateMultiplier *
127
+ calcMinDepositRateMultiplier(minDepositRate, seconds) -
128
+ 1,
129
+ );
105
130
  }
106
131
 
107
132
  /**
@@ -110,6 +135,7 @@ export function calcBorrowRateForTimeRange(
110
135
  * @param product Spot product
111
136
  * @param seconds Number of seconds for the time period
112
137
  * @param interestFeeFrac Fraction of paid borrower interest that is paid as a fee (0.2 = 20% fee)
138
+ * @param minDepositRate Annual min deposit rate, as stored in the product config
113
139
  */
114
140
  export function calcRealizedDepositRateForTimeRange(
115
141
  product: SpotProduct,
@@ -117,12 +143,21 @@ export function calcRealizedDepositRateForTimeRange(
117
143
  interestFeeFrac: BigNumberish,
118
144
  minDepositRate: BigNumberish,
119
145
  ) {
146
+ const minDepositRateMultiplier = calcMinDepositRateMultiplier(
147
+ minDepositRate,
148
+ seconds,
149
+ );
120
150
  const utilization = calcUtilizationRatio(product);
151
+ // Nothing is borrowed, so no interest is paid — but the engine still applies
152
+ // the min deposit multiplier, which is the whole point of a rate floor.
121
153
  if (utilization.eq(0)) {
122
- return toBigNumber(0);
154
+ return toBigNumber(minDepositRateMultiplier - 1);
123
155
  }
124
- return utilization
156
+ const realizedDepositRate = utilization
125
157
  .times(calcBorrowRateForTimeRange(product, seconds, toBigNumber(0)))
126
- .times(BigNumbers.ONE.minus(toBigNumber(interestFeeFrac)))
127
- .plus(toBigNumber(minDepositRate));
158
+ .times(BigNumbers.ONE.minus(toBigNumber(interestFeeFrac)));
159
+ return realizedDepositRate
160
+ .plus(BigNumbers.ONE)
161
+ .times(minDepositRateMultiplier)
162
+ .minus(BigNumbers.ONE);
128
163
  }
@@ -1,7 +1,8 @@
1
1
  import { BigNumberish, toBigNumber } from './bigNumber';
2
2
 
3
3
  /**
4
- * Converts a BigNumberish value to string
4
+ * Converts a BigNumberish value to an integer string, rounding any fractional
5
+ * part to the nearest integer (bignumber.js `.toFixed(0)` default rounding mode).
5
6
  *
6
7
  * @param val
7
8
  */
@@ -1 +0,0 @@
1
- {"version":3,"file":"toIntegerString-BUESqiY1.d.cts","names":[],"sources":["../src/utils/math/src/utils/math/toIntegerString.ts"],"mappings":";;;;;AAOA;;;iBAAgB,eAAA,CAAgB,GAAiB,EAAZ,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"toIntegerString-CyhOUxbE.d.ts","names":[],"sources":["../src/utils/math/src/utils/math/toIntegerString.ts"],"mappings":";;;;;AAOA;;;iBAAgB,eAAA,CAAgB,GAAiB,EAAZ,YAAY"}