@paraswap/dex-lib 3.7.2 → 3.7.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 (236) hide show
  1. package/build/abi/Redstone.json +739 -0
  2. package/build/abi/aavev3stata/Factory.json +128 -0
  3. package/build/abi/aavev3stata/Token.json +735 -0
  4. package/build/abi/angle-transmuter/MorphoOracle.json +193 -0
  5. package/build/abi/angle-transmuter/MorphoVault.json +2506 -0
  6. package/build/abi/angle-transmuter/Pyth.json +1534 -0
  7. package/build/abi/angle-transmuter/Transmuter.json +1711 -0
  8. package/build/abi/angle-transmuter/TransmuterSidechain.json +1798 -0
  9. package/build/abi/idle-dao/idle-cdo-factory.json +38 -0
  10. package/build/abi/idle-dao/idle-cdo.json +1245 -0
  11. package/build/dex/aave-v1/aave-v1.d.ts +2 -1
  12. package/build/dex/aave-v1/aave-v1.js +22 -0
  13. package/build/dex/aave-v1/aave-v1.js.map +1 -1
  14. package/build/dex/aave-v3-stata/aave-v3-stata.d.ts +50 -0
  15. package/build/dex/aave-v3-stata/aave-v3-stata.js +365 -0
  16. package/build/dex/aave-v3-stata/aave-v3-stata.js.map +1 -0
  17. package/build/dex/aave-v3-stata/config.d.ts +4 -0
  18. package/build/dex/aave-v3-stata/config.js +50 -0
  19. package/build/dex/aave-v3-stata/config.js.map +1 -0
  20. package/build/dex/aave-v3-stata/tokens.d.ts +9 -0
  21. package/build/dex/aave-v3-stata/tokens.js +37 -0
  22. package/build/dex/aave-v3-stata/tokens.js.map +1 -0
  23. package/build/dex/aave-v3-stata/types.d.ts +46 -0
  24. package/build/dex/aave-v3-stata/types.js +23 -0
  25. package/build/dex/aave-v3-stata/types.js.map +1 -0
  26. package/build/dex/aave-v3-stata/utils.d.ts +4 -0
  27. package/build/dex/aave-v3-stata/utils.js +67 -0
  28. package/build/dex/aave-v3-stata/utils.js.map +1 -0
  29. package/build/dex/angle-transmuter/angle-transmuter-pool.d.ts +67 -0
  30. package/build/dex/angle-transmuter/angle-transmuter-pool.js +448 -0
  31. package/build/dex/angle-transmuter/angle-transmuter-pool.js.map +1 -0
  32. package/build/dex/angle-transmuter/angle-transmuter.d.ts +59 -0
  33. package/build/dex/angle-transmuter/angle-transmuter.js +264 -0
  34. package/build/dex/angle-transmuter/angle-transmuter.js.map +1 -0
  35. package/build/dex/angle-transmuter/backedOracle.d.ts +19 -0
  36. package/build/dex/angle-transmuter/backedOracle.js +56 -0
  37. package/build/dex/angle-transmuter/backedOracle.js.map +1 -0
  38. package/build/dex/angle-transmuter/config.d.ts +4 -0
  39. package/build/dex/angle-transmuter/config.js +50 -0
  40. package/build/dex/angle-transmuter/config.js.map +1 -0
  41. package/build/dex/angle-transmuter/constants.d.ts +9 -0
  42. package/build/dex/angle-transmuter/constants.js +136 -0
  43. package/build/dex/angle-transmuter/constants.js.map +1 -0
  44. package/build/dex/angle-transmuter/morphoOracle.d.ts +42 -0
  45. package/build/dex/angle-transmuter/morphoOracle.js +179 -0
  46. package/build/dex/angle-transmuter/morphoOracle.js.map +1 -0
  47. package/build/dex/angle-transmuter/morphoVault.d.ts +20 -0
  48. package/build/dex/angle-transmuter/morphoVault.js +84 -0
  49. package/build/dex/angle-transmuter/morphoVault.js.map +1 -0
  50. package/build/dex/angle-transmuter/pyth.d.ts +19 -0
  51. package/build/dex/angle-transmuter/pyth.js +70 -0
  52. package/build/dex/angle-transmuter/pyth.js.map +1 -0
  53. package/build/dex/angle-transmuter/redstone.d.ts +20 -0
  54. package/build/dex/angle-transmuter/redstone.js +64 -0
  55. package/build/dex/angle-transmuter/redstone.js.map +1 -0
  56. package/build/dex/angle-transmuter/transmuter.d.ts +55 -0
  57. package/build/dex/angle-transmuter/transmuter.js +398 -0
  58. package/build/dex/angle-transmuter/transmuter.js.map +1 -0
  59. package/build/dex/angle-transmuter/types.d.ts +189 -0
  60. package/build/dex/angle-transmuter/types.js +33 -0
  61. package/build/dex/angle-transmuter/types.js.map +1 -0
  62. package/build/dex/angle-transmuter/utils.d.ts +9 -0
  63. package/build/dex/angle-transmuter/utils.js +257 -0
  64. package/build/dex/angle-transmuter/utils.js.map +1 -0
  65. package/build/dex/idle-dao/config.d.ts +6 -0
  66. package/build/dex/idle-dao/config.js +307 -0
  67. package/build/dex/idle-dao/config.js.map +1 -0
  68. package/build/dex/idle-dao/idle-dao-pool.d.ts +56 -0
  69. package/build/dex/idle-dao/idle-dao-pool.js +176 -0
  70. package/build/dex/idle-dao/idle-dao-pool.js.map +1 -0
  71. package/build/dex/idle-dao/idle-dao-pooling-pool.d.ts +16 -0
  72. package/build/dex/idle-dao/idle-dao-pooling-pool.js +57 -0
  73. package/build/dex/idle-dao/idle-dao-pooling-pool.js.map +1 -0
  74. package/build/dex/idle-dao/idle-dao.d.ts +41 -0
  75. package/build/dex/idle-dao/idle-dao.js +238 -0
  76. package/build/dex/idle-dao/idle-dao.js.map +1 -0
  77. package/build/dex/idle-dao/scripts.d.ts +9 -0
  78. package/build/dex/idle-dao/scripts.js +552 -0
  79. package/build/dex/idle-dao/scripts.js.map +1 -0
  80. package/build/dex/idle-dao/tokens.d.ts +9 -0
  81. package/build/dex/idle-dao/tokens.js +65 -0
  82. package/build/dex/idle-dao/tokens.js.map +1 -0
  83. package/build/dex/idle-dao/types.d.ts +35 -0
  84. package/build/dex/idle-dao/types.js +11 -0
  85. package/build/dex/idle-dao/types.js.map +1 -0
  86. package/build/dex/idle-dao/utils.d.ts +8 -0
  87. package/build/dex/idle-dao/utils.js +148 -0
  88. package/build/dex/idle-dao/utils.js.map +1 -0
  89. package/build/dex/index.js +2 -0
  90. package/build/dex/index.js.map +1 -1
  91. package/build/dex/platypus/platypus.d.ts +2 -1
  92. package/build/dex/platypus/platypus.js +31 -0
  93. package/build/dex/platypus/platypus.js.map +1 -1
  94. package/build/dex/swaap-v1/swaap-v1.d.ts +2 -1
  95. package/build/dex/swaap-v1/swaap-v1.js +10 -0
  96. package/build/dex/swaap-v1/swaap-v1.js.map +1 -1
  97. package/build/lib/chainlink.d.ts +2 -0
  98. package/build/lib/chainlink.js +9 -0
  99. package/build/lib/chainlink.js.map +1 -1
  100. package/build/utils.d.ts +1 -1
  101. package/package.json +1 -1
  102. package/src/abi/Redstone.json +739 -0
  103. package/src/abi/angle-transmuter/ChainlinkAccessControlledOffchainAggregator.json +1246 -0
  104. package/src/abi/angle-transmuter/ChainlinkEACAggregatorProxy.json +509 -0
  105. package/src/abi/angle-transmuter/MorphoOracle.json +193 -0
  106. package/src/abi/angle-transmuter/MorphoVault.json +2506 -0
  107. package/src/abi/angle-transmuter/Pyth.json +1534 -0
  108. package/src/abi/angle-transmuter/Transmuter.json +1711 -0
  109. package/src/abi/angle-transmuter/TransmuterSidechain.json +1798 -0
  110. package/src/dex/angle-transmuter/angle-transmuter-e2e.test.ts +156 -0
  111. package/src/dex/angle-transmuter/angle-transmuter-events.test.ts +365 -0
  112. package/src/dex/angle-transmuter/angle-transmuter-integration.test.ts +390 -0
  113. package/src/dex/angle-transmuter/angle-transmuter-pool.ts +895 -0
  114. package/src/dex/angle-transmuter/angle-transmuter.ts +408 -0
  115. package/src/dex/angle-transmuter/backedOracle.ts +91 -0
  116. package/src/dex/angle-transmuter/config.ts +50 -0
  117. package/src/dex/angle-transmuter/constants.ts +138 -0
  118. package/src/dex/angle-transmuter/morphoOracle.ts +335 -0
  119. package/src/dex/angle-transmuter/morphoVault.ts +134 -0
  120. package/src/dex/angle-transmuter/pyth.ts +105 -0
  121. package/src/dex/angle-transmuter/redstone.ts +105 -0
  122. package/src/dex/angle-transmuter/transmuter.ts +619 -0
  123. package/src/dex/angle-transmuter/types.ts +203 -0
  124. package/src/dex/angle-transmuter/utils.ts +358 -0
  125. package/src/dex/index.ts +2 -0
  126. package/src/lib/chainlink.ts +15 -0
  127. package/tests/constants-e2e.ts +48 -3
  128. package/build/abi/Uncompressor.json +0 -250
  129. package/build/dex/bProtocol.d.ts +0 -18
  130. package/build/dex/bProtocol.js +0 -39
  131. package/build/dex/bProtocol.js.map +0 -1
  132. package/build/dex/bancor.d.ts +0 -26
  133. package/build/dex/bancor.js +0 -58
  134. package/build/dex/bancor.js.map +0 -1
  135. package/build/dex/compound.d.ts +0 -18
  136. package/build/dex/compound.js +0 -46
  137. package/build/dex/compound.js.map +0 -1
  138. package/build/dex/curve-v2.d.ts +0 -58
  139. package/build/dex/curve-v2.js +0 -180
  140. package/build/dex/curve-v2.js.map +0 -1
  141. package/build/dex/dodo-v1.d.ts +0 -33
  142. package/build/dex/dodo-v1.js +0 -63
  143. package/build/dex/dodo-v1.js.map +0 -1
  144. package/build/dex/etherfi/etherfi.d.ts +0 -26
  145. package/build/dex/etherfi/etherfi.js +0 -96
  146. package/build/dex/etherfi/etherfi.js.map +0 -1
  147. package/build/dex/lido.d.ts +0 -19
  148. package/build/dex/lido.js +0 -42
  149. package/build/dex/lido.js.map +0 -1
  150. package/build/dex/onebit.d.ts +0 -25
  151. package/build/dex/onebit.js +0 -42
  152. package/build/dex/onebit.js.map +0 -1
  153. package/build/dex/sdai/config.d.ts +0 -11
  154. package/build/dex/sdai/config.js +0 -21
  155. package/build/dex/sdai/config.js.map +0 -1
  156. package/build/dex/sdai/constants.d.ts +0 -2
  157. package/build/dex/sdai/constants.js +0 -6
  158. package/build/dex/sdai/constants.js.map +0 -1
  159. package/build/dex/sdai/scripts/validate-state.d.ts +0 -1
  160. package/build/dex/sdai/scripts/validate-state.js +0 -102
  161. package/build/dex/sdai/scripts/validate-state.js.map +0 -1
  162. package/build/dex/sdai/sdai-pool.d.ts +0 -16
  163. package/build/dex/sdai/sdai-pool.js +0 -85
  164. package/build/dex/sdai/sdai-pool.js.map +0 -1
  165. package/build/dex/sdai/sdai.d.ts +0 -51
  166. package/build/dex/sdai/sdai.js +0 -172
  167. package/build/dex/sdai/sdai.js.map +0 -1
  168. package/build/dex/sdai/types.d.ts +0 -21
  169. package/build/dex/sdai/types.js +0 -11
  170. package/build/dex/sdai/types.js.map +0 -1
  171. package/build/dex/sdai/utils.d.ts +0 -4
  172. package/build/dex/sdai/utils.js +0 -39
  173. package/build/dex/sdai/utils.js.map +0 -1
  174. package/build/dex/smoothy.d.ts +0 -26
  175. package/build/dex/smoothy.js +0 -48
  176. package/build/dex/smoothy.js.map +0 -1
  177. package/build/dex/stable-pool.d.ts +0 -28
  178. package/build/dex/stable-pool.js +0 -62
  179. package/build/dex/stable-pool.js.map +0 -1
  180. package/build/dex/trader-joe-v2.1.d.ts +0 -43
  181. package/build/dex/trader-joe-v2.1.js +0 -79
  182. package/build/dex/trader-joe-v2.1.js.map +0 -1
  183. package/build/executor/compressor/Compressor.d.ts +0 -23
  184. package/build/executor/compressor/Compressor.js +0 -144
  185. package/build/executor/compressor/Compressor.js.map +0 -1
  186. package/build/executor/compressor/compress.d.ts +0 -17
  187. package/build/executor/compressor/compress.js +0 -187
  188. package/build/executor/compressor/compress.js.map +0 -1
  189. package/build/executor/compressor/compress_functions/addressSubstitution.d.ts +0 -6
  190. package/build/executor/compressor/compress_functions/addressSubstitution.js +0 -29
  191. package/build/executor/compressor/compress_functions/addressSubstitution.js.map +0 -1
  192. package/build/executor/compressor/compress_functions/addressSubstitutionFromObject.d.ts +0 -10
  193. package/build/executor/compressor/compress_functions/addressSubstitutionFromObject.js +0 -40
  194. package/build/executor/compressor/compress_functions/addressSubstitutionFromObject.js.map +0 -1
  195. package/build/executor/compressor/compress_functions/byteSubstitution.d.ts +0 -11
  196. package/build/executor/compressor/compress_functions/byteSubstitution.js +0 -64
  197. package/build/executor/compressor/compress_functions/byteSubstitution.js.map +0 -1
  198. package/build/executor/compressor/compress_functions/ffSubstitution.d.ts +0 -7
  199. package/build/executor/compressor/compress_functions/ffSubstitution.js +0 -54
  200. package/build/executor/compressor/compress_functions/ffSubstitution.js.map +0 -1
  201. package/build/executor/compressor/compress_functions/findLongestAndSubstitute.d.ts +0 -8
  202. package/build/executor/compressor/compress_functions/findLongestAndSubstitute.js +0 -67
  203. package/build/executor/compressor/compress_functions/findLongestAndSubstitute.js.map +0 -1
  204. package/build/executor/compressor/compress_functions/findLongestAndSubstitute_old.d.ts +0 -8
  205. package/build/executor/compressor/compress_functions/findLongestAndSubstitute_old.js +0 -61
  206. package/build/executor/compressor/compress_functions/findLongestAndSubstitute_old.js.map +0 -1
  207. package/build/executor/compressor/compress_functions/reverseAddressSubstitution.d.ts +0 -6
  208. package/build/executor/compressor/compress_functions/reverseAddressSubstitution.js +0 -37
  209. package/build/executor/compressor/compress_functions/reverseAddressSubstitution.js.map +0 -1
  210. package/build/executor/compressor/compress_functions/searchUnusedBytes.d.ts +0 -9
  211. package/build/executor/compressor/compress_functions/searchUnusedBytes.js +0 -33
  212. package/build/executor/compressor/compress_functions/searchUnusedBytes.js.map +0 -1
  213. package/build/executor/compressor/fetchSaved.d.ts +0 -0
  214. package/build/executor/compressor/fetchSaved.js +0 -17
  215. package/build/executor/compressor/fetchSaved.js.map +0 -1
  216. package/build/executor/compressor/utils/computeCostL2.d.ts +0 -2
  217. package/build/executor/compressor/utils/computeCostL2.js +0 -14
  218. package/build/executor/compressor/utils/computeCostL2.js.map +0 -1
  219. package/build/executor/compressor/utils/findLongestDuplicatedString.d.ts +0 -2
  220. package/build/executor/compressor/utils/findLongestDuplicatedString.js +0 -30
  221. package/build/executor/compressor/utils/findLongestDuplicatedString.js.map +0 -1
  222. package/build/executor/compressor/utils/getAllIndexes.d.ts +0 -2
  223. package/build/executor/compressor/utils/getAllIndexes.js +0 -12
  224. package/build/executor/compressor/utils/getAllIndexes.js.map +0 -1
  225. package/build/executor/compressor/utils/getByteForAddress.d.ts +0 -2
  226. package/build/executor/compressor/utils/getByteForAddress.js +0 -14
  227. package/build/executor/compressor/utils/getByteForAddress.js.map +0 -1
  228. package/build/executor/compressor/utils/intTo2Bytes.d.ts +0 -2
  229. package/build/executor/compressor/utils/intTo2Bytes.js +0 -14
  230. package/build/executor/compressor/utils/intTo2Bytes.js.map +0 -1
  231. package/build/executor/compressor/utils/intTo3Bytes.d.ts +0 -2
  232. package/build/executor/compressor/utils/intTo3Bytes.js +0 -18
  233. package/build/executor/compressor/utils/intTo3Bytes.js.map +0 -1
  234. package/build/executor/compressor/utils/intToByte.d.ts +0 -2
  235. package/build/executor/compressor/utils/intToByte.js +0 -10
  236. package/build/executor/compressor/utils/intToByte.js.map +0 -1
@@ -0,0 +1,895 @@
1
+ import { Interface } from '@ethersproject/abi';
2
+ import { DeepReadonly } from 'ts-essentials';
3
+ import { lens } from '../../lens';
4
+ import { Address, Logger } from '../../types';
5
+ import { ComposedEventSubscriber } from '../../composed-event-subscriber';
6
+ import { ChainLinkSubscriber } from '../../lib/chainlink';
7
+ import { IDexHelper } from '../../dex-helper/idex-helper';
8
+ import {
9
+ ChainlinkConfig,
10
+ DecodedOracleConfig,
11
+ MorphoConfig,
12
+ OracleFeed,
13
+ OracleHyperparameter,
14
+ OracleQuoteType,
15
+ OracleReadType,
16
+ PoolConfig,
17
+ PoolState,
18
+ PythConfig,
19
+ TransmuterParams,
20
+ TransmuterState,
21
+ } from './types';
22
+ import TransmuterABI from '../../abi/angle-transmuter/Transmuter.json';
23
+ import TransmuterSidechainABI from '../../abi/angle-transmuter/TransmuterSidechain.json';
24
+ import { Contract } from 'web3-eth-contract';
25
+ import { TransmuterSubscriber } from './transmuter';
26
+ import { PythSubscriber } from './pyth';
27
+ import {
28
+ _quoteBurnExactInput,
29
+ _quoteBurnExactOutput,
30
+ _quoteMintExactInput,
31
+ _quoteMintExactOutput,
32
+ filterDictionaryOnly,
33
+ } from './utils';
34
+ import { RedstoneSubscriber } from './redstone';
35
+ import { formatEther, formatUnits, parseUnits } from 'ethers/lib/utils';
36
+ import { BackedSubscriber } from './backedOracle';
37
+ import { Network, SwapSide } from '../../constants';
38
+ import { ethers } from 'ethers';
39
+ import { MorphoOracleEventPool } from './morphoOracle';
40
+ import { bigIntify } from '../../utils';
41
+ import { MorphoVaultSubscriber } from './morphoVault';
42
+ import _ from 'lodash';
43
+
44
+ const FORCE_REGENERATE_STATE_INTERVAL_MS = 1000 * 60 * 60 * 24; // 24h
45
+
46
+ export class AngleTransmuterEventPool extends ComposedEventSubscriber<PoolState> {
47
+ public transmuter: Contract;
48
+ public readonly angleTransmuterIface;
49
+ readonly config: PoolConfig;
50
+ timer: NodeJS.Timer;
51
+
52
+ constructor(
53
+ readonly parentName: string,
54
+ protected network: number,
55
+ protected dexHelper: IDexHelper,
56
+ logger: Logger,
57
+ config: PoolConfig,
58
+ ) {
59
+ const chainlinkMap = Object.entries(config.oracles.chainlink).reduce(
60
+ (
61
+ acc: { [address: string]: ChainLinkSubscriber<PoolState> },
62
+ [key, value],
63
+ ) => {
64
+ acc[key] = new ChainLinkSubscriber<PoolState>(
65
+ value.proxy,
66
+ value.aggregator,
67
+ lens<DeepReadonly<PoolState>>().oracles.chainlink[key],
68
+ dexHelper.getLogger(`${key} ChainLink for ${parentName}-${network}`),
69
+ );
70
+ return acc;
71
+ },
72
+ {},
73
+ );
74
+ const backedMap = Object.entries(config.oracles.backed).reduce(
75
+ (
76
+ acc: { [address: string]: BackedSubscriber<PoolState> },
77
+ [key, value],
78
+ ) => {
79
+ acc[key] = new BackedSubscriber<PoolState>(
80
+ value.proxy,
81
+ value.aggregator,
82
+ lens<DeepReadonly<PoolState>>().oracles.chainlink[key],
83
+ dexHelper.getLogger(`${key} ChainLink for ${parentName}-${network}`),
84
+ );
85
+ return acc;
86
+ },
87
+ {},
88
+ );
89
+
90
+ const redstoneMap = Object.entries(config.oracles.redstone).reduce(
91
+ (
92
+ acc: { [address: string]: RedstoneSubscriber<PoolState> },
93
+ [key, value],
94
+ ) => {
95
+ acc[key] = new RedstoneSubscriber<PoolState>(
96
+ value.proxy,
97
+ value.aggregator,
98
+ lens<DeepReadonly<PoolState>>().oracles.chainlink[key],
99
+ dexHelper.getLogger(`${key} ChainLink for ${parentName}-${network}`),
100
+ );
101
+ return acc;
102
+ },
103
+ {},
104
+ );
105
+
106
+ const morphoMap = Object.entries(config.oracles.morpho).reduce(
107
+ (
108
+ acc: { [address: string]: MorphoVaultSubscriber<PoolState> },
109
+ [key, value],
110
+ ) => {
111
+ if (value.baseVault && value.baseVault !== ethers.constants.AddressZero)
112
+ acc[value.baseVault] = new MorphoVaultSubscriber<PoolState>(
113
+ value.baseVault,
114
+ lens<DeepReadonly<PoolState>>().oracles.morphoVault[
115
+ value.baseVault
116
+ ],
117
+ dexHelper.getLogger(
118
+ `${key}:${value.baseVault} Morpho Vault for ${parentName}-${network}`,
119
+ ),
120
+ );
121
+ if (
122
+ value.quoteVault &&
123
+ value.quoteVault !== ethers.constants.AddressZero
124
+ )
125
+ acc[value.quoteVault] = new MorphoVaultSubscriber<PoolState>(
126
+ value.quoteVault,
127
+ lens<DeepReadonly<PoolState>>().oracles.morphoVault[
128
+ value.quoteVault
129
+ ],
130
+ dexHelper.getLogger(
131
+ `${key}:${value.quoteVault} Morpho Vault for ${parentName}-${network}`,
132
+ ),
133
+ );
134
+ return acc;
135
+ },
136
+ {},
137
+ );
138
+
139
+ const pythListener: PythSubscriber<PoolState>[] = [];
140
+ if (!_.isEmpty(config.oracles.pyth)) {
141
+ pythListener[0] = new PythSubscriber<PoolState>(
142
+ config.oracles.pyth.proxy,
143
+ config.oracles.pyth.ids,
144
+ lens<DeepReadonly<PoolState>>().oracles.pyth,
145
+ dexHelper.getLogger(`Pyth for ${parentName}-${network}`),
146
+ );
147
+ }
148
+
149
+ const transmuterListener = new TransmuterSubscriber(
150
+ config.stablecoin.address,
151
+ config.transmuter,
152
+ config.collaterals,
153
+ network,
154
+ lens<DeepReadonly<PoolState>>().transmuter,
155
+ dexHelper.getLogger(`${parentName}-${network} Transmuter`),
156
+ );
157
+
158
+ super(
159
+ parentName,
160
+ 'Transmuter',
161
+ dexHelper.getLogger(`${parentName}-${network}`),
162
+ dexHelper,
163
+ [
164
+ ...Object.values(chainlinkMap),
165
+ ...Object.values(backedMap),
166
+ ...Object.values(redstoneMap),
167
+ ...Object.values(morphoMap),
168
+ transmuterListener,
169
+ ...pythListener,
170
+ ],
171
+ {
172
+ stablecoin: config.stablecoin,
173
+ transmuter: {} as TransmuterState,
174
+ oracles: { chainlink: {}, pyth: {}, morphoVault: {} },
175
+ },
176
+ );
177
+
178
+ this.angleTransmuterIface =
179
+ network === Network.MAINNET
180
+ ? new Interface(TransmuterABI)
181
+ : new Interface(TransmuterSidechainABI);
182
+ this.transmuter = new this.dexHelper.web3Provider.eth.Contract(
183
+ TransmuterABI as any,
184
+ config.transmuter,
185
+ );
186
+
187
+ this.config = config;
188
+
189
+ // some oracles don't emit events (redstone), need to regenerate state after some period of time. 1 day should provide up-to-date info
190
+ this.timer = setInterval(() => {
191
+ const blockNumber = this.dexHelper.blockManager.getLatestBlockNumber();
192
+ this.generateState(blockNumber).then(newState =>
193
+ this.setState(newState, blockNumber),
194
+ );
195
+ }, FORCE_REGENERATE_STATE_INTERVAL_MS);
196
+ }
197
+
198
+ releaseResources(): void {
199
+ clearInterval(this.timer);
200
+ }
201
+
202
+ async getStateOrGenerate(blockNumber: number): Promise<Readonly<PoolState>> {
203
+ const evenState = this.getState(blockNumber);
204
+ if (evenState) return evenState as PoolState;
205
+ const onChainState = await this.generateState(blockNumber);
206
+ this.setState(onChainState, blockNumber);
207
+ return onChainState as PoolState;
208
+ }
209
+
210
+ async generateState(blockNumber: number): Promise<DeepReadonly<PoolState>> {
211
+ const state = (await super.generateState(blockNumber)) as PoolState;
212
+ return this.stateToLowercase(state);
213
+ }
214
+
215
+ private stateToLowercase(state: PoolState): PoolState {
216
+ return {
217
+ oracles: state.oracles,
218
+ stablecoin: {
219
+ ...state.stablecoin,
220
+ address: state.stablecoin.address.toLowerCase(),
221
+ },
222
+ transmuter: {
223
+ ...state.transmuter,
224
+ collaterals: Object.fromEntries(
225
+ Object.entries(state.transmuter.collaterals).map(([k, v]) => [
226
+ k.toLowerCase(),
227
+ v,
228
+ ]),
229
+ ),
230
+ },
231
+ };
232
+ }
233
+
234
+ // Reference to the original implementation
235
+ // https://github.com/AngleProtocol/angle-transmuter/blob/6e1f2eb1f961d6c3b1cdaefe068d967c33c41936/contracts/transmuter/facets/Swapper.sol#L177
236
+ async getAmountOut(
237
+ _tokenIn: Address,
238
+ _tokenOut: Address,
239
+ side: SwapSide,
240
+ _amounts: number[],
241
+ blockNumber: number,
242
+ ): Promise<number[] | null> {
243
+ const state = await this.getStateOrGenerate(blockNumber);
244
+ const isMint = _tokenOut === state.stablecoin.address;
245
+ let oracleValue: number;
246
+ let minRatio = 0;
247
+ let collateral = _tokenIn;
248
+ if (isMint)
249
+ oracleValue = await this._readMint(
250
+ this.config,
251
+ state,
252
+ _tokenIn,
253
+ blockNumber,
254
+ );
255
+ else {
256
+ collateral = _tokenOut;
257
+ ({ oracleValue, minRatio } = await this._getBurnOracle(
258
+ this.config,
259
+ state,
260
+ _tokenOut,
261
+ blockNumber,
262
+ ));
263
+ }
264
+
265
+ const collatStablecoinIssued =
266
+ state.transmuter.collaterals[collateral].stablecoinsIssued;
267
+ const otherStablecoinIssued =
268
+ state.transmuter.totalStablecoinIssued - collatStablecoinIssued;
269
+ const fees = state.transmuter.collaterals[collateral].fees;
270
+ const stablecoinCap =
271
+ state.transmuter.collaterals[collateral].stablecoinCap;
272
+
273
+ return _amounts.map(_amount => {
274
+ if (isMint && side === SwapSide.SELL)
275
+ return _quoteMintExactInput(
276
+ oracleValue,
277
+ _amount,
278
+ fees,
279
+ collatStablecoinIssued,
280
+ otherStablecoinIssued,
281
+ stablecoinCap,
282
+ );
283
+ if (isMint && side === SwapSide.BUY)
284
+ return _quoteMintExactOutput(
285
+ oracleValue,
286
+ _amount,
287
+ fees,
288
+ collatStablecoinIssued,
289
+ otherStablecoinIssued,
290
+ stablecoinCap,
291
+ );
292
+ if (!isMint && side === SwapSide.SELL)
293
+ return _quoteBurnExactInput(
294
+ oracleValue,
295
+ minRatio,
296
+ _amount,
297
+ fees,
298
+ collatStablecoinIssued,
299
+ otherStablecoinIssued,
300
+ );
301
+ return _quoteBurnExactOutput(
302
+ oracleValue,
303
+ minRatio,
304
+ _amount,
305
+ fees,
306
+ collatStablecoinIssued,
307
+ otherStablecoinIssued,
308
+ );
309
+ });
310
+ }
311
+
312
+ static async getCollateralsList(
313
+ transmuterAddress: Address,
314
+ blockNumber: number | 'latest',
315
+ multiContract: Contract,
316
+ ) {
317
+ const tokensResult = (
318
+ await multiContract.methods
319
+ .aggregate([
320
+ {
321
+ target: transmuterAddress,
322
+ callData:
323
+ TransmuterSubscriber.transmuterCrosschainInterface.encodeFunctionData(
324
+ 'getCollateralList',
325
+ ),
326
+ },
327
+ ])
328
+ .call({}, blockNumber)
329
+ ).returnData;
330
+ const tokens: Address[] =
331
+ TransmuterSubscriber.transmuterCrosschainInterface.decodeFunctionResult(
332
+ 'getCollateralList',
333
+ tokensResult[0],
334
+ )[0];
335
+ // .map((t: any) => t.toLowerCase());
336
+ return tokens;
337
+ }
338
+
339
+ static async _fillMap(
340
+ chainlinkMap: ChainlinkConfig,
341
+ backedMap: ChainlinkConfig,
342
+ redstoneMap: ChainlinkConfig,
343
+ pythIds: PythConfig,
344
+ morphoMap: MorphoConfig,
345
+ oracleFeed: OracleFeed,
346
+ blockNumber: number | 'latest',
347
+ multiContract: Contract,
348
+ ): Promise<{
349
+ chainlinkMap: ChainlinkConfig;
350
+ backedMap: ChainlinkConfig;
351
+ redstoneMap: ChainlinkConfig;
352
+ pythIds: PythConfig;
353
+ morphoMap: MorphoConfig;
354
+ }> {
355
+ if (oracleFeed.isChainlink) {
356
+ await Promise.all(
357
+ oracleFeed.chainlink!.circuitChainlink.map(async feed => {
358
+ ({ chainlinkMap, backedMap, redstoneMap } =
359
+ await AngleTransmuterEventPool.generateStateChainlinkLike(
360
+ feed,
361
+ chainlinkMap,
362
+ backedMap,
363
+ redstoneMap,
364
+ blockNumber,
365
+ multiContract,
366
+ ));
367
+ }),
368
+ );
369
+ } else if (oracleFeed.isPyth) {
370
+ pythIds.ids = pythIds.ids.concat(oracleFeed.pyth!.feedIds);
371
+ } else if (oracleFeed.isMorpho) {
372
+ const morphoOracleResult = (
373
+ await multiContract.methods
374
+ .aggregate(
375
+ MorphoOracleEventPool.getGenerateInfoMultiCallInput(
376
+ oracleFeed.morpho!.oracle,
377
+ ),
378
+ )
379
+ .call({}, blockNumber)
380
+ ).returnData;
381
+ const morphoOracleInfo =
382
+ MorphoOracleEventPool.generateInfo(morphoOracleResult);
383
+ if (morphoOracleInfo.baseFeed1 !== ethers.constants.AddressZero)
384
+ ({ chainlinkMap, backedMap, redstoneMap } =
385
+ await AngleTransmuterEventPool.generateStateChainlinkLike(
386
+ morphoOracleInfo.baseFeed1,
387
+ chainlinkMap,
388
+ backedMap,
389
+ redstoneMap,
390
+ blockNumber,
391
+ multiContract,
392
+ ));
393
+ if (morphoOracleInfo.baseFeed2 !== ethers.constants.AddressZero)
394
+ ({ chainlinkMap, backedMap, redstoneMap } =
395
+ await AngleTransmuterEventPool.generateStateChainlinkLike(
396
+ morphoOracleInfo.baseFeed2,
397
+ chainlinkMap,
398
+ backedMap,
399
+ redstoneMap,
400
+ blockNumber,
401
+ multiContract,
402
+ ));
403
+ if (morphoOracleInfo.quoteFeed1 !== ethers.constants.AddressZero)
404
+ ({ chainlinkMap, backedMap, redstoneMap } =
405
+ await AngleTransmuterEventPool.generateStateChainlinkLike(
406
+ morphoOracleInfo.quoteFeed1,
407
+ chainlinkMap,
408
+ backedMap,
409
+ redstoneMap,
410
+ blockNumber,
411
+ multiContract,
412
+ ));
413
+ if (morphoOracleInfo.quoteFeed2 !== ethers.constants.AddressZero)
414
+ ({ chainlinkMap, backedMap, redstoneMap } =
415
+ await AngleTransmuterEventPool.generateStateChainlinkLike(
416
+ morphoOracleInfo.quoteFeed2,
417
+ chainlinkMap,
418
+ backedMap,
419
+ redstoneMap,
420
+ blockNumber,
421
+ multiContract,
422
+ ));
423
+ morphoMap[oracleFeed.morpho!.oracle] = morphoOracleInfo;
424
+ } else {
425
+ throw new Error('Unknown oracle feed');
426
+ }
427
+ return { chainlinkMap, backedMap, redstoneMap, pythIds, morphoMap };
428
+ }
429
+
430
+ static async generateStateChainlinkLike(
431
+ feed: string,
432
+ chainlinkMap: ChainlinkConfig,
433
+ backedMap: ChainlinkConfig,
434
+ redstoneMap: ChainlinkConfig,
435
+ blockNumber: number | 'latest',
436
+ multiContract: Contract,
437
+ ): Promise<{
438
+ chainlinkMap: ChainlinkConfig;
439
+ backedMap: ChainlinkConfig;
440
+ redstoneMap: ChainlinkConfig;
441
+ }> {
442
+ let aggreagator: Address;
443
+ let decimals: number;
444
+ // it can be Chainlink or Redstone feed
445
+ // if the call getReadAggregatorMultiCallInput fail with a Chainlink instance
446
+ // then it is a Redstone one
447
+ try {
448
+ // Chainlink
449
+ const proxyResult = (
450
+ await multiContract.methods
451
+ .aggregate([
452
+ ChainLinkSubscriber.getReadAggregatorMultiCallInput(feed),
453
+ ChainLinkSubscriber.getReadDecimal(feed),
454
+ ])
455
+ .call({}, blockNumber)
456
+ ).returnData;
457
+ aggreagator = ChainLinkSubscriber.readAggregator(proxyResult[0]);
458
+ decimals = Number(ChainLinkSubscriber.readDecimals(proxyResult[1]));
459
+ chainlinkMap[feed] = {
460
+ proxy: feed,
461
+ aggregator: aggreagator,
462
+ decimals: decimals,
463
+ };
464
+ } catch {
465
+ try {
466
+ // Redstone
467
+ const proxyResult = (
468
+ await multiContract.methods
469
+ .aggregate([
470
+ RedstoneSubscriber.getReadAggregatorMultiCallInput(feed),
471
+ RedstoneSubscriber.getReadDecimal(feed),
472
+ ])
473
+ .call({}, blockNumber)
474
+ ).returnData;
475
+ aggreagator = RedstoneSubscriber.readAggregator(proxyResult[0]);
476
+ decimals = Number(RedstoneSubscriber.readDecimals(proxyResult[1]));
477
+ redstoneMap[feed] = {
478
+ proxy: feed,
479
+ aggregator: aggreagator,
480
+ decimals: decimals,
481
+ };
482
+ } catch {
483
+ // Backed
484
+ const proxyResult = (
485
+ await multiContract.methods
486
+ .aggregate([ChainLinkSubscriber.getReadDecimal(feed)])
487
+ .call({}, blockNumber)
488
+ ).returnData;
489
+ decimals = Number(ChainLinkSubscriber.readDecimals(proxyResult[0]));
490
+ backedMap[feed] = {
491
+ proxy: feed,
492
+ aggregator: feed,
493
+ decimals: decimals,
494
+ };
495
+ }
496
+ }
497
+ return { chainlinkMap, backedMap, redstoneMap };
498
+ }
499
+
500
+ static async getOraclesConfig(
501
+ transmuterAddress: Address,
502
+ pythAddress: Address,
503
+ collaterals: Address[],
504
+ blockNumber: number | 'latest',
505
+ multiContract: Contract,
506
+ ) {
507
+ const getOracleConfigData = collaterals.map(collat => {
508
+ return {
509
+ callData:
510
+ TransmuterSubscriber.transmuterCrosschainInterface.encodeFunctionData(
511
+ 'getOracle',
512
+ [collat],
513
+ ),
514
+ target: transmuterAddress,
515
+ };
516
+ });
517
+ const oracleConfigResult = (
518
+ await multiContract.methods
519
+ .aggregate(getOracleConfigData)
520
+ .call({}, blockNumber)
521
+ ).returnData;
522
+
523
+ const oracleConfigs: DecodedOracleConfig[] = oracleConfigResult.map(
524
+ (p: any) =>
525
+ TransmuterSubscriber.transmuterCrosschainInterface.decodeFunctionResult(
526
+ 'getOracle',
527
+ p,
528
+ ),
529
+ );
530
+
531
+ let chainlinkMap: ChainlinkConfig = {} as ChainlinkConfig;
532
+ let backedMap: ChainlinkConfig = {} as ChainlinkConfig;
533
+ let redstoneMap: ChainlinkConfig = {} as ChainlinkConfig;
534
+ let pythIds: PythConfig = { proxy: pythAddress, ids: [] } as PythConfig;
535
+ let morphoMap: MorphoConfig = {} as MorphoConfig;
536
+
537
+ await Promise.all(
538
+ oracleConfigs.map(async oracleConfigDecoded => {
539
+ if (oracleConfigDecoded.oracleType !== OracleReadType.EXTERNAL) {
540
+ // add all the feed oracles used to their respective channels
541
+ if (
542
+ oracleConfigDecoded.oracleType === OracleReadType.CHAINLINK_FEEDS ||
543
+ oracleConfigDecoded.oracleType === OracleReadType.PYTH ||
544
+ oracleConfigDecoded.oracleType === OracleReadType.MORPHO_ORACLE
545
+ ) {
546
+ const oracleFeed = TransmuterSubscriber._decodeOracleFeed(
547
+ oracleConfigDecoded.oracleType,
548
+ oracleConfigDecoded.oracleData,
549
+ );
550
+ ({ chainlinkMap, backedMap, redstoneMap, pythIds, morphoMap } =
551
+ await AngleTransmuterEventPool._fillMap(
552
+ chainlinkMap,
553
+ backedMap,
554
+ redstoneMap,
555
+ pythIds,
556
+ morphoMap,
557
+ oracleFeed,
558
+ blockNumber,
559
+ multiContract,
560
+ ));
561
+ }
562
+ if (
563
+ oracleConfigDecoded.targetType === OracleReadType.CHAINLINK_FEEDS ||
564
+ oracleConfigDecoded.targetType === OracleReadType.PYTH ||
565
+ oracleConfigDecoded.targetType === OracleReadType.MORPHO_ORACLE
566
+ ) {
567
+ const oracleFeed = TransmuterSubscriber._decodeOracleFeed(
568
+ oracleConfigDecoded.targetType,
569
+ oracleConfigDecoded.targetData,
570
+ );
571
+ ({ chainlinkMap, backedMap, redstoneMap, pythIds, morphoMap } =
572
+ await AngleTransmuterEventPool._fillMap(
573
+ chainlinkMap,
574
+ backedMap,
575
+ redstoneMap,
576
+ pythIds,
577
+ morphoMap,
578
+ oracleFeed,
579
+ blockNumber,
580
+ multiContract,
581
+ ));
582
+ }
583
+ }
584
+ }),
585
+ );
586
+ return {
587
+ chainlink: chainlinkMap,
588
+ backed: backedMap,
589
+ redstone: redstoneMap,
590
+ pyth: pythIds,
591
+ morpho: morphoMap,
592
+ };
593
+ }
594
+
595
+ static async getConfig(
596
+ dexParams: TransmuterParams,
597
+ blockNumber: number | 'latest',
598
+ multiContract: Contract,
599
+ ): Promise<PoolConfig> {
600
+ const collaterals = await AngleTransmuterEventPool.getCollateralsList(
601
+ dexParams.transmuter,
602
+ blockNumber,
603
+ multiContract,
604
+ );
605
+
606
+ // get all oracles feed
607
+ const oracles = await AngleTransmuterEventPool.getOraclesConfig(
608
+ dexParams.transmuter,
609
+ dexParams.pyth!,
610
+ collaterals,
611
+ blockNumber,
612
+ multiContract,
613
+ );
614
+
615
+ return {
616
+ stablecoin: dexParams.stablecoin,
617
+ transmuter: dexParams.transmuter,
618
+ collaterals: collaterals.map(el => el.toLowerCase()),
619
+ oracles: oracles,
620
+ };
621
+ }
622
+
623
+ /*//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
624
+ UTILS
625
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////////////*/
626
+
627
+ _readMint(
628
+ config: PoolConfig,
629
+ state: PoolState,
630
+ collateral: Address,
631
+ blockNumber: number,
632
+ ): number {
633
+ const configOracle = state.transmuter.collaterals[collateral].config;
634
+ if (configOracle.oracleType === OracleReadType.EXTERNAL) {
635
+ return 1;
636
+ }
637
+ let target: number;
638
+ let spot: number;
639
+ ({ spot, target } = this._readSpotAndTarget(
640
+ config,
641
+ state,
642
+ collateral,
643
+ blockNumber,
644
+ ));
645
+
646
+ if (target < spot) spot = target;
647
+ return spot;
648
+ }
649
+
650
+ _getBurnOracle(
651
+ config: PoolConfig,
652
+ state: PoolState,
653
+ collateral: Address,
654
+ blockNumber: number,
655
+ ): { oracleValue: number; minRatio: number } {
656
+ return config.collaterals.reduce(
657
+ (
658
+ acc: { oracleValue: number; minRatio: number },
659
+ comparedCollat: string,
660
+ j: number,
661
+ ) => {
662
+ let ratio: number;
663
+ if (comparedCollat.toLowerCase() === collateral.toLowerCase()) {
664
+ let oracleValue: number;
665
+ ({ oracleValue, ratio } = this._readBurn(
666
+ config,
667
+ state,
668
+ comparedCollat,
669
+ blockNumber,
670
+ ));
671
+ acc.oracleValue = oracleValue;
672
+ } else
673
+ ({ ratio } = this._readBurn(
674
+ config,
675
+ state,
676
+ comparedCollat,
677
+ blockNumber,
678
+ ));
679
+ if (ratio < acc.minRatio) acc.minRatio = ratio;
680
+ return acc;
681
+ },
682
+ { oracleValue: 0, minRatio: 1 },
683
+ );
684
+ }
685
+
686
+ _readBurn(
687
+ config: PoolConfig,
688
+ state: PoolState,
689
+ collateral: Address,
690
+ blockNumber: number,
691
+ ): { oracleValue: number; ratio: number } {
692
+ const configOracle = state.transmuter.collaterals[collateral].config;
693
+ if (configOracle.oracleType === OracleReadType.EXTERNAL) {
694
+ return { oracleValue: 1, ratio: 1 };
695
+ }
696
+ let spot: number;
697
+ let target: number;
698
+ let burnRatioDeviation: number;
699
+ ({ spot, target, burnRatioDeviation } = this._readSpotAndTarget(
700
+ config,
701
+ state,
702
+ collateral,
703
+ blockNumber,
704
+ ));
705
+
706
+ let ratio = 1;
707
+ if (spot < target * (1 - burnRatioDeviation)) ratio = spot / target;
708
+ else if (spot < target) spot = target;
709
+ return { oracleValue: spot, ratio };
710
+ }
711
+
712
+ _readSpotAndTarget(
713
+ config: PoolConfig,
714
+ state: PoolState,
715
+ collateral: Address,
716
+ blockNumber: number,
717
+ ): {
718
+ spot: number;
719
+ target: number;
720
+ userDeviation: number;
721
+ burnRatioDeviation: number;
722
+ } {
723
+ const configOracle = state.transmuter.collaterals[collateral].config;
724
+ const hyperparameters = filterDictionaryOnly(
725
+ ethers.utils.defaultAbiCoder.decode(
726
+ ['uint128 userDeviation', 'uint128 burnRatioDeviation'],
727
+ configOracle.hyperparameters,
728
+ ),
729
+ ) as unknown as OracleHyperparameter;
730
+ const userDeviation = Number.parseFloat(
731
+ formatEther(hyperparameters.userDeviation.toString()),
732
+ );
733
+ const burnRatioDeviation = Number.parseFloat(
734
+ formatEther(hyperparameters.burnRatioDeviation.toString()),
735
+ );
736
+ const targetPrice = this._read(
737
+ config,
738
+ state,
739
+ configOracle.targetType,
740
+ configOracle.targetFeed,
741
+ 1,
742
+ );
743
+ let oracleValue = this._read(
744
+ config,
745
+ state,
746
+ configOracle.oracleType,
747
+ configOracle.oracleFeed,
748
+ targetPrice,
749
+ );
750
+
751
+ if (
752
+ targetPrice * (1 - userDeviation) < oracleValue &&
753
+ oracleValue < targetPrice * (1 + userDeviation)
754
+ )
755
+ oracleValue = targetPrice;
756
+ return {
757
+ spot: oracleValue,
758
+ target: targetPrice,
759
+ userDeviation,
760
+ burnRatioDeviation,
761
+ };
762
+ }
763
+
764
+ _quoteAmount(quoteType: OracleQuoteType, baseValue: number): number {
765
+ if (quoteType === OracleQuoteType.UNIT) return 1;
766
+ return baseValue;
767
+ }
768
+
769
+ _read(
770
+ config: PoolConfig,
771
+ state: PoolState,
772
+ oracleType: OracleReadType,
773
+ feed: OracleFeed,
774
+ baseValue: number,
775
+ ): number {
776
+ let price = 1;
777
+ if (oracleType === OracleReadType.CHAINLINK_FEEDS) {
778
+ price = this._quoteAmount(feed.chainlink!.quoteType, baseValue);
779
+ for (let i = 0; i < feed.chainlink!.circuitChainlink.length; i++) {
780
+ ({ price } = this._readChainlink(
781
+ config,
782
+ state,
783
+ feed.chainlink!.circuitChainlink[i],
784
+ feed.chainlink!.circuitChainIsMultiplied[i],
785
+ price,
786
+ ));
787
+ }
788
+ } else if (oracleType === OracleReadType.PYTH) {
789
+ price = this._quoteAmount(feed.pyth!.quoteType, baseValue);
790
+ for (let i = 0; i < feed.pyth!.feedIds.length; i++) {
791
+ const id = feed.pyth!.feedIds[i];
792
+ const rate = state.oracles.pyth[id].answer;
793
+ if (feed.pyth!.isMultiplied[i] === 1) price *= rate;
794
+ else price /= rate;
795
+ }
796
+ } else if (oracleType === OracleReadType.STABLE) {
797
+ price = 1;
798
+ } else if (oracleType === OracleReadType.NO_ORACLE) {
799
+ price = baseValue;
800
+ } else if (oracleType == OracleReadType.MAX) {
801
+ price = feed.maxValue!;
802
+ } else if (oracleType == OracleReadType.MORPHO_ORACLE) {
803
+ const morphoOracleConfig = config.oracles.morpho[feed.morpho!.oracle];
804
+ const baseVaultPrice =
805
+ morphoOracleConfig.baseVault !== ethers.constants.AddressZero
806
+ ? (morphoOracleConfig.baseVaultConversion *
807
+ state.oracles.morphoVault[morphoOracleConfig.baseVault]
808
+ .totalAssets) /
809
+ state.oracles.morphoVault[morphoOracleConfig.baseVault].totalSupply
810
+ : 1n;
811
+ const quoteVaultPrice =
812
+ morphoOracleConfig.quoteVault !== ethers.constants.AddressZero
813
+ ? (morphoOracleConfig.quoteVaultConversion *
814
+ state.oracles.morphoVault[morphoOracleConfig.quoteVault]
815
+ .totalAssets) /
816
+ state.oracles.morphoVault[morphoOracleConfig.quoteVault].totalSupply
817
+ : 1n;
818
+ const baseFeed1Price =
819
+ morphoOracleConfig.baseFeed1 !== ethers.constants.AddressZero
820
+ ? this._readMorphoFeedChainlink(
821
+ config,
822
+ state,
823
+ morphoOracleConfig.baseFeed1,
824
+ )
825
+ : 1n;
826
+ const baseFeed2Price =
827
+ morphoOracleConfig.baseFeed2 !== ethers.constants.AddressZero
828
+ ? this._readMorphoFeedChainlink(
829
+ config,
830
+ state,
831
+ morphoOracleConfig.baseFeed2,
832
+ )
833
+ : 1n;
834
+ const quoteFeed1Price =
835
+ morphoOracleConfig.quoteFeed1 !== ethers.constants.AddressZero
836
+ ? this._readMorphoFeedChainlink(
837
+ config,
838
+ state,
839
+ morphoOracleConfig.quoteFeed1,
840
+ )
841
+ : 1n;
842
+ const quoteFeed2Price =
843
+ morphoOracleConfig.quoteFeed2 !== ethers.constants.AddressZero
844
+ ? this._readMorphoFeedChainlink(
845
+ config,
846
+ state,
847
+ morphoOracleConfig.quoteFeed2,
848
+ )
849
+ : 1n;
850
+ price = Number.parseFloat(
851
+ formatUnits(
852
+ (morphoOracleConfig.scaleFactor *
853
+ baseVaultPrice *
854
+ baseFeed1Price *
855
+ baseFeed2Price) /
856
+ quoteVaultPrice /
857
+ quoteFeed1Price /
858
+ quoteFeed2Price,
859
+ 36,
860
+ ),
861
+ );
862
+ }
863
+ return price;
864
+ }
865
+
866
+ _readChainlink(
867
+ config: PoolConfig,
868
+ state: PoolState,
869
+ id: string,
870
+ isMultiplied: number,
871
+ price: number,
872
+ ): { price: number; decimals: number } {
873
+ let decimals: number;
874
+ if (Object.keys(config.oracles.chainlink).includes(id))
875
+ decimals = config.oracles.chainlink[id].decimals;
876
+ else if (Object.keys(config.oracles.redstone).includes(id))
877
+ decimals = config.oracles.redstone[id].decimals;
878
+ else decimals = config.oracles.backed[id].decimals;
879
+ const rate = Number.parseFloat(
880
+ formatUnits(state.oracles.chainlink[id].answer.toString(), decimals),
881
+ );
882
+ if (isMultiplied === 1) price *= rate;
883
+ else price /= rate;
884
+ return { price, decimals };
885
+ }
886
+
887
+ _readMorphoFeedChainlink(
888
+ config: PoolConfig,
889
+ state: PoolState,
890
+ id: string,
891
+ ): bigint {
892
+ const { price, decimals } = this._readChainlink(config, state, id, 1, 1);
893
+ return bigIntify(parseUnits(price.toString(), decimals));
894
+ }
895
+ }