@paraswap/dex-lib 3.7.2 → 3.7.3-idle

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 (251) 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/config.js +2 -0
  12. package/build/config.js.map +1 -1
  13. package/build/dex/aave-v1/aave-v1.d.ts +2 -1
  14. package/build/dex/aave-v1/aave-v1.js +22 -0
  15. package/build/dex/aave-v1/aave-v1.js.map +1 -1
  16. package/build/dex/aave-v3-stata/aave-v3-stata.d.ts +50 -0
  17. package/build/dex/aave-v3-stata/aave-v3-stata.js +385 -0
  18. package/build/dex/aave-v3-stata/aave-v3-stata.js.map +1 -0
  19. package/build/dex/aave-v3-stata/config.d.ts +4 -0
  20. package/build/dex/aave-v3-stata/config.js +50 -0
  21. package/build/dex/aave-v3-stata/config.js.map +1 -0
  22. package/build/dex/aave-v3-stata/tokens.d.ts +10 -0
  23. package/build/dex/aave-v3-stata/tokens.js +43 -0
  24. package/build/dex/aave-v3-stata/tokens.js.map +1 -0
  25. package/build/dex/aave-v3-stata/types.d.ts +46 -0
  26. package/build/dex/aave-v3-stata/types.js +23 -0
  27. package/build/dex/aave-v3-stata/types.js.map +1 -0
  28. package/build/dex/aave-v3-stata/utils.d.ts +4 -0
  29. package/build/dex/aave-v3-stata/utils.js +67 -0
  30. package/build/dex/aave-v3-stata/utils.js.map +1 -0
  31. package/build/dex/angle-transmuter/angle-transmuter-pool.d.ts +67 -0
  32. package/build/dex/angle-transmuter/angle-transmuter-pool.js +448 -0
  33. package/build/dex/angle-transmuter/angle-transmuter-pool.js.map +1 -0
  34. package/build/dex/angle-transmuter/angle-transmuter.d.ts +59 -0
  35. package/build/dex/angle-transmuter/angle-transmuter.js +264 -0
  36. package/build/dex/angle-transmuter/angle-transmuter.js.map +1 -0
  37. package/build/dex/angle-transmuter/backedOracle.d.ts +19 -0
  38. package/build/dex/angle-transmuter/backedOracle.js +56 -0
  39. package/build/dex/angle-transmuter/backedOracle.js.map +1 -0
  40. package/build/dex/angle-transmuter/config.d.ts +4 -0
  41. package/build/dex/angle-transmuter/config.js +50 -0
  42. package/build/dex/angle-transmuter/config.js.map +1 -0
  43. package/build/dex/angle-transmuter/constants.d.ts +9 -0
  44. package/build/dex/angle-transmuter/constants.js +136 -0
  45. package/build/dex/angle-transmuter/constants.js.map +1 -0
  46. package/build/dex/angle-transmuter/morphoOracle.d.ts +42 -0
  47. package/build/dex/angle-transmuter/morphoOracle.js +179 -0
  48. package/build/dex/angle-transmuter/morphoOracle.js.map +1 -0
  49. package/build/dex/angle-transmuter/morphoVault.d.ts +20 -0
  50. package/build/dex/angle-transmuter/morphoVault.js +84 -0
  51. package/build/dex/angle-transmuter/morphoVault.js.map +1 -0
  52. package/build/dex/angle-transmuter/pyth.d.ts +19 -0
  53. package/build/dex/angle-transmuter/pyth.js +70 -0
  54. package/build/dex/angle-transmuter/pyth.js.map +1 -0
  55. package/build/dex/angle-transmuter/redstone.d.ts +20 -0
  56. package/build/dex/angle-transmuter/redstone.js +64 -0
  57. package/build/dex/angle-transmuter/redstone.js.map +1 -0
  58. package/build/dex/angle-transmuter/transmuter.d.ts +55 -0
  59. package/build/dex/angle-transmuter/transmuter.js +398 -0
  60. package/build/dex/angle-transmuter/transmuter.js.map +1 -0
  61. package/build/dex/angle-transmuter/types.d.ts +189 -0
  62. package/build/dex/angle-transmuter/types.js +33 -0
  63. package/build/dex/angle-transmuter/types.js.map +1 -0
  64. package/build/dex/angle-transmuter/utils.d.ts +9 -0
  65. package/build/dex/angle-transmuter/utils.js +257 -0
  66. package/build/dex/angle-transmuter/utils.js.map +1 -0
  67. package/build/dex/idle-dao/config.d.ts +6 -0
  68. package/build/dex/idle-dao/config.js +307 -0
  69. package/build/dex/idle-dao/config.js.map +1 -0
  70. package/build/dex/idle-dao/idle-dao-pool.d.ts +56 -0
  71. package/build/dex/idle-dao/idle-dao-pool.js +176 -0
  72. package/build/dex/idle-dao/idle-dao-pool.js.map +1 -0
  73. package/build/dex/idle-dao/idle-dao-pooling-pool.d.ts +16 -0
  74. package/build/dex/idle-dao/idle-dao-pooling-pool.js +57 -0
  75. package/build/dex/idle-dao/idle-dao-pooling-pool.js.map +1 -0
  76. package/build/dex/idle-dao/idle-dao.d.ts +41 -0
  77. package/build/dex/idle-dao/idle-dao.js +238 -0
  78. package/build/dex/idle-dao/idle-dao.js.map +1 -0
  79. package/build/dex/idle-dao/scripts.d.ts +9 -0
  80. package/build/dex/idle-dao/scripts.js +552 -0
  81. package/build/dex/idle-dao/scripts.js.map +1 -0
  82. package/build/dex/idle-dao/tokens.d.ts +9 -0
  83. package/build/dex/idle-dao/tokens.js +65 -0
  84. package/build/dex/idle-dao/tokens.js.map +1 -0
  85. package/build/dex/idle-dao/types.d.ts +35 -0
  86. package/build/dex/idle-dao/types.js +11 -0
  87. package/build/dex/idle-dao/types.js.map +1 -0
  88. package/build/dex/idle-dao/utils.d.ts +8 -0
  89. package/build/dex/idle-dao/utils.js +148 -0
  90. package/build/dex/idle-dao/utils.js.map +1 -0
  91. package/build/dex/index.js +4 -0
  92. package/build/dex/index.js.map +1 -1
  93. package/build/dex/platypus/platypus.d.ts +2 -1
  94. package/build/dex/platypus/platypus.js +31 -0
  95. package/build/dex/platypus/platypus.js.map +1 -1
  96. package/build/dex/swaap-v1/swaap-v1.d.ts +2 -1
  97. package/build/dex/swaap-v1/swaap-v1.js +10 -0
  98. package/build/dex/swaap-v1/swaap-v1.js.map +1 -1
  99. package/build/lib/chainlink.d.ts +2 -0
  100. package/build/lib/chainlink.js +9 -0
  101. package/build/lib/chainlink.js.map +1 -1
  102. package/build/types.d.ts +1 -0
  103. package/build/utils.d.ts +1 -1
  104. package/package.json +1 -1
  105. package/src/abi/Redstone.json +739 -0
  106. package/src/abi/angle-transmuter/ChainlinkAccessControlledOffchainAggregator.json +1246 -0
  107. package/src/abi/angle-transmuter/ChainlinkEACAggregatorProxy.json +509 -0
  108. package/src/abi/angle-transmuter/MorphoOracle.json +193 -0
  109. package/src/abi/angle-transmuter/MorphoVault.json +2506 -0
  110. package/src/abi/angle-transmuter/Pyth.json +1534 -0
  111. package/src/abi/angle-transmuter/Transmuter.json +1711 -0
  112. package/src/abi/angle-transmuter/TransmuterSidechain.json +1798 -0
  113. package/src/abi/idle-dao/idle-cdo-factory.json +38 -0
  114. package/src/abi/idle-dao/idle-cdo.json +1245 -0
  115. package/src/config.ts +3 -0
  116. package/src/dex/angle-transmuter/angle-transmuter-e2e.test.ts +156 -0
  117. package/src/dex/angle-transmuter/angle-transmuter-events.test.ts +365 -0
  118. package/src/dex/angle-transmuter/angle-transmuter-integration.test.ts +390 -0
  119. package/src/dex/angle-transmuter/angle-transmuter-pool.ts +895 -0
  120. package/src/dex/angle-transmuter/angle-transmuter.ts +408 -0
  121. package/src/dex/angle-transmuter/backedOracle.ts +91 -0
  122. package/src/dex/angle-transmuter/config.ts +50 -0
  123. package/src/dex/angle-transmuter/constants.ts +138 -0
  124. package/src/dex/angle-transmuter/morphoOracle.ts +335 -0
  125. package/src/dex/angle-transmuter/morphoVault.ts +134 -0
  126. package/src/dex/angle-transmuter/pyth.ts +105 -0
  127. package/src/dex/angle-transmuter/redstone.ts +105 -0
  128. package/src/dex/angle-transmuter/transmuter.ts +619 -0
  129. package/src/dex/angle-transmuter/types.ts +203 -0
  130. package/src/dex/angle-transmuter/utils.ts +358 -0
  131. package/src/dex/idle-dao/config.ts +309 -0
  132. package/src/dex/idle-dao/idle-dao-e2e.test.ts +112 -0
  133. package/src/dex/idle-dao/idle-dao-integration.test.ts +155 -0
  134. package/src/dex/idle-dao/idle-dao.ts +363 -0
  135. package/src/dex/idle-dao/scripts.ts +553 -0
  136. package/src/dex/idle-dao/tokens.ts +90 -0
  137. package/src/dex/idle-dao/types.ts +43 -0
  138. package/src/dex/idle-dao/utils.ts +215 -0
  139. package/src/dex/index.ts +4 -0
  140. package/src/lib/chainlink.ts +15 -0
  141. package/src/types.ts +1 -0
  142. package/tests/constants-e2e.ts +117 -6
  143. package/build/abi/Uncompressor.json +0 -250
  144. package/build/dex/bProtocol.d.ts +0 -18
  145. package/build/dex/bProtocol.js +0 -39
  146. package/build/dex/bProtocol.js.map +0 -1
  147. package/build/dex/bancor.d.ts +0 -26
  148. package/build/dex/bancor.js +0 -58
  149. package/build/dex/bancor.js.map +0 -1
  150. package/build/dex/compound.d.ts +0 -18
  151. package/build/dex/compound.js +0 -46
  152. package/build/dex/compound.js.map +0 -1
  153. package/build/dex/curve-v2.d.ts +0 -58
  154. package/build/dex/curve-v2.js +0 -180
  155. package/build/dex/curve-v2.js.map +0 -1
  156. package/build/dex/dodo-v1.d.ts +0 -33
  157. package/build/dex/dodo-v1.js +0 -63
  158. package/build/dex/dodo-v1.js.map +0 -1
  159. package/build/dex/etherfi/etherfi.d.ts +0 -26
  160. package/build/dex/etherfi/etherfi.js +0 -96
  161. package/build/dex/etherfi/etherfi.js.map +0 -1
  162. package/build/dex/lido.d.ts +0 -19
  163. package/build/dex/lido.js +0 -42
  164. package/build/dex/lido.js.map +0 -1
  165. package/build/dex/onebit.d.ts +0 -25
  166. package/build/dex/onebit.js +0 -42
  167. package/build/dex/onebit.js.map +0 -1
  168. package/build/dex/sdai/config.d.ts +0 -11
  169. package/build/dex/sdai/config.js +0 -21
  170. package/build/dex/sdai/config.js.map +0 -1
  171. package/build/dex/sdai/constants.d.ts +0 -2
  172. package/build/dex/sdai/constants.js +0 -6
  173. package/build/dex/sdai/constants.js.map +0 -1
  174. package/build/dex/sdai/scripts/validate-state.d.ts +0 -1
  175. package/build/dex/sdai/scripts/validate-state.js +0 -102
  176. package/build/dex/sdai/scripts/validate-state.js.map +0 -1
  177. package/build/dex/sdai/sdai-pool.d.ts +0 -16
  178. package/build/dex/sdai/sdai-pool.js +0 -85
  179. package/build/dex/sdai/sdai-pool.js.map +0 -1
  180. package/build/dex/sdai/sdai.d.ts +0 -51
  181. package/build/dex/sdai/sdai.js +0 -172
  182. package/build/dex/sdai/sdai.js.map +0 -1
  183. package/build/dex/sdai/types.d.ts +0 -21
  184. package/build/dex/sdai/types.js +0 -11
  185. package/build/dex/sdai/types.js.map +0 -1
  186. package/build/dex/sdai/utils.d.ts +0 -4
  187. package/build/dex/sdai/utils.js +0 -39
  188. package/build/dex/sdai/utils.js.map +0 -1
  189. package/build/dex/smoothy.d.ts +0 -26
  190. package/build/dex/smoothy.js +0 -48
  191. package/build/dex/smoothy.js.map +0 -1
  192. package/build/dex/stable-pool.d.ts +0 -28
  193. package/build/dex/stable-pool.js +0 -62
  194. package/build/dex/stable-pool.js.map +0 -1
  195. package/build/dex/trader-joe-v2.1.d.ts +0 -43
  196. package/build/dex/trader-joe-v2.1.js +0 -79
  197. package/build/dex/trader-joe-v2.1.js.map +0 -1
  198. package/build/executor/compressor/Compressor.d.ts +0 -23
  199. package/build/executor/compressor/Compressor.js +0 -144
  200. package/build/executor/compressor/Compressor.js.map +0 -1
  201. package/build/executor/compressor/compress.d.ts +0 -17
  202. package/build/executor/compressor/compress.js +0 -187
  203. package/build/executor/compressor/compress.js.map +0 -1
  204. package/build/executor/compressor/compress_functions/addressSubstitution.d.ts +0 -6
  205. package/build/executor/compressor/compress_functions/addressSubstitution.js +0 -29
  206. package/build/executor/compressor/compress_functions/addressSubstitution.js.map +0 -1
  207. package/build/executor/compressor/compress_functions/addressSubstitutionFromObject.d.ts +0 -10
  208. package/build/executor/compressor/compress_functions/addressSubstitutionFromObject.js +0 -40
  209. package/build/executor/compressor/compress_functions/addressSubstitutionFromObject.js.map +0 -1
  210. package/build/executor/compressor/compress_functions/byteSubstitution.d.ts +0 -11
  211. package/build/executor/compressor/compress_functions/byteSubstitution.js +0 -64
  212. package/build/executor/compressor/compress_functions/byteSubstitution.js.map +0 -1
  213. package/build/executor/compressor/compress_functions/ffSubstitution.d.ts +0 -7
  214. package/build/executor/compressor/compress_functions/ffSubstitution.js +0 -54
  215. package/build/executor/compressor/compress_functions/ffSubstitution.js.map +0 -1
  216. package/build/executor/compressor/compress_functions/findLongestAndSubstitute.d.ts +0 -8
  217. package/build/executor/compressor/compress_functions/findLongestAndSubstitute.js +0 -67
  218. package/build/executor/compressor/compress_functions/findLongestAndSubstitute.js.map +0 -1
  219. package/build/executor/compressor/compress_functions/findLongestAndSubstitute_old.d.ts +0 -8
  220. package/build/executor/compressor/compress_functions/findLongestAndSubstitute_old.js +0 -61
  221. package/build/executor/compressor/compress_functions/findLongestAndSubstitute_old.js.map +0 -1
  222. package/build/executor/compressor/compress_functions/reverseAddressSubstitution.d.ts +0 -6
  223. package/build/executor/compressor/compress_functions/reverseAddressSubstitution.js +0 -37
  224. package/build/executor/compressor/compress_functions/reverseAddressSubstitution.js.map +0 -1
  225. package/build/executor/compressor/compress_functions/searchUnusedBytes.d.ts +0 -9
  226. package/build/executor/compressor/compress_functions/searchUnusedBytes.js +0 -33
  227. package/build/executor/compressor/compress_functions/searchUnusedBytes.js.map +0 -1
  228. package/build/executor/compressor/fetchSaved.d.ts +0 -0
  229. package/build/executor/compressor/fetchSaved.js +0 -17
  230. package/build/executor/compressor/fetchSaved.js.map +0 -1
  231. package/build/executor/compressor/utils/computeCostL2.d.ts +0 -2
  232. package/build/executor/compressor/utils/computeCostL2.js +0 -14
  233. package/build/executor/compressor/utils/computeCostL2.js.map +0 -1
  234. package/build/executor/compressor/utils/findLongestDuplicatedString.d.ts +0 -2
  235. package/build/executor/compressor/utils/findLongestDuplicatedString.js +0 -30
  236. package/build/executor/compressor/utils/findLongestDuplicatedString.js.map +0 -1
  237. package/build/executor/compressor/utils/getAllIndexes.d.ts +0 -2
  238. package/build/executor/compressor/utils/getAllIndexes.js +0 -12
  239. package/build/executor/compressor/utils/getAllIndexes.js.map +0 -1
  240. package/build/executor/compressor/utils/getByteForAddress.d.ts +0 -2
  241. package/build/executor/compressor/utils/getByteForAddress.js +0 -14
  242. package/build/executor/compressor/utils/getByteForAddress.js.map +0 -1
  243. package/build/executor/compressor/utils/intTo2Bytes.d.ts +0 -2
  244. package/build/executor/compressor/utils/intTo2Bytes.js +0 -14
  245. package/build/executor/compressor/utils/intTo2Bytes.js.map +0 -1
  246. package/build/executor/compressor/utils/intTo3Bytes.d.ts +0 -2
  247. package/build/executor/compressor/utils/intTo3Bytes.js +0 -18
  248. package/build/executor/compressor/utils/intTo3Bytes.js.map +0 -1
  249. package/build/executor/compressor/utils/intToByte.d.ts +0 -2
  250. package/build/executor/compressor/utils/intToByte.js +0 -10
  251. 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
+ }