@gearbox-protocol/sdk 3.0.0-vfourabi.1 → 3.0.0-vfourabi.3

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 (275) hide show
  1. package/.eslintignore +3 -0
  2. package/.github/workflows/lint_pr.yml +18 -0
  3. package/.github/workflows/pr.yml +39 -0
  4. package/.github/workflows/release.yml +47 -0
  5. package/.gitignore +19 -0
  6. package/.husky/commit-msg +1 -0
  7. package/.husky/pre-commit +1 -0
  8. package/.nvmrc +1 -0
  9. package/.releaserc.json +34 -0
  10. package/.vscode/extensions.json +7 -0
  11. package/.vscode/settings.json +16 -0
  12. package/.yarn/install-state.gz +0 -0
  13. package/.yarnrc.yml +1 -0
  14. package/ContributionAgreement +72 -0
  15. package/dist/cjs/abi/iZapper.cjs +85 -0
  16. package/dist/cjs/abi/iZapper.d.ts +123 -0
  17. package/dist/cjs/sdk/index.cjs +19830 -19764
  18. package/dist/cjs/sdk/index.d.ts +3746 -3061
  19. package/dist/esm/abi/iZapper.d.mts +123 -0
  20. package/dist/esm/abi/iZapper.mjs +83 -0
  21. package/dist/esm/sdk/index.d.mts +3746 -3061
  22. package/dist/esm/sdk/index.mjs +19807 -19765
  23. package/jest.config.js +7 -0
  24. package/package.json +32 -2
  25. package/scripts/example.ts +30 -0
  26. package/src/abi/compressors.ts +3962 -0
  27. package/src/abi/errors.ts +196 -0
  28. package/src/abi/iERC20.ts +122 -0
  29. package/src/abi/iPausable.ts +35 -0
  30. package/src/abi/iPriceFeedStore.ts +227 -0
  31. package/src/abi/iUpdatablePriceFeed.ts +16 -0
  32. package/src/abi/iVersion.ts +28 -0
  33. package/src/abi/iZapper.ts +84 -0
  34. package/src/abi/routerV300.ts +378 -0
  35. package/src/abi/v300.ts +4151 -0
  36. package/src/abi/v310.ts +5179 -0
  37. package/src/adapters/AbstractAdapter.ts +28 -0
  38. package/src/adapters/BalancerV2VaultAdapterContract.ts +19 -0
  39. package/src/adapters/BalancerV3RouterAdapterContract.ts +19 -0
  40. package/src/adapters/CamelotV3AdapterContract.ts +20 -0
  41. package/src/adapters/ConvexV1BaseRewardPoolAdapterContract.ts +20 -0
  42. package/src/adapters/ConvexV1BoosterAdapterContract.ts +20 -0
  43. package/src/adapters/Curve2AssetsAdapterContract.ts +20 -0
  44. package/src/adapters/Curve3AssetsAdapterContract.ts +20 -0
  45. package/src/adapters/Curve4AssetsAdapterContract.ts +20 -0
  46. package/src/adapters/CurveV1AdapterStETHContract.ts +20 -0
  47. package/src/adapters/CurveV1StableNGAdapterContract.ts +20 -0
  48. package/src/adapters/DaiUsdsAdapterContract.ts +21 -0
  49. package/src/adapters/ERC4626AdapterContract.ts +20 -0
  50. package/src/adapters/MellowERC4626VaultAdapterContract.ts +20 -0
  51. package/src/adapters/MellowVaultAdapterContract.ts +20 -0
  52. package/src/adapters/PendleRouterAdapterContract.ts +21 -0
  53. package/src/adapters/StakingRewardsAdapterContract.ts +21 -0
  54. package/src/adapters/UniswapV2AdapterContract.ts +20 -0
  55. package/src/adapters/UniswapV3AdapterContract.ts +170 -0
  56. package/src/adapters/VelodromeV2AdapterContract.ts +20 -0
  57. package/src/adapters/WstETHV1AdapterContract.ts +20 -0
  58. package/src/adapters/YearnV2AdapterContract.ts +20 -0
  59. package/src/adapters/abi/adapters.ts +4325 -0
  60. package/src/adapters/abi/iBalancerV3RouterAdapter.ts +126 -0
  61. package/src/adapters/abi/index.ts +2 -0
  62. package/src/adapters/index.ts +2 -0
  63. package/src/adapters/plugin.ts +90 -0
  64. package/src/adapters/types.ts +62 -0
  65. package/src/dev/AccountOpener.ts +611 -0
  66. package/src/dev/PriceFeedStore.ts +95 -0
  67. package/src/dev/SDKExample.ts +167 -0
  68. package/src/dev/abi.ts +14 -0
  69. package/src/dev/calcLiquidatableLTs.ts +77 -0
  70. package/src/dev/createAnvilClient.ts +155 -0
  71. package/src/dev/index.ts +7 -0
  72. package/src/dev/setLTZero.ts +127 -0
  73. package/src/dev/setLTs.ts +68 -0
  74. package/src/sdk/GearboxSDK.ts +488 -0
  75. package/src/sdk/abi/index.ts +1 -0
  76. package/src/sdk/abi/oracles.ts +4614 -0
  77. package/src/sdk/accounts/CreditAccountsService.ts +1123 -0
  78. package/src/sdk/accounts/index.ts +1 -0
  79. package/src/sdk/base/AddressLabeller.ts +35 -0
  80. package/src/sdk/base/BaseContract.ts +237 -0
  81. package/src/sdk/base/IAddressLabeller.ts +13 -0
  82. package/src/sdk/base/PlaceholderContract.ts +12 -0
  83. package/src/sdk/base/SDKConstruct.ts +35 -0
  84. package/src/sdk/base/TokensMeta.ts +45 -0
  85. package/src/sdk/base/index.ts +6 -0
  86. package/src/sdk/base/types.ts +102 -0
  87. package/src/sdk/bots/BotsService.ts +92 -0
  88. package/src/sdk/bots/index.ts +2 -0
  89. package/src/sdk/bots/utils.ts +19 -0
  90. package/src/sdk/chain/Provider.ts +99 -0
  91. package/src/sdk/chain/chains.ts +41 -0
  92. package/src/sdk/chain/detectChain.ts +21 -0
  93. package/src/sdk/chain/index.ts +3 -0
  94. package/src/sdk/constants/address-provider.ts +47 -0
  95. package/src/sdk/constants/addresses.ts +82 -0
  96. package/src/sdk/constants/bot-permissions.ts +36 -0
  97. package/src/sdk/constants/index.ts +6 -0
  98. package/src/sdk/constants/math.ts +23 -0
  99. package/src/sdk/constants/networks.ts +38 -0
  100. package/src/sdk/constants/periphery.ts +7 -0
  101. package/src/sdk/core/BotListV3Contract.ts +103 -0
  102. package/src/sdk/core/GearStakingV3Contract.ts +84 -0
  103. package/src/sdk/core/address-provider/AbstractAddressProviderContract.ts +101 -0
  104. package/src/sdk/core/address-provider/AddressProviderV3Contract.ts +105 -0
  105. package/src/sdk/core/address-provider/AddressProviderV3_1Contract.ts +92 -0
  106. package/src/sdk/core/address-provider/getAddressProvider.ts +45 -0
  107. package/src/sdk/core/address-provider/index.ts +4 -0
  108. package/src/sdk/core/address-provider/types.ts +17 -0
  109. package/src/sdk/core/index.ts +3 -0
  110. package/src/sdk/gauges/GaugeStakingService.ts +100 -0
  111. package/src/sdk/gauges/index.ts +2 -0
  112. package/src/sdk/gauges/utils.ts +25 -0
  113. package/src/sdk/index.ts +17 -0
  114. package/src/sdk/market/MarketConfiguratorContract.ts +78 -0
  115. package/src/sdk/market/MarketRegister.ts +330 -0
  116. package/src/sdk/market/MarketSuite.ts +83 -0
  117. package/src/sdk/market/adapters/PlaceholderAdapterContracts.ts +18 -0
  118. package/src/sdk/market/adapters/factory.ts +34 -0
  119. package/src/sdk/market/adapters/index.ts +3 -0
  120. package/src/sdk/market/adapters/types.ts +7 -0
  121. package/src/sdk/market/credit/CreditConfiguratorV300Contract.ts +164 -0
  122. package/src/sdk/market/credit/CreditConfiguratorV310Contract.ts +139 -0
  123. package/src/sdk/market/credit/CreditFacadeV300Contract.ts +206 -0
  124. package/src/sdk/market/credit/CreditFacadeV310Contract.ts +190 -0
  125. package/src/sdk/market/credit/CreditManagerV300Contract.ts +122 -0
  126. package/src/sdk/market/credit/CreditManagerV310Contract.ts +122 -0
  127. package/src/sdk/market/credit/CreditSuite.ts +65 -0
  128. package/src/sdk/market/credit/createCreditConfigurator.ts +18 -0
  129. package/src/sdk/market/credit/createCreditFacade.ts +18 -0
  130. package/src/sdk/market/credit/createCreditManager.ts +18 -0
  131. package/src/sdk/market/credit/index.ts +8 -0
  132. package/src/sdk/market/credit/types.ts +42 -0
  133. package/src/sdk/market/index.ts +7 -0
  134. package/src/sdk/market/oracle/PriceOracleBaseContract.ts +376 -0
  135. package/src/sdk/market/oracle/PriceOracleV300Contract.ts +71 -0
  136. package/src/sdk/market/oracle/PriceOracleV310Contract.ts +43 -0
  137. package/src/sdk/market/oracle/createPriceOracle.ts +22 -0
  138. package/src/sdk/market/oracle/index.ts +4 -0
  139. package/src/sdk/market/oracle/types.ts +48 -0
  140. package/src/sdk/market/pool/GaugeContract.ts +120 -0
  141. package/src/sdk/market/pool/LinearInterestRateModelContract.ts +75 -0
  142. package/src/sdk/market/pool/PoolQuotaKeeperV300Contract.ts +89 -0
  143. package/src/sdk/market/pool/PoolQuotaKeeperV310Contract.ts +89 -0
  144. package/src/sdk/market/pool/PoolSuite.ts +76 -0
  145. package/src/sdk/market/pool/PoolV300Contract.ts +144 -0
  146. package/src/sdk/market/pool/PoolV310Contract.ts +144 -0
  147. package/src/sdk/market/pool/TumblerContract.ts +73 -0
  148. package/src/sdk/market/pool/createInterestRateModel.ts +33 -0
  149. package/src/sdk/market/pool/createPool.ts +19 -0
  150. package/src/sdk/market/pool/createPoolQuotaKeeper.ts +20 -0
  151. package/src/sdk/market/pool/createRateKeeper.ts +25 -0
  152. package/src/sdk/market/pool/index.ts +6 -0
  153. package/src/sdk/market/pool/types.ts +28 -0
  154. package/src/sdk/market/pricefeeds/AbstractLPPriceFeed.ts +71 -0
  155. package/src/sdk/market/pricefeeds/AbstractPriceFeed.ts +137 -0
  156. package/src/sdk/market/pricefeeds/BalancerStablePriceFeed.ts +24 -0
  157. package/src/sdk/market/pricefeeds/BalancerWeightedPriceFeed.ts +70 -0
  158. package/src/sdk/market/pricefeeds/BoundedPriceFeed.ts +34 -0
  159. package/src/sdk/market/pricefeeds/ChainlinkPriceFeed.ts +16 -0
  160. package/src/sdk/market/pricefeeds/CompositePriceFeed.ts +25 -0
  161. package/src/sdk/market/pricefeeds/CurveCryptoPriceFeed.ts +24 -0
  162. package/src/sdk/market/pricefeeds/CurveStablePriceFeed.ts +24 -0
  163. package/src/sdk/market/pricefeeds/CurveUSDPriceFeed.ts +24 -0
  164. package/src/sdk/market/pricefeeds/Erc4626PriceFeed.ts +36 -0
  165. package/src/sdk/market/pricefeeds/MellowLRTPriceFeed.ts +26 -0
  166. package/src/sdk/market/pricefeeds/PendleTWAPPTPriceFeed.ts +44 -0
  167. package/src/sdk/market/pricefeeds/PriceFeedRef.ts +34 -0
  168. package/src/sdk/market/pricefeeds/PriceFeedsRegister.ts +293 -0
  169. package/src/sdk/market/pricefeeds/RedstonePriceFeed.ts +65 -0
  170. package/src/sdk/market/pricefeeds/RedstoneUpdater.ts +304 -0
  171. package/src/sdk/market/pricefeeds/WstETHPriceFeed.ts +24 -0
  172. package/src/sdk/market/pricefeeds/YearnPriceFeed.ts +24 -0
  173. package/src/sdk/market/pricefeeds/ZeroPriceFeed.ts +17 -0
  174. package/src/sdk/market/pricefeeds/index.ts +20 -0
  175. package/src/sdk/market/pricefeeds/types.ts +82 -0
  176. package/src/sdk/market/pricefeeds/utils.ts +24 -0
  177. package/src/sdk/plugins/index.ts +10 -0
  178. package/src/sdk/router/PathOptionFactory.ts +147 -0
  179. package/src/sdk/router/RouterV3Contract.ts +558 -0
  180. package/src/sdk/router/index.ts +2 -0
  181. package/src/sdk/router/types.ts +48 -0
  182. package/src/sdk/sdk-gov-legacy/contracts/adapters.ts +39 -0
  183. package/src/sdk/sdk-gov-legacy/contracts/contracts.ts +647 -0
  184. package/src/sdk/sdk-gov-legacy/contracts/index.ts +2 -0
  185. package/src/sdk/sdk-gov-legacy/core/constants.ts +11 -0
  186. package/src/sdk/sdk-gov-legacy/index.ts +4 -0
  187. package/src/sdk/sdk-gov-legacy/oracles/PriceFeedType.ts +24 -0
  188. package/src/sdk/sdk-gov-legacy/oracles/index.ts +1 -0
  189. package/src/sdk/sdk-gov-legacy/tokens/aave.ts +101 -0
  190. package/src/sdk/sdk-gov-legacy/tokens/aura.ts +284 -0
  191. package/src/sdk/sdk-gov-legacy/tokens/balancer.ts +421 -0
  192. package/src/sdk/sdk-gov-legacy/tokens/compound.ts +79 -0
  193. package/src/sdk/sdk-gov-legacy/tokens/convex.ts +561 -0
  194. package/src/sdk/sdk-gov-legacy/tokens/curveLP.ts +480 -0
  195. package/src/sdk/sdk-gov-legacy/tokens/decimals.ts +301 -0
  196. package/src/sdk/sdk-gov-legacy/tokens/erc4626.ts +75 -0
  197. package/src/sdk/sdk-gov-legacy/tokens/gear.ts +273 -0
  198. package/src/sdk/sdk-gov-legacy/tokens/index.ts +18 -0
  199. package/src/sdk/sdk-gov-legacy/tokens/normal.ts +613 -0
  200. package/src/sdk/sdk-gov-legacy/tokens/quoted.ts +10 -0
  201. package/src/sdk/sdk-gov-legacy/tokens/stakingRewards.ts +41 -0
  202. package/src/sdk/sdk-gov-legacy/tokens/token.ts +2354 -0
  203. package/src/sdk/sdk-gov-legacy/tokens/tokenData.ts +44 -0
  204. package/src/sdk/sdk-gov-legacy/tokens/tokenType.ts +33 -0
  205. package/src/sdk/sdk-gov-legacy/tokens/wrapped.ts +28 -0
  206. package/src/sdk/sdk-gov-legacy/tokens/yearn.ts +122 -0
  207. package/src/sdk/sdk-gov-legacy/tokens/zircuit.ts +44 -0
  208. package/src/sdk/sdk-legacy/apy/index.ts +149 -0
  209. package/src/sdk/sdk-legacy/core/assets.ts +200 -0
  210. package/src/sdk/sdk-legacy/core/creditAccount.spec.ts +1966 -0
  211. package/src/sdk/sdk-legacy/core/creditAccount.ts +816 -0
  212. package/src/sdk/sdk-legacy/core/creditManager.ts +316 -0
  213. package/src/sdk/sdk-legacy/core/creditSession.ts +352 -0
  214. package/src/sdk/sdk-legacy/core/endpoint.ts +131 -0
  215. package/src/sdk/sdk-legacy/core/eventOrTx.ts +101 -0
  216. package/src/sdk/sdk-legacy/core/pool.ts +439 -0
  217. package/src/sdk/sdk-legacy/core/strategy.ts +82 -0
  218. package/src/sdk/sdk-legacy/core/transactions.ts +865 -0
  219. package/src/sdk/sdk-legacy/gearboxRewards/api.ts +704 -0
  220. package/src/sdk/sdk-legacy/gearboxRewards/apy.ts +267 -0
  221. package/src/sdk/sdk-legacy/gearboxRewards/extraAPY.ts +165 -0
  222. package/src/sdk/sdk-legacy/gearboxRewards/index.ts +3 -0
  223. package/src/sdk/sdk-legacy/gearboxRewards/merklAPI.ts +151 -0
  224. package/src/sdk/sdk-legacy/index.ts +21 -0
  225. package/src/sdk/sdk-legacy/pathfinder/core.ts +18 -0
  226. package/src/sdk/sdk-legacy/pathfinder/index.ts +1 -0
  227. package/src/sdk/sdk-legacy/payload/creditAccount.ts +48 -0
  228. package/src/sdk/sdk-legacy/payload/creditManager.ts +133 -0
  229. package/src/sdk/sdk-legacy/payload/creditSession.ts +143 -0
  230. package/src/sdk/sdk-legacy/payload/graphPayload.ts +10 -0
  231. package/src/sdk/sdk-legacy/payload/pool.ts +198 -0
  232. package/src/sdk/sdk-legacy/payload/token.ts +13 -0
  233. package/src/sdk/sdk-legacy/tokens/tokenData.ts +57 -0
  234. package/src/sdk/sdk-legacy/types/IAirdropDistributor.ts +103 -0
  235. package/src/sdk/sdk-legacy/types/IBaseRewardPool.ts +249 -0
  236. package/src/sdk/sdk-legacy/types/IDataCompressorV3.ts +957 -0
  237. package/src/sdk/sdk-legacy/types/IDegenDistributor.ts +70 -0
  238. package/src/sdk/sdk-legacy/types/IERC20Permit.ts +35 -0
  239. package/src/sdk/sdk-legacy/types/IERC20ZapperDeposits.ts +99 -0
  240. package/src/sdk/sdk-legacy/types/IETHZapperDeposits.ts +27 -0
  241. package/src/sdk/sdk-legacy/types/IFarmingPool.ts +229 -0
  242. package/src/sdk/sdk-legacy/types/IInterestRateModel.ts +23 -0
  243. package/src/sdk/sdk-legacy/types/IMulticall3.ts +242 -0
  244. package/src/sdk/sdk-legacy/types/IOffchainOracle.ts +31 -0
  245. package/src/sdk/sdk-legacy/types/index.ts +11 -0
  246. package/src/sdk/sdk-legacy/utils/formatter.ts +46 -0
  247. package/src/sdk/sdk-legacy/utils/index.ts +3 -0
  248. package/src/sdk/sdk-legacy/utils/math.ts +6 -0
  249. package/src/sdk/sdk-legacy/utils/price.ts +27 -0
  250. package/src/sdk/types/index.ts +5 -0
  251. package/src/sdk/types/logger.ts +13 -0
  252. package/src/sdk/types/state-human.ts +224 -0
  253. package/src/sdk/types/state.ts +8 -0
  254. package/src/sdk/types/transactions.ts +22 -0
  255. package/src/sdk/types/tvl.ts +4 -0
  256. package/src/sdk/utils/AddressMap.ts +111 -0
  257. package/src/sdk/utils/bytes32ToString.ts +6 -0
  258. package/src/sdk/utils/childLogger.ts +8 -0
  259. package/src/sdk/utils/createRawTx.ts +86 -0
  260. package/src/sdk/utils/etherscan.ts +42 -0
  261. package/src/sdk/utils/filterDust.ts +20 -0
  262. package/src/sdk/utils/formatter.ts +124 -0
  263. package/src/sdk/utils/index.ts +10 -0
  264. package/src/sdk/utils/internal/Hooks.ts +51 -0
  265. package/src/sdk/utils/internal/index.ts +1 -0
  266. package/src/sdk/utils/json.ts +27 -0
  267. package/src/sdk/utils/mappers.ts +38 -0
  268. package/src/sdk/utils/retry.ts +24 -0
  269. package/src/sdk/utils/viem/detectNetwork.ts +27 -0
  270. package/src/sdk/utils/viem/index.ts +3 -0
  271. package/src/sdk/utils/viem/sendRawTx.ts +36 -0
  272. package/src/sdk/utils/viem/simulateMulticall.ts +262 -0
  273. package/tsconfig.build.json +4 -0
  274. package/tsconfig.json +16 -0
  275. package/tsup.config.ts +77 -0
@@ -0,0 +1,1966 @@
1
+ import { describe, expect, it } from "@jest/globals";
2
+ import type { Address } from "viem";
3
+
4
+ import {
5
+ MIN_INT96,
6
+ PERCENTAGE_FACTOR,
7
+ PRICE_DECIMALS_POW,
8
+ } from "../../constants";
9
+ import type { Asset } from "../../router";
10
+ import type { TokensAPYList } from "../apy";
11
+ import { TokenData } from "../tokens/tokenData";
12
+ import { toBN } from "../utils/formatter";
13
+ import { PriceUtils } from "../utils/price";
14
+ import { AssetUtils } from "./assets";
15
+ import type {
16
+ CalcHealthFactorProps,
17
+ CalcOverallAPYProps,
18
+ CalcQuotaUpdateProps,
19
+ } from "./creditAccount";
20
+ import { CreditAccountData_Legacy } from "./creditAccount";
21
+
22
+ const WETH =
23
+ "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2".toLowerCase() as Address;
24
+ const DAI =
25
+ "0x6B175474E89094C44Da98b954EedeAC495271d0F".toLowerCase() as Address;
26
+ const USDC =
27
+ "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48".toLowerCase() as Address;
28
+ const STETH =
29
+ "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84".toLowerCase() as Address;
30
+
31
+ const tokenDataList: Record<Address, TokenData> = {
32
+ [WETH]: new TokenData({
33
+ addr: WETH,
34
+ decimals: 18,
35
+ symbol: "WETH",
36
+ name: "",
37
+ }),
38
+ [DAI]: new TokenData({
39
+ addr: DAI,
40
+ decimals: 18,
41
+ symbol: "DAI",
42
+ name: "",
43
+ }),
44
+ [USDC]: new TokenData({
45
+ addr: USDC,
46
+ decimals: 6,
47
+ symbol: "USDC",
48
+ name: "",
49
+ }),
50
+ [STETH]: new TokenData({
51
+ addr: STETH,
52
+ decimals: 18,
53
+ symbol: "STETH",
54
+ name: "",
55
+ }),
56
+ };
57
+
58
+ interface CATestInfo {
59
+ assets: Array<Asset>;
60
+ totalValue: bigint;
61
+ debt: bigint;
62
+ borrowRate: number;
63
+ underlyingToken: Address;
64
+ quotas: Record<Address, Asset>;
65
+ rates: CalcOverallAPYProps["quotaRates"];
66
+ }
67
+
68
+ const prices = {
69
+ [WETH]: toBN("1738.11830000", PRICE_DECIMALS_POW),
70
+ [DAI]: toBN("0.99941103", PRICE_DECIMALS_POW),
71
+ [USDC]: toBN("0.999", PRICE_DECIMALS_POW),
72
+ [STETH]: toBN("1703.87588096", PRICE_DECIMALS_POW),
73
+ };
74
+
75
+ const lpAPY: TokensAPYList = {
76
+ [STETH]: 38434,
77
+ };
78
+
79
+ const DAI_DECIMALS = 18;
80
+ const WETH_DECIMALS = 18;
81
+ const STETH_DECIMALS = 18;
82
+ const USDC_DECIMALS = 6;
83
+
84
+ const caWithoutLP: CATestInfo = {
85
+ assets: [
86
+ {
87
+ balance: toBN("54780", DAI_DECIMALS),
88
+ token: DAI,
89
+ },
90
+ {
91
+ balance: toBN("3.5", WETH_DECIMALS),
92
+ token: WETH,
93
+ },
94
+ ],
95
+ totalValue: toBN("60860", DAI_DECIMALS),
96
+ debt: toBN("54780", DAI_DECIMALS),
97
+ borrowRate: 7712,
98
+ underlyingToken: DAI,
99
+ quotas: {
100
+ [WETH]: {
101
+ balance: toBN("173811.830000", WETH_DECIMALS),
102
+ token: DAI,
103
+ },
104
+ },
105
+ rates: {
106
+ [WETH]: {
107
+ rate: 38434n,
108
+ isActive: true,
109
+ },
110
+ },
111
+ };
112
+
113
+ const caWithLP: CATestInfo = {
114
+ assets: [
115
+ {
116
+ balance: toBN("119.999999999999999997", STETH_DECIMALS),
117
+ token: STETH,
118
+ },
119
+ {
120
+ balance: toBN("3.5", WETH_DECIMALS),
121
+ token: WETH,
122
+ },
123
+ ],
124
+ totalValue: toBN("117.635897231615362429", WETH_DECIMALS),
125
+ debt: toBN("90.000000000000000000", WETH_DECIMALS),
126
+ borrowRate: 5736,
127
+ underlyingToken: WETH,
128
+ quotas: {
129
+ [STETH]: {
130
+ balance: toBN(
131
+ String((1703.87588096 * 119.9999999999999) / 1738.1183),
132
+ WETH_DECIMALS,
133
+ ),
134
+ token: STETH,
135
+ },
136
+ },
137
+ rates: {
138
+ [STETH]: {
139
+ rate: 38434n,
140
+ isActive: true,
141
+ },
142
+ },
143
+ };
144
+
145
+ describe("CreditAccount CreditAccountData_Legacy.calcOverallAPY test", () => {
146
+ it("overall APY calculation for caWithoutLP is correct", () => {
147
+ const result = CreditAccountData_Legacy.calcOverallAPY({
148
+ caAssets: caWithoutLP.assets,
149
+ totalValue: caWithoutLP.totalValue,
150
+ debt: caWithoutLP.debt,
151
+ baseRateWithFee: caWithoutLP.borrowRate,
152
+ underlyingToken: caWithoutLP.underlyingToken,
153
+
154
+ quotaRates: {},
155
+ quotas: {},
156
+ feeInterest: 0,
157
+
158
+ lpAPY,
159
+ prices,
160
+ tokensList: tokenDataList,
161
+ });
162
+
163
+ expect(result).toEqual(-69484n);
164
+ });
165
+ it("overall APY calculation for caWithLP is correct", () => {
166
+ const result = CreditAccountData_Legacy.calcOverallAPY({
167
+ caAssets: caWithLP.assets,
168
+ totalValue: caWithLP.totalValue,
169
+ debt: caWithLP.debt,
170
+ baseRateWithFee: caWithLP.borrowRate,
171
+ underlyingToken: caWithLP.underlyingToken,
172
+
173
+ quotaRates: {},
174
+ quotas: {},
175
+ feeInterest: 0,
176
+
177
+ lpAPY,
178
+ prices,
179
+ tokensList: tokenDataList,
180
+ });
181
+
182
+ expect(result).toEqual(144919n);
183
+ });
184
+ it("overall APY is undefined when !lpAPY", () => {
185
+ const result = CreditAccountData_Legacy.calcOverallAPY({
186
+ caAssets: caWithLP.assets,
187
+ totalValue: caWithLP.totalValue,
188
+ debt: caWithLP.debt,
189
+ baseRateWithFee: caWithLP.borrowRate,
190
+ underlyingToken: caWithLP.underlyingToken,
191
+
192
+ quotaRates: {},
193
+ quotas: {},
194
+ feeInterest: 0,
195
+
196
+ lpAPY: undefined,
197
+ prices,
198
+ tokensList: tokenDataList,
199
+ });
200
+
201
+ expect(result).toEqual(undefined);
202
+ });
203
+ it("overall APY is undefined when !totalValue", () => {
204
+ const result = CreditAccountData_Legacy.calcOverallAPY({
205
+ caAssets: caWithLP.assets,
206
+ totalValue: undefined,
207
+ debt: caWithLP.debt,
208
+ baseRateWithFee: caWithLP.borrowRate,
209
+ underlyingToken: caWithLP.underlyingToken,
210
+
211
+ quotaRates: {},
212
+ quotas: {},
213
+ feeInterest: 0,
214
+
215
+ lpAPY,
216
+ prices,
217
+ tokensList: tokenDataList,
218
+ });
219
+
220
+ expect(result).toEqual(undefined);
221
+ });
222
+ it("overall APY is undefined when !debt", () => {
223
+ const result = CreditAccountData_Legacy.calcOverallAPY({
224
+ caAssets: caWithLP.assets,
225
+ totalValue: caWithLP.totalValue,
226
+ debt: undefined,
227
+ baseRateWithFee: caWithLP.borrowRate,
228
+ underlyingToken: caWithLP.underlyingToken,
229
+
230
+ quotaRates: {},
231
+ quotas: {},
232
+ feeInterest: 0,
233
+
234
+ lpAPY,
235
+ prices,
236
+ tokensList: tokenDataList,
237
+ });
238
+
239
+ expect(result).toEqual(undefined);
240
+ });
241
+ it("overall APY is undefined when totalValue lte 0", () => {
242
+ const result = CreditAccountData_Legacy.calcOverallAPY({
243
+ caAssets: caWithLP.assets,
244
+ totalValue: 0n,
245
+ debt: undefined,
246
+ baseRateWithFee: caWithLP.borrowRate,
247
+ underlyingToken: caWithLP.underlyingToken,
248
+
249
+ quotaRates: {},
250
+ quotas: {},
251
+ feeInterest: 0,
252
+
253
+ lpAPY,
254
+ prices,
255
+ tokensList: tokenDataList,
256
+ });
257
+
258
+ expect(result).toEqual(undefined);
259
+ });
260
+ it("overall APY calculation for caWithLP with sufficient quota is correct", () => {
261
+ const result = CreditAccountData_Legacy.calcOverallAPY({
262
+ caAssets: caWithLP.assets,
263
+ totalValue: caWithLP.totalValue,
264
+ debt: caWithLP.debt,
265
+ baseRateWithFee: caWithLP.borrowRate,
266
+ underlyingToken: caWithLP.underlyingToken,
267
+
268
+ quotaRates: caWithLP.rates,
269
+ quotas: caWithLP.quotas,
270
+ feeInterest: 0,
271
+
272
+ lpAPY,
273
+ prices,
274
+ tokensList: tokenDataList,
275
+ });
276
+
277
+ expect(result).toEqual(-18680n);
278
+ });
279
+ it("overall APY calculation for caWithLP with insufficient quota is correct", () => {
280
+ const result = CreditAccountData_Legacy.calcOverallAPY({
281
+ caAssets: caWithLP.assets,
282
+ totalValue: caWithLP.totalValue,
283
+ debt: caWithLP.debt,
284
+ baseRateWithFee: caWithLP.borrowRate,
285
+ underlyingToken: caWithLP.underlyingToken,
286
+
287
+ quotaRates: caWithLP.rates,
288
+ quotas: {
289
+ [STETH]: {
290
+ balance: 0n,
291
+ token: STETH,
292
+ },
293
+ },
294
+ feeInterest: 0,
295
+
296
+ lpAPY,
297
+ prices,
298
+ tokensList: tokenDataList,
299
+ });
300
+
301
+ expect(result).toEqual(144919n);
302
+ });
303
+ });
304
+
305
+ describe("CreditAccount calcMaxDebtIncrease test", () => {
306
+ it("health max increase borrow is zero if hf < 1", () => {
307
+ const result = CreditAccountData_Legacy.calcMaxDebtIncrease(
308
+ 9999,
309
+ BigInt("156522834253690396032546"),
310
+ 9300,
311
+ );
312
+ expect(result.toString()).toEqual("0");
313
+ });
314
+ it("health max increase borrow is calculated correctly", () => {
315
+ const result = CreditAccountData_Legacy.calcMaxDebtIncrease(
316
+ 10244,
317
+ BigInt("156522834253690396032546"),
318
+ 9300,
319
+ );
320
+
321
+ expect(result.toString()).toEqual("54559387939857795188487");
322
+ });
323
+ it("health max increase borrow is calculated correctly (low hf, high debt)", () => {
324
+ const loweHf = 10244;
325
+
326
+ const result = CreditAccountData_Legacy.calcMaxDebtIncrease(
327
+ loweHf,
328
+ BigInt("54782991988791638611392"),
329
+ 9300,
330
+ );
331
+
332
+ expect(result.toString()).toEqual("19095785778950228315970");
333
+ });
334
+ });
335
+
336
+ const liquidationThresholds = {
337
+ [USDC]: 9800n,
338
+ [DAI]: 9300n,
339
+ [WETH]: 8500n,
340
+ [STETH]: 8000n,
341
+ };
342
+
343
+ describe("CreditAccount calcMaxLendingDebt test", () => {
344
+ it("calcMaxLendingDebt for several collaterals with zero lt", () => {
345
+ const result = CreditAccountData_Legacy.calcMaxLendingDebt({
346
+ assets: [
347
+ {
348
+ token: DAI,
349
+ balance: toBN("1000", DAI_DECIMALS),
350
+ },
351
+ {
352
+ token: WETH,
353
+ balance: toBN("1", WETH_DECIMALS),
354
+ },
355
+ ],
356
+ liquidationThresholds: {
357
+ ...liquidationThresholds,
358
+ [DAI]: 0n,
359
+ },
360
+ underlyingToken: USDC,
361
+ prices: {
362
+ [DAI]: 1n,
363
+ [USDC]: 1n,
364
+ [WETH]: 1000n,
365
+ },
366
+ tokensList: tokenDataList,
367
+ });
368
+ expect(result).toEqual(toBN("850", USDC_DECIMALS));
369
+ });
370
+ it("calcMaxLendingDebt for several collaterals with zero underlying price", () => {
371
+ const result = CreditAccountData_Legacy.calcMaxLendingDebt({
372
+ assets: [
373
+ {
374
+ token: DAI,
375
+ balance: toBN("1000", DAI_DECIMALS),
376
+ },
377
+ {
378
+ token: WETH,
379
+ balance: toBN("1", WETH_DECIMALS),
380
+ },
381
+ ],
382
+ liquidationThresholds: liquidationThresholds,
383
+ underlyingToken: USDC,
384
+ prices: {
385
+ [DAI]: 1n,
386
+ [WETH]: 1000n,
387
+ },
388
+ tokensList: tokenDataList,
389
+ });
390
+ expect(result).toEqual(0n);
391
+ });
392
+ it("calcMaxLendingDebt for simplest case", () => {
393
+ const result = CreditAccountData_Legacy.calcMaxLendingDebt({
394
+ assets: [
395
+ {
396
+ token: DAI,
397
+ balance: toBN("1000", DAI_DECIMALS),
398
+ },
399
+ ],
400
+ liquidationThresholds,
401
+ underlyingToken: USDC,
402
+ prices: {
403
+ [DAI]: 1n,
404
+ [USDC]: 1n,
405
+ },
406
+ tokensList: tokenDataList,
407
+ });
408
+ expect(result).toEqual(toBN("930", USDC_DECIMALS));
409
+ });
410
+ it("calcMaxLendingDebt for several collaterals", () => {
411
+ const result = CreditAccountData_Legacy.calcMaxLendingDebt({
412
+ assets: [
413
+ {
414
+ token: DAI,
415
+ balance: toBN("1000", DAI_DECIMALS),
416
+ },
417
+ {
418
+ token: WETH,
419
+ balance: toBN("1", WETH_DECIMALS),
420
+ },
421
+ ],
422
+ liquidationThresholds,
423
+ underlyingToken: USDC,
424
+ prices: {
425
+ [DAI]: 1n,
426
+ [USDC]: 1n,
427
+ [WETH]: 1000n,
428
+ },
429
+ tokensList: tokenDataList,
430
+ });
431
+ expect(result).toEqual(toBN("1780", USDC_DECIMALS));
432
+ });
433
+ it("calcMaxLendingDebt for several collaterals with target HF", () => {
434
+ const result = CreditAccountData_Legacy.calcMaxLendingDebt({
435
+ assets: [
436
+ {
437
+ token: DAI,
438
+ balance: toBN("1000", DAI_DECIMALS),
439
+ },
440
+ {
441
+ token: WETH,
442
+ balance: toBN("1", WETH_DECIMALS),
443
+ },
444
+ ],
445
+ liquidationThresholds,
446
+ underlyingToken: USDC,
447
+ prices: {
448
+ [DAI]: 1n,
449
+ [USDC]: 1n,
450
+ [WETH]: 1000n,
451
+ },
452
+ targetHF: 12500n,
453
+ tokensList: tokenDataList,
454
+ });
455
+ expect(result).toEqual(toBN("1424", USDC_DECIMALS));
456
+ });
457
+ });
458
+
459
+ interface CAHfTestInfo {
460
+ assets: Array<Asset>;
461
+ debt: bigint;
462
+ underlyingToken: Address;
463
+ healthFactor: number;
464
+ underlyingDecimals: number;
465
+ quotas: Record<Address, Asset>;
466
+ quotasInfo: CalcHealthFactorProps["quotasInfo"];
467
+ }
468
+
469
+ const defaultCA: CAHfTestInfo = {
470
+ assets: [
471
+ {
472
+ balance: toBN("156552", DAI_DECIMALS),
473
+ token: DAI,
474
+ },
475
+ {
476
+ balance: toBN("10", WETH_DECIMALS),
477
+ token: WETH,
478
+ },
479
+ ],
480
+ debt: toBN("156552", DAI_DECIMALS),
481
+ healthFactor: 10244,
482
+ underlyingToken: DAI,
483
+ underlyingDecimals: DAI_DECIMALS,
484
+ quotas: {
485
+ [WETH]: {
486
+ balance: toBN(String(1750 * 10), DAI_DECIMALS),
487
+ token: WETH,
488
+ },
489
+ },
490
+ quotasInfo: {
491
+ [WETH]: {
492
+ isActive: true,
493
+ },
494
+ },
495
+ };
496
+
497
+ describe("CreditAccount calcHealthFactor test", () => {
498
+ it("health factor is calculated correctly", () => {
499
+ const result = CreditAccountData_Legacy.calcHealthFactor({
500
+ quotas: {},
501
+ quotasInfo: {},
502
+ assets: defaultCA.assets,
503
+ prices,
504
+ liquidationThresholds,
505
+ underlyingToken: defaultCA.underlyingToken,
506
+ debt: defaultCA.debt,
507
+ tokensList: tokenDataList,
508
+ });
509
+
510
+ expect(result).toEqual(defaultCA.healthFactor);
511
+ });
512
+ it("health factor calculation has no division by zero error", () => {
513
+ const result = CreditAccountData_Legacy.calcHealthFactor({
514
+ quotas: {},
515
+ quotasInfo: {},
516
+ assets: [],
517
+ prices: {},
518
+ liquidationThresholds: {},
519
+ underlyingToken: "" as Address,
520
+ debt: 0n,
521
+ tokensList: tokenDataList,
522
+ });
523
+
524
+ expect(result).toEqual(65535);
525
+ });
526
+ it("health factor after add collateral is calculated correctly", () => {
527
+ const collateral: Asset = {
528
+ balance: toBN("10", WETH_DECIMALS),
529
+ token: WETH,
530
+ };
531
+
532
+ const afterAdd = AssetUtils.sumAssets(defaultCA.assets, [collateral]);
533
+ const result = CreditAccountData_Legacy.calcHealthFactor({
534
+ quotas: {},
535
+ quotasInfo: {},
536
+ assets: afterAdd,
537
+ prices,
538
+ liquidationThresholds,
539
+ underlyingToken: defaultCA.underlyingToken,
540
+ debt: defaultCA.debt,
541
+ tokensList: tokenDataList,
542
+ });
543
+
544
+ expect(result).toEqual(11188);
545
+ });
546
+ it("health factor after decrease debt is calculated correctly", () => {
547
+ const amountDecrease = toBN("10000", defaultCA.underlyingDecimals);
548
+ const debtDecrease: Asset = {
549
+ balance: amountDecrease,
550
+ token: defaultCA.underlyingToken as Address,
551
+ };
552
+
553
+ const afterDecrease = AssetUtils.subAssets(defaultCA.assets, [
554
+ debtDecrease,
555
+ ]);
556
+ const result = CreditAccountData_Legacy.calcHealthFactor({
557
+ quotas: {},
558
+ quotasInfo: {},
559
+ assets: afterDecrease,
560
+ prices,
561
+ liquidationThresholds,
562
+ underlyingToken: defaultCA.underlyingToken,
563
+ debt: defaultCA.debt - amountDecrease,
564
+ tokensList: tokenDataList,
565
+ });
566
+
567
+ expect(result).toEqual(10308);
568
+ });
569
+ it("health factor after increase debt is calculated correctly", () => {
570
+ const amountIncrease = toBN("20000", defaultCA.underlyingDecimals);
571
+ const debtIncrease: Asset = {
572
+ balance: amountIncrease,
573
+ token: defaultCA.underlyingToken as Address,
574
+ };
575
+
576
+ const afterIncrease = AssetUtils.sumAssets(defaultCA.assets, [
577
+ debtIncrease,
578
+ ]);
579
+ const result = CreditAccountData_Legacy.calcHealthFactor({
580
+ quotas: {},
581
+ quotasInfo: {},
582
+ assets: afterIncrease,
583
+ prices,
584
+ liquidationThresholds,
585
+ underlyingToken: defaultCA.underlyingToken,
586
+ debt: defaultCA.debt + amountIncrease,
587
+ tokensList: tokenDataList,
588
+ });
589
+
590
+ expect(result).toEqual(10137);
591
+ });
592
+ it("health factor after swap is calculated correctly", () => {
593
+ const swapAsset: Asset = {
594
+ balance: defaultCA.debt,
595
+ token: defaultCA.underlyingToken as Address,
596
+ };
597
+ const underlyingPrice = prices[defaultCA.underlyingToken];
598
+ const wethPrice = prices[WETH];
599
+
600
+ const getAmount = PriceUtils.convertByPrice(
601
+ PriceUtils.calcTotalPrice(
602
+ underlyingPrice,
603
+ defaultCA.debt,
604
+ defaultCA.underlyingDecimals,
605
+ ),
606
+ { price: wethPrice, decimals: WETH_DECIMALS },
607
+ );
608
+
609
+ const getAsset: Asset = {
610
+ balance: getAmount,
611
+ token: WETH,
612
+ };
613
+
614
+ const afterSub = AssetUtils.subAssets(defaultCA.assets, [swapAsset]);
615
+ const afterSwap = AssetUtils.sumAssets(afterSub, [getAsset]);
616
+
617
+ const result = CreditAccountData_Legacy.calcHealthFactor({
618
+ quotas: {},
619
+ quotasInfo: {},
620
+ assets: afterSwap,
621
+ prices,
622
+ liquidationThresholds,
623
+ underlyingToken: defaultCA.underlyingToken,
624
+ debt: defaultCA.debt,
625
+ tokensList: tokenDataList,
626
+ });
627
+
628
+ expect(result).toEqual(9444);
629
+ });
630
+ it("health factor with sufficient quotas is calculated correctly", () => {
631
+ const result = CreditAccountData_Legacy.calcHealthFactor({
632
+ quotas: defaultCA.quotas,
633
+ quotasInfo: defaultCA.quotasInfo,
634
+ assets: defaultCA.assets,
635
+ prices,
636
+ liquidationThresholds,
637
+ underlyingToken: defaultCA.underlyingToken,
638
+ debt: defaultCA.debt,
639
+ tokensList: tokenDataList,
640
+ });
641
+
642
+ expect(result).toEqual(defaultCA.healthFactor);
643
+ });
644
+ it("health factor with insufficient quotas is calculated correctly", () => {
645
+ const result = CreditAccountData_Legacy.calcHealthFactor({
646
+ quotas: {
647
+ [WETH]: {
648
+ balance: 0n,
649
+ token: WETH,
650
+ },
651
+ },
652
+ quotasInfo: defaultCA.quotasInfo,
653
+ assets: defaultCA.assets,
654
+ prices,
655
+ liquidationThresholds,
656
+ underlyingToken: defaultCA.underlyingToken,
657
+ debt: defaultCA.debt,
658
+ tokensList: tokenDataList,
659
+ });
660
+
661
+ expect(result).toEqual(9300);
662
+ });
663
+ it("health factor with disabled quota is calculated correctly", () => {
664
+ const result = CreditAccountData_Legacy.calcHealthFactor({
665
+ quotas: defaultCA.quotas,
666
+ quotasInfo: {
667
+ [WETH]: {
668
+ isActive: false,
669
+ },
670
+ },
671
+ assets: defaultCA.assets,
672
+ prices,
673
+ liquidationThresholds,
674
+ underlyingToken: defaultCA.underlyingToken,
675
+ debt: defaultCA.debt,
676
+ tokensList: tokenDataList,
677
+ });
678
+
679
+ expect(result).toEqual(9300);
680
+ });
681
+ });
682
+
683
+ const cmQuotas: CalcQuotaUpdateProps["quotas"] = {
684
+ [DAI]: {
685
+ token: DAI,
686
+ isActive: true,
687
+ },
688
+ [WETH]: {
689
+ token: WETH,
690
+ isActive: true,
691
+ },
692
+ [STETH]: {
693
+ token: STETH,
694
+ isActive: true,
695
+ },
696
+ };
697
+
698
+ const caQuota: CalcQuotaUpdateProps["initialQuotas"] = {
699
+ [DAI]: {
700
+ quota: 5n * PERCENTAGE_FACTOR,
701
+ },
702
+ [WETH]: {
703
+ quota: 10n * PERCENTAGE_FACTOR,
704
+ },
705
+ };
706
+
707
+ const QUOTA_RESERVE = 100n;
708
+ const DEFAULT_LT = {
709
+ [DAI]: PERCENTAGE_FACTOR,
710
+ [WETH]: PERCENTAGE_FACTOR,
711
+ [STETH]: PERCENTAGE_FACTOR,
712
+ };
713
+ const HUGE_MAX_DEBT = 20n * PERCENTAGE_FACTOR;
714
+
715
+ describe("CreditAccount calcQuotaUpdate test", () => {
716
+ it("open account should buy quota", () => {
717
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
718
+ maxDebt: HUGE_MAX_DEBT,
719
+ quotaReserve: QUOTA_RESERVE,
720
+ quotas: cmQuotas,
721
+ initialQuotas: {},
722
+ assetsAfterUpdate: {
723
+ [DAI]: {
724
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
725
+ balance: 0n,
726
+ token: DAI,
727
+ },
728
+ [WETH]: {
729
+ amountInTarget: 20n * PERCENTAGE_FACTOR,
730
+ balance: 0n,
731
+ token: WETH,
732
+ },
733
+ },
734
+
735
+ allowedToObtain: {
736
+ [DAI]: {},
737
+ [WETH]: {},
738
+ },
739
+ allowedToSpend: {},
740
+
741
+ liquidationThresholds: DEFAULT_LT,
742
+ });
743
+
744
+ expect(result.quotaIncrease).toEqual([
745
+ {
746
+ balance: 10n * PERCENTAGE_FACTOR,
747
+ token: DAI,
748
+ },
749
+ {
750
+ balance: 20n * PERCENTAGE_FACTOR,
751
+ token: WETH,
752
+ },
753
+ ]);
754
+ expect(result.quotaDecrease).toEqual([]);
755
+ expect(result.desiredQuota).toEqual({
756
+ [DAI]: {
757
+ balance: 10n * PERCENTAGE_FACTOR,
758
+ token: DAI,
759
+ },
760
+ [WETH]: {
761
+ balance: 20n * PERCENTAGE_FACTOR,
762
+ token: WETH,
763
+ },
764
+ [STETH]: {
765
+ balance: 0n * PERCENTAGE_FACTOR,
766
+ token: STETH,
767
+ },
768
+ });
769
+ });
770
+ it("add collateral should buy quota", () => {
771
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
772
+ maxDebt: HUGE_MAX_DEBT,
773
+ quotaReserve: QUOTA_RESERVE,
774
+ quotas: cmQuotas,
775
+ initialQuotas: caQuota,
776
+ assetsAfterUpdate: {
777
+ [STETH]: {
778
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
779
+ balance: 0n,
780
+ token: DAI,
781
+ },
782
+ },
783
+
784
+ allowedToObtain: {
785
+ [STETH]: {},
786
+ },
787
+ allowedToSpend: {},
788
+
789
+ liquidationThresholds: DEFAULT_LT,
790
+ });
791
+
792
+ expect(result.quotaIncrease).toEqual([
793
+ {
794
+ balance: 10n * PERCENTAGE_FACTOR,
795
+ token: STETH,
796
+ },
797
+ ]);
798
+ expect(result.quotaDecrease).toEqual([]);
799
+ expect(result.desiredQuota).toEqual({
800
+ [DAI]: {
801
+ balance: 5n * PERCENTAGE_FACTOR,
802
+ token: DAI,
803
+ },
804
+ [WETH]: {
805
+ balance: 10n * PERCENTAGE_FACTOR,
806
+ token: WETH,
807
+ },
808
+ [STETH]: {
809
+ balance: 10n * PERCENTAGE_FACTOR,
810
+ token: STETH,
811
+ },
812
+ });
813
+ });
814
+ it("add collateral should add additional quota", () => {
815
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
816
+ maxDebt: HUGE_MAX_DEBT,
817
+ quotaReserve: QUOTA_RESERVE,
818
+ quotas: cmQuotas,
819
+ initialQuotas: caQuota,
820
+ assetsAfterUpdate: {
821
+ [DAI]: {
822
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
823
+ balance: 0n,
824
+ token: DAI,
825
+ },
826
+ },
827
+
828
+ allowedToObtain: {
829
+ [DAI]: {},
830
+ },
831
+ allowedToSpend: {},
832
+
833
+ liquidationThresholds: DEFAULT_LT,
834
+ });
835
+
836
+ expect(result.quotaIncrease).toEqual([
837
+ {
838
+ balance: 5n * PERCENTAGE_FACTOR,
839
+ token: DAI,
840
+ },
841
+ ]);
842
+ expect(result.quotaDecrease).toEqual([]);
843
+ expect(result.desiredQuota).toEqual({
844
+ [DAI]: {
845
+ balance: 10n * PERCENTAGE_FACTOR,
846
+ token: DAI,
847
+ },
848
+ [WETH]: {
849
+ balance: 10n * PERCENTAGE_FACTOR,
850
+ token: WETH,
851
+ },
852
+ [STETH]: {
853
+ balance: 0n * PERCENTAGE_FACTOR,
854
+ token: STETH,
855
+ },
856
+ });
857
+ });
858
+ it("add collateral shouldn't add additional quota", () => {
859
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
860
+ maxDebt: HUGE_MAX_DEBT,
861
+ quotaReserve: QUOTA_RESERVE,
862
+ quotas: cmQuotas,
863
+ initialQuotas: caQuota,
864
+ assetsAfterUpdate: {
865
+ [WETH]: {
866
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
867
+ balance: 0n,
868
+ token: WETH,
869
+ },
870
+ },
871
+
872
+ allowedToObtain: {
873
+ [WETH]: {},
874
+ },
875
+ allowedToSpend: {},
876
+
877
+ liquidationThresholds: DEFAULT_LT,
878
+ });
879
+
880
+ expect(result.quotaIncrease).toEqual([]);
881
+ expect(result.quotaDecrease).toEqual([]);
882
+ expect(result.desiredQuota).toEqual({
883
+ [DAI]: {
884
+ balance: 5n * PERCENTAGE_FACTOR,
885
+ token: DAI,
886
+ },
887
+ [WETH]: {
888
+ balance: 10n * PERCENTAGE_FACTOR,
889
+ token: WETH,
890
+ },
891
+ [STETH]: {
892
+ balance: 0n * PERCENTAGE_FACTOR,
893
+ token: STETH,
894
+ },
895
+ });
896
+ });
897
+ it("swap should buy quota", () => {
898
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
899
+ maxDebt: HUGE_MAX_DEBT,
900
+ quotaReserve: QUOTA_RESERVE,
901
+ quotas: cmQuotas,
902
+ initialQuotas: caQuota,
903
+ assetsAfterUpdate: {
904
+ [STETH]: {
905
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
906
+ balance: 0n,
907
+ token: DAI,
908
+ },
909
+ [WETH]: {
910
+ amountInTarget: 0n * PERCENTAGE_FACTOR,
911
+ balance: 0n,
912
+ token: DAI,
913
+ },
914
+ },
915
+
916
+ allowedToObtain: {
917
+ [STETH]: {},
918
+ },
919
+ allowedToSpend: { [WETH]: {} },
920
+
921
+ liquidationThresholds: DEFAULT_LT,
922
+ });
923
+
924
+ expect(result.quotaIncrease).toEqual([
925
+ {
926
+ balance: 10n * PERCENTAGE_FACTOR,
927
+ token: STETH,
928
+ },
929
+ ]);
930
+ expect(result.quotaDecrease).toEqual([
931
+ {
932
+ balance: MIN_INT96,
933
+ token: WETH,
934
+ },
935
+ ]);
936
+ expect(result.desiredQuota).toEqual({
937
+ [DAI]: {
938
+ balance: 5n * PERCENTAGE_FACTOR,
939
+ token: DAI,
940
+ },
941
+ [WETH]: {
942
+ balance: 0n * PERCENTAGE_FACTOR,
943
+ token: WETH,
944
+ },
945
+ [STETH]: {
946
+ balance: 10n * PERCENTAGE_FACTOR,
947
+ token: STETH,
948
+ },
949
+ });
950
+ });
951
+ it("swap should buy additional quota", () => {
952
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
953
+ maxDebt: HUGE_MAX_DEBT,
954
+ quotaReserve: QUOTA_RESERVE,
955
+ quotas: cmQuotas,
956
+ initialQuotas: caQuota,
957
+ assetsAfterUpdate: {
958
+ [DAI]: {
959
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
960
+ balance: 0n,
961
+ token: DAI,
962
+ },
963
+ [WETH]: {
964
+ amountInTarget: 0n * PERCENTAGE_FACTOR,
965
+ balance: 0n,
966
+ token: WETH,
967
+ },
968
+ },
969
+
970
+ allowedToObtain: {
971
+ [DAI]: {},
972
+ },
973
+ allowedToSpend: {
974
+ [WETH]: {},
975
+ },
976
+
977
+ liquidationThresholds: DEFAULT_LT,
978
+ });
979
+
980
+ expect(result.quotaIncrease).toEqual([
981
+ {
982
+ balance: 5n * PERCENTAGE_FACTOR,
983
+ token: DAI,
984
+ },
985
+ ]);
986
+ expect(result.quotaDecrease).toEqual([
987
+ {
988
+ balance: MIN_INT96,
989
+ token: WETH,
990
+ },
991
+ ]);
992
+ expect(result.desiredQuota).toEqual({
993
+ [DAI]: {
994
+ balance: 10n * PERCENTAGE_FACTOR,
995
+ token: DAI,
996
+ },
997
+ [WETH]: {
998
+ balance: 0n * PERCENTAGE_FACTOR,
999
+ token: WETH,
1000
+ },
1001
+ [STETH]: {
1002
+ balance: 0n * PERCENTAGE_FACTOR,
1003
+ token: STETH,
1004
+ },
1005
+ });
1006
+ });
1007
+ it("swap should buy additional quota with respect to debt", () => {
1008
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1009
+ maxDebt: HUGE_MAX_DEBT,
1010
+ quotaReserve: QUOTA_RESERVE,
1011
+ quotas: cmQuotas,
1012
+ initialQuotas: caQuota,
1013
+ calcModification: {
1014
+ debt: 7n * PERCENTAGE_FACTOR,
1015
+ type: "recommendedQuota",
1016
+ },
1017
+ assetsAfterUpdate: {
1018
+ [DAI]: {
1019
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
1020
+ balance: 0n,
1021
+ token: DAI,
1022
+ },
1023
+ [WETH]: {
1024
+ amountInTarget: 0n * PERCENTAGE_FACTOR,
1025
+ balance: 0n,
1026
+ token: WETH,
1027
+ },
1028
+ },
1029
+
1030
+ allowedToObtain: {
1031
+ [DAI]: {},
1032
+ },
1033
+ allowedToSpend: {
1034
+ [WETH]: {},
1035
+ },
1036
+
1037
+ liquidationThresholds: DEFAULT_LT,
1038
+ });
1039
+
1040
+ expect(result.quotaIncrease).toEqual([
1041
+ {
1042
+ balance: 2n * PERCENTAGE_FACTOR,
1043
+ token: DAI,
1044
+ },
1045
+ ]);
1046
+ expect(result.quotaDecrease).toEqual([
1047
+ {
1048
+ balance: MIN_INT96,
1049
+ token: WETH,
1050
+ },
1051
+ ]);
1052
+ expect(result.desiredQuota).toEqual({
1053
+ [DAI]: {
1054
+ balance: 7n * PERCENTAGE_FACTOR,
1055
+ token: DAI,
1056
+ },
1057
+ [WETH]: {
1058
+ balance: 0n * PERCENTAGE_FACTOR,
1059
+ token: WETH,
1060
+ },
1061
+ [STETH]: {
1062
+ balance: 0n * PERCENTAGE_FACTOR,
1063
+ token: STETH,
1064
+ },
1065
+ });
1066
+ });
1067
+ it("swap shouldn't buy additional quota", () => {
1068
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1069
+ maxDebt: HUGE_MAX_DEBT,
1070
+ quotaReserve: QUOTA_RESERVE,
1071
+ quotas: cmQuotas,
1072
+ initialQuotas: caQuota,
1073
+ assetsAfterUpdate: {
1074
+ [WETH]: {
1075
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
1076
+ balance: 0n,
1077
+ token: WETH,
1078
+ },
1079
+ [DAI]: {
1080
+ amountInTarget: 0n * PERCENTAGE_FACTOR,
1081
+ balance: 0n,
1082
+ token: DAI,
1083
+ },
1084
+ },
1085
+
1086
+ allowedToObtain: {
1087
+ [WETH]: {},
1088
+ },
1089
+ allowedToSpend: { [DAI]: {} },
1090
+
1091
+ liquidationThresholds: DEFAULT_LT,
1092
+ });
1093
+
1094
+ expect(result.quotaIncrease).toEqual([]);
1095
+ expect(result.quotaDecrease).toEqual([
1096
+ {
1097
+ balance: MIN_INT96,
1098
+ token: DAI,
1099
+ },
1100
+ ]);
1101
+ expect(result.desiredQuota).toEqual({
1102
+ [DAI]: {
1103
+ balance: 0n * PERCENTAGE_FACTOR,
1104
+ token: DAI,
1105
+ },
1106
+ [WETH]: {
1107
+ balance: 10n * PERCENTAGE_FACTOR,
1108
+ token: WETH,
1109
+ },
1110
+ [STETH]: {
1111
+ balance: 0n * PERCENTAGE_FACTOR,
1112
+ token: STETH,
1113
+ },
1114
+ });
1115
+ });
1116
+ it("shouldn't change quota if disallowed", () => {
1117
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1118
+ maxDebt: HUGE_MAX_DEBT,
1119
+ quotaReserve: QUOTA_RESERVE,
1120
+ quotas: cmQuotas,
1121
+ initialQuotas: caQuota,
1122
+ assetsAfterUpdate: {
1123
+ [DAI]: {
1124
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
1125
+ balance: 0n,
1126
+ token: DAI,
1127
+ },
1128
+ [WETH]: {
1129
+ amountInTarget: 0n * PERCENTAGE_FACTOR,
1130
+ balance: 0n,
1131
+ token: WETH,
1132
+ },
1133
+ },
1134
+
1135
+ allowedToObtain: {},
1136
+ allowedToSpend: {},
1137
+
1138
+ liquidationThresholds: DEFAULT_LT,
1139
+ });
1140
+
1141
+ expect(result.quotaIncrease).toEqual([]);
1142
+ expect(result.quotaDecrease).toEqual([]);
1143
+ expect(result.desiredQuota).toEqual({
1144
+ [DAI]: {
1145
+ balance: 5n * PERCENTAGE_FACTOR,
1146
+ token: DAI,
1147
+ },
1148
+ [WETH]: {
1149
+ balance: 10n * PERCENTAGE_FACTOR,
1150
+ token: WETH,
1151
+ },
1152
+ [STETH]: {
1153
+ balance: 0n * PERCENTAGE_FACTOR,
1154
+ token: STETH,
1155
+ },
1156
+ });
1157
+ });
1158
+ it("shouldn't change quota if it is disabled", () => {
1159
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1160
+ maxDebt: HUGE_MAX_DEBT,
1161
+ quotaReserve: QUOTA_RESERVE,
1162
+ quotas: {
1163
+ [DAI]: {
1164
+ token: DAI,
1165
+ isActive: false,
1166
+ },
1167
+ [WETH]: {
1168
+ token: WETH,
1169
+ isActive: false,
1170
+ },
1171
+ [STETH]: {
1172
+ token: STETH,
1173
+ isActive: false,
1174
+ },
1175
+ },
1176
+ initialQuotas: caQuota,
1177
+ assetsAfterUpdate: {
1178
+ [DAI]: {
1179
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
1180
+ balance: 0n,
1181
+ token: DAI,
1182
+ },
1183
+ [WETH]: {
1184
+ amountInTarget: 0n * PERCENTAGE_FACTOR,
1185
+ balance: 0n,
1186
+ token: WETH,
1187
+ },
1188
+ },
1189
+
1190
+ allowedToObtain: {
1191
+ [DAI]: {},
1192
+ },
1193
+ allowedToSpend: {
1194
+ [WETH]: {},
1195
+ },
1196
+
1197
+ liquidationThresholds: DEFAULT_LT,
1198
+ });
1199
+
1200
+ expect(result.quotaIncrease).toEqual([]);
1201
+ expect(result.quotaDecrease).toEqual([]);
1202
+ expect(result.desiredQuota).toEqual({
1203
+ [DAI]: {
1204
+ balance: 5n * PERCENTAGE_FACTOR,
1205
+ token: DAI,
1206
+ },
1207
+ [WETH]: {
1208
+ balance: 10n * PERCENTAGE_FACTOR,
1209
+ token: WETH,
1210
+ },
1211
+ [STETH]: {
1212
+ balance: 0n * PERCENTAGE_FACTOR,
1213
+ token: STETH,
1214
+ },
1215
+ });
1216
+ });
1217
+ it("swap shouldn't buy quota if no lt", () => {
1218
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1219
+ maxDebt: HUGE_MAX_DEBT,
1220
+ quotaReserve: QUOTA_RESERVE,
1221
+ quotas: cmQuotas,
1222
+ initialQuotas: {
1223
+ ...caQuota,
1224
+ [STETH]: {
1225
+ quota: 5n * PERCENTAGE_FACTOR,
1226
+ },
1227
+ },
1228
+ assetsAfterUpdate: {
1229
+ [STETH]: {
1230
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
1231
+ balance: 0n,
1232
+ token: DAI,
1233
+ },
1234
+ [WETH]: {
1235
+ amountInTarget: 5n * PERCENTAGE_FACTOR,
1236
+ balance: 0n,
1237
+ token: DAI,
1238
+ },
1239
+ },
1240
+
1241
+ allowedToObtain: {
1242
+ [STETH]: {},
1243
+ },
1244
+ allowedToSpend: { [WETH]: {} },
1245
+
1246
+ liquidationThresholds: {},
1247
+ });
1248
+
1249
+ expect(result.quotaIncrease).toEqual([]);
1250
+ expect(result.quotaDecrease).toEqual([
1251
+ {
1252
+ balance: MIN_INT96,
1253
+ token: WETH,
1254
+ },
1255
+ ]);
1256
+ expect(result.desiredQuota).toEqual({
1257
+ [DAI]: {
1258
+ balance: 5n * PERCENTAGE_FACTOR,
1259
+ token: DAI,
1260
+ },
1261
+ [WETH]: {
1262
+ balance: 0n * PERCENTAGE_FACTOR,
1263
+ token: WETH,
1264
+ },
1265
+ [STETH]: {
1266
+ balance: 5n * PERCENTAGE_FACTOR,
1267
+ token: STETH,
1268
+ },
1269
+ });
1270
+ });
1271
+ it("swap should buy quota with respect to lt", () => {
1272
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1273
+ maxDebt: HUGE_MAX_DEBT,
1274
+ quotaReserve: QUOTA_RESERVE,
1275
+ quotas: cmQuotas,
1276
+ initialQuotas: {
1277
+ ...caQuota,
1278
+ [STETH]: { quota: 5n * PERCENTAGE_FACTOR },
1279
+ },
1280
+ assetsAfterUpdate: {
1281
+ [STETH]: {
1282
+ amountInTarget: 20n * PERCENTAGE_FACTOR,
1283
+ balance: 0n,
1284
+ token: DAI,
1285
+ },
1286
+ [WETH]: {
1287
+ amountInTarget: 5n * PERCENTAGE_FACTOR,
1288
+ balance: 0n,
1289
+ token: DAI,
1290
+ },
1291
+ },
1292
+
1293
+ allowedToObtain: {
1294
+ [STETH]: {},
1295
+ },
1296
+ allowedToSpend: { [WETH]: {} },
1297
+
1298
+ liquidationThresholds: {
1299
+ ...DEFAULT_LT,
1300
+ [STETH]: 5000n,
1301
+ },
1302
+ });
1303
+
1304
+ expect(result.quotaIncrease).toEqual([
1305
+ {
1306
+ balance: 5n * PERCENTAGE_FACTOR,
1307
+ token: STETH,
1308
+ },
1309
+ ]);
1310
+ expect(result.quotaDecrease).toEqual([
1311
+ {
1312
+ balance: -5n * PERCENTAGE_FACTOR,
1313
+ token: WETH,
1314
+ },
1315
+ ]);
1316
+ expect(result.desiredQuota).toEqual({
1317
+ [DAI]: {
1318
+ balance: 5n * PERCENTAGE_FACTOR,
1319
+ token: DAI,
1320
+ },
1321
+ [WETH]: {
1322
+ balance: 5n * PERCENTAGE_FACTOR,
1323
+ token: WETH,
1324
+ },
1325
+ [STETH]: {
1326
+ balance: 10n * PERCENTAGE_FACTOR,
1327
+ token: STETH,
1328
+ },
1329
+ });
1330
+ });
1331
+ it("swap shouldn't buy quota with respect to lt", () => {
1332
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1333
+ maxDebt: HUGE_MAX_DEBT,
1334
+ quotaReserve: QUOTA_RESERVE,
1335
+ quotas: cmQuotas,
1336
+ initialQuotas: {
1337
+ ...caQuota,
1338
+ [STETH]: { quota: 5n * PERCENTAGE_FACTOR },
1339
+ },
1340
+ assetsAfterUpdate: {
1341
+ [STETH]: {
1342
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
1343
+ balance: 0n,
1344
+ token: DAI,
1345
+ },
1346
+ [WETH]: {
1347
+ amountInTarget: 5n * PERCENTAGE_FACTOR,
1348
+ balance: 0n,
1349
+ token: DAI,
1350
+ },
1351
+ },
1352
+
1353
+ allowedToObtain: {
1354
+ [STETH]: {},
1355
+ },
1356
+ allowedToSpend: { [WETH]: {} },
1357
+
1358
+ liquidationThresholds: {
1359
+ ...DEFAULT_LT,
1360
+ [STETH]: 5000n,
1361
+ },
1362
+ });
1363
+
1364
+ expect(result.quotaIncrease).toEqual([]);
1365
+ expect(result.quotaDecrease).toEqual([
1366
+ {
1367
+ balance: -5n * PERCENTAGE_FACTOR,
1368
+ token: WETH,
1369
+ },
1370
+ ]);
1371
+ expect(result.desiredQuota).toEqual({
1372
+ [DAI]: {
1373
+ balance: 5n * PERCENTAGE_FACTOR,
1374
+ token: DAI,
1375
+ },
1376
+ [WETH]: {
1377
+ balance: 5n * PERCENTAGE_FACTOR,
1378
+ token: WETH,
1379
+ },
1380
+ [STETH]: {
1381
+ balance: 5n * PERCENTAGE_FACTOR,
1382
+ token: STETH,
1383
+ },
1384
+ });
1385
+ });
1386
+ it("swap should buy additional quota after limit was increased", () => {
1387
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1388
+ maxDebt: 10n * PERCENTAGE_FACTOR,
1389
+ quotaReserve: QUOTA_RESERVE,
1390
+ quotas: cmQuotas,
1391
+ initialQuotas: {
1392
+ ...caQuota,
1393
+ [DAI]: {
1394
+ quota: 10n * PERCENTAGE_FACTOR,
1395
+ },
1396
+ },
1397
+ assetsAfterUpdate: {
1398
+ [DAI]: {
1399
+ amountInTarget: 20n * PERCENTAGE_FACTOR,
1400
+ balance: 0n,
1401
+ token: DAI,
1402
+ },
1403
+ [WETH]: {
1404
+ amountInTarget: 0n * PERCENTAGE_FACTOR,
1405
+ balance: 0n,
1406
+ token: WETH,
1407
+ },
1408
+ },
1409
+
1410
+ allowedToObtain: {
1411
+ [DAI]: {},
1412
+ },
1413
+ allowedToSpend: {
1414
+ [WETH]: {},
1415
+ },
1416
+
1417
+ liquidationThresholds: DEFAULT_LT,
1418
+ });
1419
+
1420
+ expect(result.quotaIncrease).toEqual([
1421
+ {
1422
+ balance: 10n * PERCENTAGE_FACTOR,
1423
+ token: DAI,
1424
+ },
1425
+ ]);
1426
+ expect(result.quotaDecrease).toEqual([
1427
+ {
1428
+ balance: MIN_INT96,
1429
+ token: WETH,
1430
+ },
1431
+ ]);
1432
+ expect(result.desiredQuota).toEqual({
1433
+ [DAI]: {
1434
+ balance: 20n * PERCENTAGE_FACTOR,
1435
+ token: DAI,
1436
+ },
1437
+ [WETH]: {
1438
+ balance: 0n * PERCENTAGE_FACTOR,
1439
+ token: WETH,
1440
+ },
1441
+ [STETH]: {
1442
+ balance: 0n * PERCENTAGE_FACTOR,
1443
+ token: STETH,
1444
+ },
1445
+ });
1446
+ });
1447
+ it("swap should buy additional quota with respect to debt limit", () => {
1448
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1449
+ maxDebt: 9n * PERCENTAGE_FACTOR,
1450
+ quotaReserve: QUOTA_RESERVE,
1451
+ quotas: cmQuotas,
1452
+ initialQuotas: {
1453
+ ...caQuota,
1454
+ [DAI]: {
1455
+ quota: 10n * PERCENTAGE_FACTOR,
1456
+ },
1457
+ },
1458
+ assetsAfterUpdate: {
1459
+ [DAI]: {
1460
+ amountInTarget: 20n * PERCENTAGE_FACTOR,
1461
+ balance: 0n,
1462
+ token: DAI,
1463
+ },
1464
+ [WETH]: {
1465
+ amountInTarget: 0n * PERCENTAGE_FACTOR,
1466
+ balance: 0n,
1467
+ token: WETH,
1468
+ },
1469
+ },
1470
+
1471
+ allowedToObtain: {
1472
+ [DAI]: {},
1473
+ },
1474
+ allowedToSpend: {
1475
+ [WETH]: {},
1476
+ },
1477
+
1478
+ liquidationThresholds: DEFAULT_LT,
1479
+ });
1480
+
1481
+ expect(result.quotaIncrease).toEqual([
1482
+ {
1483
+ balance: 8n * PERCENTAGE_FACTOR,
1484
+ token: DAI,
1485
+ },
1486
+ ]);
1487
+ expect(result.quotaDecrease).toEqual([
1488
+ {
1489
+ balance: MIN_INT96,
1490
+ token: WETH,
1491
+ },
1492
+ ]);
1493
+ expect(result.desiredQuota).toEqual({
1494
+ [DAI]: {
1495
+ balance: 18n * PERCENTAGE_FACTOR,
1496
+ token: DAI,
1497
+ },
1498
+ [WETH]: {
1499
+ balance: 0n * PERCENTAGE_FACTOR,
1500
+ token: WETH,
1501
+ },
1502
+ [STETH]: {
1503
+ balance: 0n * PERCENTAGE_FACTOR,
1504
+ token: STETH,
1505
+ },
1506
+ });
1507
+ });
1508
+ it("swap shouldn't buy additional quota if debt limit more then current quota", () => {
1509
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1510
+ maxDebt: 5n * PERCENTAGE_FACTOR,
1511
+ quotaReserve: QUOTA_RESERVE,
1512
+ quotas: cmQuotas,
1513
+ initialQuotas: {
1514
+ ...caQuota,
1515
+ [DAI]: {
1516
+ quota: 10n * PERCENTAGE_FACTOR,
1517
+ },
1518
+ },
1519
+ assetsAfterUpdate: {
1520
+ [DAI]: {
1521
+ amountInTarget: 20n * PERCENTAGE_FACTOR,
1522
+ balance: 0n,
1523
+ token: DAI,
1524
+ },
1525
+ [WETH]: {
1526
+ amountInTarget: 0n * PERCENTAGE_FACTOR,
1527
+ balance: 0n,
1528
+ token: WETH,
1529
+ },
1530
+ },
1531
+
1532
+ allowedToObtain: {
1533
+ [DAI]: {},
1534
+ },
1535
+ allowedToSpend: {
1536
+ [WETH]: {},
1537
+ },
1538
+
1539
+ liquidationThresholds: DEFAULT_LT,
1540
+ });
1541
+
1542
+ expect(result.quotaIncrease).toEqual([]);
1543
+ expect(result.quotaDecrease).toEqual([
1544
+ {
1545
+ balance: MIN_INT96,
1546
+ token: WETH,
1547
+ },
1548
+ ]);
1549
+ expect(result.desiredQuota).toEqual({
1550
+ [DAI]: {
1551
+ balance: 10n * PERCENTAGE_FACTOR,
1552
+ token: DAI,
1553
+ },
1554
+ [WETH]: {
1555
+ balance: 0n * PERCENTAGE_FACTOR,
1556
+ token: WETH,
1557
+ },
1558
+ [STETH]: {
1559
+ balance: 0n * PERCENTAGE_FACTOR,
1560
+ token: STETH,
1561
+ },
1562
+ });
1563
+ });
1564
+ it("swap on old accounts should work correctly", () => {
1565
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1566
+ maxDebt: HUGE_MAX_DEBT,
1567
+ quotaReserve: QUOTA_RESERVE,
1568
+ quotas: cmQuotas,
1569
+ initialQuotas: {
1570
+ ...caQuota,
1571
+ [DAI]: {
1572
+ quota: 5_2345n,
1573
+ },
1574
+ [WETH]: {
1575
+ quota: 10_7458n * PERCENTAGE_FACTOR,
1576
+ },
1577
+ },
1578
+ assetsAfterUpdate: {
1579
+ [STETH]: {
1580
+ amountInTarget: 10_1345n,
1581
+ balance: 0n,
1582
+ token: STETH,
1583
+ },
1584
+ [WETH]: {
1585
+ amountInTarget: 0n * PERCENTAGE_FACTOR,
1586
+ balance: 0n,
1587
+ token: WETH,
1588
+ },
1589
+ },
1590
+
1591
+ allowedToObtain: {
1592
+ [STETH]: {},
1593
+ },
1594
+ allowedToSpend: { [WETH]: {} },
1595
+
1596
+ liquidationThresholds: DEFAULT_LT,
1597
+ });
1598
+
1599
+ expect(result.quotaIncrease).toEqual([
1600
+ {
1601
+ balance: 10n * PERCENTAGE_FACTOR,
1602
+ token: STETH,
1603
+ },
1604
+ ]);
1605
+ expect(result.quotaDecrease).toEqual([
1606
+ {
1607
+ balance: MIN_INT96,
1608
+ token: WETH,
1609
+ },
1610
+ ]);
1611
+ expect(result.desiredQuota).toEqual({
1612
+ [DAI]: {
1613
+ balance: 5_2345n,
1614
+ token: DAI,
1615
+ },
1616
+ [WETH]: {
1617
+ balance: 0n * PERCENTAGE_FACTOR,
1618
+ token: WETH,
1619
+ },
1620
+ [STETH]: {
1621
+ balance: 10n * PERCENTAGE_FACTOR,
1622
+ token: STETH,
1623
+ },
1624
+ });
1625
+ });
1626
+ it("swap on old accounts should work correctly and respect maxQuotaIncrease", () => {
1627
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1628
+ maxDebt: 6n * PERCENTAGE_FACTOR,
1629
+ quotaReserve: QUOTA_RESERVE,
1630
+ quotas: cmQuotas,
1631
+ initialQuotas: {
1632
+ ...caQuota,
1633
+ [DAI]: {
1634
+ quota: 5_2345n,
1635
+ },
1636
+ [WETH]: {
1637
+ quota: 10_7458n * PERCENTAGE_FACTOR,
1638
+ },
1639
+ },
1640
+ assetsAfterUpdate: {
1641
+ [STETH]: {
1642
+ amountInTarget: 10_1345n,
1643
+ balance: 0n,
1644
+ token: STETH,
1645
+ },
1646
+ [WETH]: {
1647
+ amountInTarget: 0n * PERCENTAGE_FACTOR,
1648
+ balance: 0n,
1649
+ token: WETH,
1650
+ },
1651
+ },
1652
+
1653
+ allowedToObtain: {
1654
+ [STETH]: {},
1655
+ },
1656
+ allowedToSpend: { [WETH]: {} },
1657
+
1658
+ liquidationThresholds: DEFAULT_LT,
1659
+ });
1660
+
1661
+ expect(result.quotaIncrease).toEqual([
1662
+ {
1663
+ balance: 7n * PERCENTAGE_FACTOR,
1664
+ token: STETH,
1665
+ },
1666
+ ]);
1667
+ expect(result.quotaDecrease).toEqual([
1668
+ {
1669
+ balance: MIN_INT96,
1670
+ token: WETH,
1671
+ },
1672
+ ]);
1673
+ expect(result.desiredQuota).toEqual({
1674
+ [DAI]: {
1675
+ balance: 5_2345n,
1676
+ token: DAI,
1677
+ },
1678
+ [WETH]: {
1679
+ balance: 0n * PERCENTAGE_FACTOR,
1680
+ token: WETH,
1681
+ },
1682
+ [STETH]: {
1683
+ balance: 7n * PERCENTAGE_FACTOR,
1684
+ token: STETH,
1685
+ },
1686
+ });
1687
+ });
1688
+ it("should buy quota correctly when all assets listed in both buy and spend", () => {
1689
+ const result = CreditAccountData_Legacy.calcQuotaUpdate({
1690
+ maxDebt: HUGE_MAX_DEBT,
1691
+ quotaReserve: QUOTA_RESERVE,
1692
+ quotas: cmQuotas,
1693
+ initialQuotas: {
1694
+ ...caQuota,
1695
+ [DAI]: {
1696
+ quota: 0n * PERCENTAGE_FACTOR,
1697
+ },
1698
+ [STETH]: {
1699
+ quota: 15n * PERCENTAGE_FACTOR,
1700
+ },
1701
+ },
1702
+ assetsAfterUpdate: {
1703
+ [STETH]: {
1704
+ amountInTarget: 0n * PERCENTAGE_FACTOR,
1705
+ balance: 0n,
1706
+ token: STETH,
1707
+ },
1708
+ [DAI]: {
1709
+ amountInTarget: 10n * PERCENTAGE_FACTOR,
1710
+ balance: 0n,
1711
+ token: DAI,
1712
+ },
1713
+ [WETH]: {
1714
+ amountInTarget: 5n * PERCENTAGE_FACTOR,
1715
+ balance: 0n,
1716
+ token: WETH,
1717
+ },
1718
+ },
1719
+
1720
+ allowedToObtain: {
1721
+ [STETH]: {},
1722
+ [DAI]: {},
1723
+ [WETH]: {},
1724
+ },
1725
+ allowedToSpend: {
1726
+ [STETH]: {},
1727
+ [DAI]: {},
1728
+ [WETH]: {},
1729
+ },
1730
+ liquidationThresholds: DEFAULT_LT,
1731
+ });
1732
+
1733
+ expect(result.quotaIncrease).toEqual([
1734
+ {
1735
+ balance: 10n * PERCENTAGE_FACTOR,
1736
+ token: DAI,
1737
+ },
1738
+ ]);
1739
+ expect(result.quotaDecrease).toEqual([
1740
+ {
1741
+ balance: MIN_INT96,
1742
+ token: STETH,
1743
+ },
1744
+ {
1745
+ balance: -5n * PERCENTAGE_FACTOR,
1746
+ token: WETH,
1747
+ },
1748
+ ]);
1749
+ expect(result.desiredQuota).toEqual({
1750
+ [DAI]: {
1751
+ balance: 10n * PERCENTAGE_FACTOR,
1752
+ token: DAI,
1753
+ },
1754
+ [WETH]: {
1755
+ balance: 5n * PERCENTAGE_FACTOR,
1756
+ token: WETH,
1757
+ },
1758
+ [STETH]: {
1759
+ balance: 0n * PERCENTAGE_FACTOR,
1760
+ token: STETH,
1761
+ },
1762
+ });
1763
+ });
1764
+ });
1765
+
1766
+ describe("CreditAccount calcAvgQuotaBorrowRate test", () => {
1767
+ it("should calculate quota rate (same amounts, different rates)", () => {
1768
+ const result = CreditAccountData_Legacy.calcQuotaBorrowRate({
1769
+ quotas: {
1770
+ [DAI]: {
1771
+ token: DAI,
1772
+ balance: 10n,
1773
+ },
1774
+ [WETH]: {
1775
+ token: WETH,
1776
+ balance: 10n,
1777
+ },
1778
+ [STETH]: {
1779
+ token: STETH,
1780
+ balance: 10n,
1781
+ },
1782
+ },
1783
+ quotaRates: {
1784
+ [DAI]: {
1785
+ rate: 5n,
1786
+ isActive: true,
1787
+ },
1788
+ [WETH]: {
1789
+ rate: 10n,
1790
+ isActive: true,
1791
+ },
1792
+ [STETH]: {
1793
+ rate: 15n,
1794
+ isActive: true,
1795
+ },
1796
+ },
1797
+ });
1798
+
1799
+ expect(result).toEqual(300n);
1800
+ });
1801
+ it("should calculate quota rate (same rates, different amounts)", () => {
1802
+ const result = CreditAccountData_Legacy.calcQuotaBorrowRate({
1803
+ quotas: {
1804
+ [DAI]: {
1805
+ token: DAI,
1806
+ balance: 5n,
1807
+ },
1808
+ [WETH]: {
1809
+ token: WETH,
1810
+ balance: 10n,
1811
+ },
1812
+ [STETH]: {
1813
+ token: STETH,
1814
+ balance: 15n,
1815
+ },
1816
+ },
1817
+ quotaRates: {
1818
+ [DAI]: {
1819
+ rate: 10n,
1820
+ isActive: true,
1821
+ },
1822
+ [WETH]: {
1823
+ rate: 10n,
1824
+ isActive: true,
1825
+ },
1826
+ [STETH]: {
1827
+ rate: 10n,
1828
+ isActive: true,
1829
+ },
1830
+ },
1831
+ });
1832
+
1833
+ expect(result).toEqual(300n);
1834
+ });
1835
+ it("should calculate quota rate (disabled quota)", () => {
1836
+ const result = CreditAccountData_Legacy.calcQuotaBorrowRate({
1837
+ quotas: {
1838
+ [DAI]: {
1839
+ token: DAI,
1840
+ balance: 5n,
1841
+ },
1842
+ [WETH]: {
1843
+ token: WETH,
1844
+ balance: 10n,
1845
+ },
1846
+ [STETH]: {
1847
+ token: STETH,
1848
+ balance: 15n,
1849
+ },
1850
+ },
1851
+ quotaRates: {
1852
+ [DAI]: {
1853
+ rate: 10n,
1854
+ isActive: true,
1855
+ },
1856
+ [WETH]: {
1857
+ rate: 10n,
1858
+ isActive: false,
1859
+ },
1860
+ [STETH]: {
1861
+ rate: 10n,
1862
+ isActive: true,
1863
+ },
1864
+ },
1865
+ });
1866
+
1867
+ expect(result).toEqual(200n);
1868
+ });
1869
+ });
1870
+
1871
+ describe("CreditAccount calcQuotaBorrowRate test", () => {
1872
+ it("should calculate quota borrow rate", () => {
1873
+ const result = CreditAccountData_Legacy.calcQuotaBorrowRate({
1874
+ quotas: {
1875
+ [DAI]: {
1876
+ token: DAI,
1877
+ balance: 10n,
1878
+ },
1879
+ },
1880
+ quotaRates: {
1881
+ [DAI]: {
1882
+ rate: 5n,
1883
+ isActive: true,
1884
+ },
1885
+ },
1886
+ });
1887
+
1888
+ expect(result).toEqual(50n);
1889
+ });
1890
+ it("should calculate quota borrow rate when no balance", () => {
1891
+ const result = CreditAccountData_Legacy.calcQuotaBorrowRate({
1892
+ quotas: {
1893
+ [DAI]: {
1894
+ token: DAI,
1895
+ balance: 1n,
1896
+ },
1897
+ },
1898
+ quotaRates: {
1899
+ [DAI]: {
1900
+ rate: 5n,
1901
+ isActive: true,
1902
+ },
1903
+ },
1904
+ });
1905
+
1906
+ expect(result).toEqual(5n);
1907
+ });
1908
+ });
1909
+
1910
+ describe("CreditAccount calcRelativeBaseBorrowRate test", () => {
1911
+ it("should calculate relative borrow rate", () => {
1912
+ const result = CreditAccountData_Legacy.calcRelativeBaseBorrowRate({
1913
+ debt: 200n,
1914
+ baseRateWithFee: 250,
1915
+ assetAmountInUnderlying: 200n,
1916
+ });
1917
+
1918
+ expect(result).toEqual(10000000n);
1919
+ });
1920
+ it("should calculate relative borrow rate if position asset === 0", () => {
1921
+ const result = CreditAccountData_Legacy.calcRelativeBaseBorrowRate({
1922
+ debt: 200n,
1923
+ baseRateWithFee: 250,
1924
+ assetAmountInUnderlying: 1n,
1925
+ });
1926
+
1927
+ expect(result).toEqual(50000n);
1928
+ });
1929
+ it("should calculate relative borrow rate if position === 0", () => {
1930
+ const result = CreditAccountData_Legacy.calcRelativeBaseBorrowRate({
1931
+ debt: 1n,
1932
+ baseRateWithFee: 250,
1933
+ assetAmountInUnderlying: 1n,
1934
+ });
1935
+
1936
+ expect(result).toEqual(250n);
1937
+ });
1938
+ });
1939
+
1940
+ describe("CreditAccount getTimeToLiquidation test", () => {
1941
+ it("should return 0 when HF < 1", () => {
1942
+ const result = CreditAccountData_Legacy.getTimeToLiquidation({
1943
+ healthFactor: 9000,
1944
+ totalBorrowRate_debt: 250n,
1945
+ });
1946
+
1947
+ expect(result).toEqual(null);
1948
+ });
1949
+ it("should return 0 when br_debt === 0", () => {
1950
+ const result = CreditAccountData_Legacy.getTimeToLiquidation({
1951
+ healthFactor: 9000,
1952
+ totalBorrowRate_debt: 0n,
1953
+ });
1954
+
1955
+ expect(result).toEqual(null);
1956
+ });
1957
+ it("should calculate time to liquidation correctly", () => {
1958
+ const result = CreditAccountData_Legacy.getTimeToLiquidation({
1959
+ healthFactor: 13750,
1960
+ totalBorrowRate_debt: 20n * 10000n,
1961
+ });
1962
+
1963
+ // 59_130_000
1964
+ expect(result).toEqual(59130000n * 1000n);
1965
+ });
1966
+ });