@paraswap/dex-lib 3.6.3 → 3.6.4-transmuter

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