@upshiftfinance/sdk 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (346) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +408 -2
  3. package/lib/abis/AddressResolver.d.ts +28 -0
  4. package/lib/abis/AddressResolver.js +23 -0
  5. package/lib/abis/ChainlinkV3.d.ts +87 -0
  6. package/lib/abis/ChainlinkV3.js +117 -0
  7. package/lib/abis/ERC20.d.ts +168 -0
  8. package/lib/abis/ERC20.js +226 -0
  9. package/lib/abis/ERC20_Bytes32.d.ts +139 -0
  10. package/lib/abis/ERC20_Bytes32.js +196 -0
  11. package/lib/abis/ERC4626.d.ts +364 -0
  12. package/lib/abis/ERC4626.js +507 -0
  13. package/lib/abis/ERC721.d.ts +231 -0
  14. package/lib/abis/ERC721.js +321 -0
  15. package/lib/abis/FeeOracle.d.ts +120 -0
  16. package/lib/abis/FeeOracle.js +162 -0
  17. package/lib/abis/LendingPool.d.ts +1393 -0
  18. package/lib/abis/LendingPool.js +1807 -0
  19. package/lib/abis/LendingPoolV2.d.ts +1413 -0
  20. package/lib/abis/LendingPoolV2.js +1833 -0
  21. package/lib/abis/LendingPoolV3.d.ts +1677 -0
  22. package/lib/abis/LendingPoolV3.js +1160 -0
  23. package/lib/abis/Loan.d.ts +837 -0
  24. package/lib/abis/Loan.js +1080 -0
  25. package/lib/abis/MultiAssetNativeDepositWrapper.d.ts +137 -0
  26. package/lib/abis/MultiAssetNativeDepositWrapper.js +125 -0
  27. package/lib/abis/Multicall3.d.ts +30 -0
  28. package/lib/abis/Multicall3.js +97 -0
  29. package/lib/abis/OFT.d.ts +116 -0
  30. package/lib/abis/OFT.js +85 -0
  31. package/lib/abis/PoolAdapter.d.ts +36 -0
  32. package/lib/abis/PoolAdapter.js +51 -0
  33. package/lib/abis/RewardDistributor.d.ts +267 -0
  34. package/lib/abis/RewardDistributor.js +352 -0
  35. package/lib/abis/RwaRedeemSubaccount.d.ts +747 -0
  36. package/lib/abis/RwaRedeemSubaccount.js +548 -0
  37. package/lib/abis/SmartAccount.d.ts +17 -0
  38. package/lib/abis/SmartAccount.js +19 -0
  39. package/lib/abis/SwapRouter.d.ts +953 -0
  40. package/lib/abis/SwapRouter.js +1228 -0
  41. package/lib/abis/TextResolver.d.ts +16 -0
  42. package/lib/abis/TextResolver.js +16 -0
  43. package/lib/abis/TokenizedVaultV2.d.ts +1364 -0
  44. package/lib/abis/TokenizedVaultV2.js +1041 -0
  45. package/lib/abis/TokenizedVaultV2DepositWithPermit.d.ts +1456 -0
  46. package/lib/abis/TokenizedVaultV2DepositWithPermit.js +1878 -0
  47. package/lib/abis/TokenizedVaultV2Receipt.d.ts +1568 -0
  48. package/lib/abis/TokenizedVaultV2Receipt.js +1061 -0
  49. package/lib/abis/TokenizedVaultV2SenderAllocationWhitelist.d.ts +454 -0
  50. package/lib/abis/TokenizedVaultV2SenderAllocationWhitelist.js +327 -0
  51. package/lib/abis/TokenizedVaultV2WhitelistedAllocation.d.ts +1466 -0
  52. package/lib/abis/TokenizedVaultV2WhitelistedAllocation.js +1092 -0
  53. package/lib/abis/TokenizedVaultV2WhitelistedAssets.d.ts +274 -0
  54. package/lib/abis/TokenizedVaultV2WhitelistedAssets.js +167 -0
  55. package/lib/abis/UniversalResolverResolve.d.ts +69 -0
  56. package/lib/abis/UniversalResolverResolve.js +35 -0
  57. package/lib/abis/UniversalSignatureValidator.d.ts +17 -0
  58. package/lib/abis/UniversalSignatureValidator.js +30 -0
  59. package/lib/abis/WrapperAdapter.d.ts +71 -0
  60. package/lib/abis/WrapperAdapter.js +77 -0
  61. package/lib/abis/index.d.ts +34 -0
  62. package/lib/abis/index.js +51 -0
  63. package/lib/adapters/evm/getters.d.ts +19 -0
  64. package/lib/adapters/evm/getters.js +249 -0
  65. package/lib/adapters/evm/index.d.ts +539 -0
  66. package/lib/adapters/evm/index.js +656 -0
  67. package/lib/adapters/evm/utils.d.ts +8 -0
  68. package/lib/adapters/evm/utils.js +51 -0
  69. package/lib/adapters/solana/confirm.d.ts +88 -0
  70. package/lib/adapters/solana/confirm.js +291 -0
  71. package/lib/adapters/solana/constants.d.ts +42 -0
  72. package/lib/adapters/solana/constants.js +59 -0
  73. package/lib/adapters/solana/getters.d.ts +42 -0
  74. package/lib/adapters/solana/getters.js +275 -0
  75. package/lib/adapters/solana/idl/vault-idl.d.ts +700 -0
  76. package/lib/adapters/solana/idl/vault-idl.js +2101 -0
  77. package/lib/adapters/solana/idl/withdrawal-queue-idl.d.ts +795 -0
  78. package/lib/adapters/solana/idl/withdrawal-queue-idl.js +1864 -0
  79. package/lib/adapters/solana/index.d.ts +372 -0
  80. package/lib/adapters/solana/index.js +459 -0
  81. package/lib/adapters/solana/pricing.d.ts +184 -0
  82. package/lib/adapters/solana/pricing.js +239 -0
  83. package/lib/adapters/solana/queue-hints.d.ts +29 -0
  84. package/lib/adapters/solana/queue-hints.js +59 -0
  85. package/lib/adapters/solana/queue.actions.d.ts +97 -0
  86. package/lib/adapters/solana/queue.actions.js +468 -0
  87. package/lib/adapters/solana/token-reads.d.ts +108 -0
  88. package/lib/adapters/solana/token-reads.js +145 -0
  89. package/lib/adapters/solana/types.d.ts +325 -0
  90. package/lib/adapters/solana/types.js +3 -0
  91. package/lib/adapters/solana/utils.d.ts +229 -0
  92. package/lib/adapters/solana/utils.js +773 -0
  93. package/lib/adapters/solana/vault.actions.d.ts +140 -0
  94. package/lib/adapters/solana/vault.actions.js +657 -0
  95. package/lib/adapters/solana/withdrawal-queue.d.ts +125 -0
  96. package/lib/adapters/solana/withdrawal-queue.js +301 -0
  97. package/lib/adapters/stellar/actions.d.ts +41 -0
  98. package/lib/adapters/stellar/actions.js +126 -0
  99. package/lib/adapters/stellar/constants.d.ts +43 -0
  100. package/lib/adapters/stellar/constants.js +53 -0
  101. package/lib/adapters/stellar/getters.d.ts +68 -0
  102. package/lib/adapters/stellar/getters.js +290 -0
  103. package/lib/adapters/stellar/index.d.ts +126 -0
  104. package/lib/adapters/stellar/index.js +187 -0
  105. package/lib/adapters/stellar/invocation.d.ts +55 -0
  106. package/lib/adapters/stellar/invocation.js +92 -0
  107. package/lib/adapters/stellar/soroban.d.ts +131 -0
  108. package/lib/adapters/stellar/soroban.js +617 -0
  109. package/lib/adapters/stellar/submit.d.ts +42 -0
  110. package/lib/adapters/stellar/submit.js +195 -0
  111. package/lib/adapters/stellar/types.d.ts +57 -0
  112. package/lib/adapters/stellar/types.js +6 -0
  113. package/lib/adapters/stellar/utils.d.ts +24 -0
  114. package/lib/adapters/stellar/utils.js +34 -0
  115. package/lib/adapters/sui/constants.d.ts +1 -0
  116. package/lib/adapters/sui/constants.js +9 -0
  117. package/lib/adapters/sui/getters.d.ts +24 -0
  118. package/lib/adapters/sui/getters.js +134 -0
  119. package/lib/adapters/sui/index.d.ts +53 -0
  120. package/lib/adapters/sui/index.js +96 -0
  121. package/lib/adapters/sui/transformer.d.ts +10 -0
  122. package/lib/adapters/sui/transformer.js +115 -0
  123. package/lib/adapters/sui/types.d.ts +70 -0
  124. package/lib/adapters/sui/types.js +3 -0
  125. package/lib/adapters/sui/utils.d.ts +8 -0
  126. package/lib/adapters/sui/utils.js +17 -0
  127. package/lib/core/analytics/app-name.d.ts +27 -0
  128. package/lib/core/analytics/app-name.js +48 -0
  129. package/lib/core/analytics/chain-name.d.ts +9 -0
  130. package/lib/core/analytics/chain-name.js +34 -0
  131. package/lib/core/analytics/constants.d.ts +15 -0
  132. package/lib/core/analytics/constants.js +28 -0
  133. package/lib/core/analytics/env.d.ts +46 -0
  134. package/lib/core/analytics/env.js +147 -0
  135. package/lib/core/analytics/index.d.ts +39 -0
  136. package/lib/core/analytics/index.js +83 -0
  137. package/lib/core/analytics/instrumentation.d.ts +29 -0
  138. package/lib/core/analytics/instrumentation.js +292 -0
  139. package/lib/core/analytics/method-taxonomy.d.ts +19 -0
  140. package/lib/core/analytics/method-taxonomy.js +156 -0
  141. package/lib/core/analytics/metrics.d.ts +48 -0
  142. package/lib/core/analytics/metrics.js +142 -0
  143. package/lib/core/analytics/sanitize.d.ts +57 -0
  144. package/lib/core/analytics/sanitize.js +272 -0
  145. package/lib/core/analytics/sentry-runtime.d.ts +15 -0
  146. package/lib/core/analytics/sentry-runtime.js +131 -0
  147. package/lib/core/analytics/sentry.d.ts +100 -0
  148. package/lib/core/analytics/sentry.js +696 -0
  149. package/lib/core/analytics/types.d.ts +55 -0
  150. package/lib/core/analytics/types.js +3 -0
  151. package/lib/core/analytics/user-identity.d.ts +55 -0
  152. package/lib/core/analytics/user-identity.js +218 -0
  153. package/lib/core/analytics/version.d.ts +6 -0
  154. package/lib/core/analytics/version.js +10 -0
  155. package/lib/core/attribution.d.ts +159 -0
  156. package/lib/core/attribution.js +194 -0
  157. package/lib/core/auth/index.d.ts +1 -0
  158. package/lib/core/auth/index.js +18 -0
  159. package/lib/core/auth/verify.d.ts +2 -0
  160. package/lib/core/auth/verify.js +35 -0
  161. package/lib/core/base.class.d.ts +193 -0
  162. package/lib/core/base.class.js +163 -0
  163. package/lib/core/cache.d.ts +9 -0
  164. package/lib/core/cache.js +31 -0
  165. package/lib/core/constants/adapters.d.ts +103 -0
  166. package/lib/core/constants/adapters.js +180 -0
  167. package/lib/core/constants/core.d.ts +162 -0
  168. package/lib/core/constants/core.js +281 -0
  169. package/lib/core/constants/swap-router.d.ts +180 -0
  170. package/lib/core/constants/swap-router.js +203 -0
  171. package/lib/core/constants/vaults.d.ts +93 -0
  172. package/lib/core/constants/vaults.js +277 -0
  173. package/lib/core/constants/web3.d.ts +106 -0
  174. package/lib/core/constants/web3.js +251 -0
  175. package/lib/core/errors/index.d.ts +177 -0
  176. package/lib/core/errors/index.js +257 -0
  177. package/lib/core/fetcher.d.ts +237 -0
  178. package/lib/core/fetcher.js +1103 -0
  179. package/lib/core/helpers/adapters.d.ts +13 -0
  180. package/lib/core/helpers/adapters.js +39 -0
  181. package/lib/core/helpers/chain-address.d.ts +13 -0
  182. package/lib/core/helpers/chain-address.js +47 -0
  183. package/lib/core/helpers/chain-error.d.ts +207 -0
  184. package/lib/core/helpers/chain-error.js +697 -0
  185. package/lib/core/helpers/chain-support.d.ts +98 -0
  186. package/lib/core/helpers/chain-support.js +139 -0
  187. package/lib/core/helpers/concurrency.d.ts +51 -0
  188. package/lib/core/helpers/concurrency.js +79 -0
  189. package/lib/core/helpers/core.d.ts +92 -0
  190. package/lib/core/helpers/core.js +368 -0
  191. package/lib/core/helpers/explorer-link.d.ts +16 -0
  192. package/lib/core/helpers/explorer-link.js +26 -0
  193. package/lib/core/helpers/multicall.d.ts +68 -0
  194. package/lib/core/helpers/multicall.js +103 -0
  195. package/lib/core/helpers/revert-decode.d.ts +248 -0
  196. package/lib/core/helpers/revert-decode.js +515 -0
  197. package/lib/core/helpers/signer.d.ts +61 -0
  198. package/lib/core/helpers/signer.js +184 -0
  199. package/lib/core/helpers/swap-router.d.ts +269 -0
  200. package/lib/core/helpers/swap-router.js +542 -0
  201. package/lib/core/helpers/vault-version.d.ts +24 -0
  202. package/lib/core/helpers/vault-version.js +83 -0
  203. package/lib/core/helpers/vaults.d.ts +89 -0
  204. package/lib/core/helpers/vaults.js +241 -0
  205. package/lib/core/helpers/web3.d.ts +366 -0
  206. package/lib/core/helpers/web3.js +1034 -0
  207. package/lib/core/index.d.ts +23 -0
  208. package/lib/core/index.js +40 -0
  209. package/lib/core/logger/curator-alert.d.ts +217 -0
  210. package/lib/core/logger/curator-alert.js +276 -0
  211. package/lib/core/logger/index.d.ts +99 -0
  212. package/lib/core/logger/index.js +163 -0
  213. package/lib/core/logger/slack.d.ts +49 -0
  214. package/lib/core/logger/slack.js +111 -0
  215. package/lib/core/vault-metadata.d.ts +12 -0
  216. package/lib/core/vault-metadata.js +42 -0
  217. package/lib/core/version-check.d.ts +58 -0
  218. package/lib/core/version-check.js +182 -0
  219. package/lib/evm/index.d.ts +2 -0
  220. package/lib/evm/index.js +19 -0
  221. package/lib/evm/methods/crossChainVault.d.ts +132 -0
  222. package/lib/evm/methods/crossChainVault.js +857 -0
  223. package/lib/evm/methods/crossChainVaultRegistry.d.ts +92 -0
  224. package/lib/evm/methods/crossChainVaultRegistry.js +239 -0
  225. package/lib/evm/methods/index.d.ts +2 -0
  226. package/lib/evm/methods/index.js +19 -0
  227. package/lib/evm/types/crossChain.d.ts +363 -0
  228. package/lib/evm/types/crossChain.js +20 -0
  229. package/lib/evm/types/index.d.ts +1 -0
  230. package/lib/evm/types/index.js +18 -0
  231. package/lib/index.d.ts +30 -0
  232. package/lib/index.js +52 -0
  233. package/lib/main.d.ts +596 -0
  234. package/lib/main.js +681 -0
  235. package/lib/modules/api/fetcher.d.ts +108 -0
  236. package/lib/modules/api/fetcher.js +187 -0
  237. package/lib/modules/api/index.d.ts +1 -0
  238. package/lib/modules/api/index.js +6 -0
  239. package/lib/modules/api/main.d.ts +587 -0
  240. package/lib/modules/api/main.js +911 -0
  241. package/lib/modules/sub-accounts/fetcher.d.ts +53 -0
  242. package/lib/modules/sub-accounts/fetcher.js +96 -0
  243. package/lib/modules/sub-accounts/index.d.ts +2 -0
  244. package/lib/modules/sub-accounts/index.js +19 -0
  245. package/lib/modules/sub-accounts/main.d.ts +249 -0
  246. package/lib/modules/sub-accounts/main.js +211 -0
  247. package/lib/modules/sub-accounts/utils.d.ts +106 -0
  248. package/lib/modules/sub-accounts/utils.js +112 -0
  249. package/lib/modules/vaults/adapter.helpers.d.ts +84 -0
  250. package/lib/modules/vaults/adapter.helpers.js +202 -0
  251. package/lib/modules/vaults/fetcher.d.ts +147 -0
  252. package/lib/modules/vaults/fetcher.js +357 -0
  253. package/lib/modules/vaults/getters.d.ts +1060 -0
  254. package/lib/modules/vaults/getters.js +4079 -0
  255. package/lib/modules/vaults/index.d.ts +20 -0
  256. package/lib/modules/vaults/index.js +44 -0
  257. package/lib/modules/vaults/main.d.ts +607 -0
  258. package/lib/modules/vaults/main.js +1687 -0
  259. package/lib/modules/vaults/prefetch.d.ts +65 -0
  260. package/lib/modules/vaults/prefetch.js +120 -0
  261. package/lib/modules/vaults/read.actions.d.ts +406 -0
  262. package/lib/modules/vaults/read.actions.js +891 -0
  263. package/lib/modules/vaults/types.d.ts +149 -0
  264. package/lib/modules/vaults/types.js +3 -0
  265. package/lib/modules/vaults/utils/call-data-decoder.d.ts +61 -0
  266. package/lib/modules/vaults/utils/call-data-decoder.js +194 -0
  267. package/lib/modules/vaults/utils/date-utils.d.ts +50 -0
  268. package/lib/modules/vaults/utils/date-utils.js +84 -0
  269. package/lib/modules/vaults/utils.d.ts +153 -0
  270. package/lib/modules/vaults/utils.js +818 -0
  271. package/lib/modules/vaults/write.actions.d.ts +812 -0
  272. package/lib/modules/vaults/write.actions.js +2377 -0
  273. package/lib/polyfills.d.ts +1 -0
  274. package/lib/polyfills.js +12 -0
  275. package/lib/sdk.d.ts +30154 -0
  276. package/lib/services/coingecko/fetcher.d.ts +15 -0
  277. package/lib/services/coingecko/fetcher.js +67 -0
  278. package/lib/services/coingecko/index.d.ts +2 -0
  279. package/lib/services/coingecko/index.js +19 -0
  280. package/lib/services/coingecko/utils.d.ts +1 -0
  281. package/lib/services/coingecko/utils.js +24 -0
  282. package/lib/services/debank/fetcher.d.ts +126 -0
  283. package/lib/services/debank/fetcher.js +47 -0
  284. package/lib/services/debank/index.d.ts +2 -0
  285. package/lib/services/debank/index.js +19 -0
  286. package/lib/services/debank/utils.d.ts +50 -0
  287. package/lib/services/debank/utils.js +325 -0
  288. package/lib/services/layerzero/deposits.d.ts +55 -0
  289. package/lib/services/layerzero/deposits.js +174 -0
  290. package/lib/services/layerzero/redeems.d.ts +20 -0
  291. package/lib/services/layerzero/redeems.js +92 -0
  292. package/lib/services/layerzero/utils.d.ts +9 -0
  293. package/lib/services/layerzero/utils.js +22 -0
  294. package/lib/services/octavfi/fetcher.d.ts +33 -0
  295. package/lib/services/octavfi/fetcher.js +39 -0
  296. package/lib/services/octavfi/index.d.ts +3 -0
  297. package/lib/services/octavfi/index.js +20 -0
  298. package/lib/services/octavfi/types.d.ts +34 -0
  299. package/lib/services/octavfi/types.js +3 -0
  300. package/lib/services/octavfi/utils.d.ts +16 -0
  301. package/lib/services/octavfi/utils.js +244 -0
  302. package/lib/services/subgraph/fetcher.d.ts +2 -0
  303. package/lib/services/subgraph/fetcher.js +66 -0
  304. package/lib/services/subgraph/index.d.ts +2 -0
  305. package/lib/services/subgraph/index.js +19 -0
  306. package/lib/services/subgraph/schema.d.ts +48 -0
  307. package/lib/services/subgraph/schema.js +87 -0
  308. package/lib/services/subgraph/vaults.d.ts +83 -0
  309. package/lib/services/subgraph/vaults.js +1282 -0
  310. package/lib/services/swap-quotes/index.d.ts +105 -0
  311. package/lib/services/swap-quotes/index.js +77 -0
  312. package/lib/services/swap-quotes/paraswap.d.ts +51 -0
  313. package/lib/services/swap-quotes/paraswap.js +138 -0
  314. package/lib/services/untangled/fetcher.d.ts +40 -0
  315. package/lib/services/untangled/fetcher.js +110 -0
  316. package/lib/services/untangled/index.d.ts +3 -0
  317. package/lib/services/untangled/index.js +20 -0
  318. package/lib/services/untangled/types.d.ts +83 -0
  319. package/lib/services/untangled/types.js +10 -0
  320. package/lib/services/untangled/utils.d.ts +91 -0
  321. package/lib/services/untangled/utils.js +339 -0
  322. package/lib/types/api.d.ts +498 -0
  323. package/lib/types/api.js +3 -0
  324. package/lib/types/index.d.ts +12 -0
  325. package/lib/types/index.js +28 -0
  326. package/lib/types/points.d.ts +28 -0
  327. package/lib/types/points.js +3 -0
  328. package/lib/types/pools.d.ts +144 -0
  329. package/lib/types/pools.js +3 -0
  330. package/lib/types/staking.d.ts +28 -0
  331. package/lib/types/staking.js +3 -0
  332. package/lib/types/sub-accounts.d.ts +150 -0
  333. package/lib/types/sub-accounts.js +3 -0
  334. package/lib/types/subgraph.d.ts +84 -0
  335. package/lib/types/subgraph.js +3 -0
  336. package/lib/types/typed-contract.d.ts +102 -0
  337. package/lib/types/typed-contract.js +3 -0
  338. package/lib/types/user.d.ts +1 -0
  339. package/lib/types/user.js +3 -0
  340. package/lib/types/vaults.d.ts +886 -0
  341. package/lib/types/vaults.js +30 -0
  342. package/lib/types/web3.d.ts +81 -0
  343. package/lib/types/web3.js +14 -0
  344. package/lib/types/webserver.d.ts +730 -0
  345. package/lib/types/webserver.js +3 -0
  346. package/package.json +78 -3
@@ -0,0 +1,1034 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getManagementFeePercent = exports.getSymbol = exports.getReceiptTokenAddress = exports.getReceiptTokenAddressOrThrow = exports.getWhitelistedAssets = exports.getDecimalsOrThrow = exports.getDecimals = exports.explorerLink = exports.getInfuraProvider = exports.createProvider = exports.getChainId = exports.determineRpcBatchMaxCount = exports.determineBlockSkipInternal = exports.determineSecondsPerBlock = exports.determineBlockCutoff = void 0;
4
+ exports.createContract = createContract;
5
+ exports.getTokenMetadata = getTokenMetadata;
6
+ exports.simulateTransaction = simulateTransaction;
7
+ exports.checkAddress = checkAddress;
8
+ exports.getLoanOracleFeeRate = getLoanOracleFeeRate;
9
+ exports.getHistoricalContractData = getHistoricalContractData;
10
+ exports.getHistoricalContractDataByDate = getHistoricalContractDataByDate;
11
+ const ethers_1 = require("ethers");
12
+ const errors_1 = require("../errors");
13
+ const cache_1 = require("../cache");
14
+ const logger_1 = require("../logger");
15
+ const web3_1 = require("../constants/web3");
16
+ const fetcher_1 = require("../fetcher");
17
+ const chain_address_1 = require("./chain-address");
18
+ const chain_error_1 = require("./chain-error");
19
+ const constants_1 = require("../../adapters/solana/constants");
20
+ const abis_1 = require("../../abis");
21
+ const TokenizedVaultV2_1 = require("../../abis/TokenizedVaultV2");
22
+ const TokenizedVaultV2WhitelistedAssets_1 = require("../../abis/TokenizedVaultV2WhitelistedAssets");
23
+ //import { getVaultVersion } from './helpers.vaults';
24
+ const vault_version_1 = require("./vault-version");
25
+ /**
26
+ * Web3 Helper Utilities
27
+ *
28
+ * Core utilities for blockchain interaction:
29
+ * - Provider Management: createProvider, getInfuraProvider, getChainId
30
+ * - Contract Helpers: createContract, getDecimals, getSymbol, getTokenMetadata
31
+ * - Transaction Utilities: simulateTransaction, checkAddress
32
+ * - Historical Queries: getHistoricalContractData, getHistoricalContractDataByDate
33
+ * - Oracle Integration: getLoanOracleFeeRate
34
+ * - Block Utilities: determineBlockCutoff, determineBlockSkipInternal
35
+ */
36
+ /**
37
+ * Get maximum block range for historical queries per chain.
38
+ * Prevents RPC timeouts by limiting lookback window.
39
+ * @param chain Chain ID
40
+ * @returns Maximum number of blocks to query
41
+ */
42
+ const determineBlockCutoff = (chain) => {
43
+ switch (chain) {
44
+ case 56:
45
+ return 120_000;
46
+ case 43114:
47
+ return 120_000;
48
+ case 143:
49
+ return 3_456_000;
50
+ default:
51
+ return 150_000;
52
+ }
53
+ };
54
+ exports.determineBlockCutoff = determineBlockCutoff;
55
+ /**
56
+ * Approximate block time in seconds per chain.
57
+ * Used to estimate time ranges from block counts.
58
+ * @param chain - Chain ID
59
+ * @returns Seconds per block
60
+ */
61
+ const determineSecondsPerBlock = (chain) => {
62
+ switch (chain) {
63
+ case 143: // Monad ~500ms
64
+ return 0.5;
65
+ case 56: // BSC ~2s
66
+ case 43114: // Avalanche ~2s
67
+ return 2;
68
+ default: // Ethereum ~12s
69
+ return 12;
70
+ }
71
+ };
72
+ exports.determineSecondsPerBlock = determineSecondsPerBlock;
73
+ /**
74
+ * Get block interval for paginated historical queries per chain.
75
+ * Respects RPC eth_getLogs range limits.
76
+ * @param chain Chain ID
77
+ * @returns Block interval for pagination
78
+ */
79
+ const determineBlockSkipInternal = (chain) => {
80
+ switch (chain) {
81
+ case 43114:
82
+ return 8_000;
83
+ case 56:
84
+ return 8_000;
85
+ case 143: // Monad — Alchemy limits eth_getLogs to 1000 blocks
86
+ return 1_000;
87
+ case 999: // HyperEVM — rpc.hypurrscan.io limits eth_getLogs to 1000 blocks
88
+ return 1_000;
89
+ case 4114: // Citrea — rpc.mainnet.citrea.xyz rejects `to - from >= 1000`
90
+ // ("query exceeds max block range 1000"). The redemption-history chunker
91
+ // queries `[end - blockSkip, end]`, so `to - from === blockSkip`; 999 is
92
+ // the largest value the RPC accepts.
93
+ return 999;
94
+ default:
95
+ // 10k blocks is the eth_getLogs range cap enforced by every mainstream
96
+ // provider (Alchemy, Infura, dRPC, QuickNode). The previous 50k default
97
+ // was rejected outright with JSON-RPC -32600 ("You can make eth_getLogs
98
+ // requests with up to a 10000 block range") on Ethereum mainnet, which
99
+ // failed `getVaultRedemptionHistory` for every vault on an unlisted
100
+ // chain. RPC-count impact: a 150k-block cutoff now costs 15 getLogs
101
+ // calls instead of 3, batched 20-concurrent, so still one round trip.
102
+ return 10_000;
103
+ }
104
+ };
105
+ exports.determineBlockSkipInternal = determineBlockSkipInternal;
106
+ /**
107
+ * Default batch cap. Chosen to stay under the smallest limit among the
108
+ * providers we route to by default (e.g. rpc.hypurrscan.io rejects > 20).
109
+ */
110
+ const DEFAULT_RPC_BATCH_MAX_COUNT = 10;
111
+ /**
112
+ * Maximum JSON-RPC calls ethers may coalesce into a single batched HTTP
113
+ * request for a given endpoint.
114
+ *
115
+ * Providers advertise wildly different batch limits and reject the whole batch
116
+ * — not the excess — when exceeded, so one oversized batch fails every call
117
+ * inside it. dRPC's free tier caps batches at 3, which made every Mezo read
118
+ * fail with `server response 500 … "Batch of more than 3 requests are not
119
+ * allowed"` (the single highest-volume SDK error in production).
120
+ *
121
+ * Detection is host-based rather than chain-based because the limit is a
122
+ * property of the endpoint, not the chain: the same chain served by a
123
+ * self-hosted node has no such cap.
124
+ *
125
+ * @param rpcUrl - The RPC endpoint URL. Unparseable values fall back to the
126
+ * conservative default rather than throwing.
127
+ * @returns Batch size cap to pass to ethers as `batchMaxCount`.
128
+ */
129
+ const determineRpcBatchMaxCount = (rpcUrl) => {
130
+ let host = '';
131
+ try {
132
+ host = new URL(rpcUrl).hostname.toLowerCase();
133
+ }
134
+ catch {
135
+ return DEFAULT_RPC_BATCH_MAX_COUNT;
136
+ }
137
+ // dRPC free tier: "Batch of more than 3 requests are not allowed".
138
+ if (host === 'drpc.org' || host.endsWith('.drpc.org'))
139
+ return 3;
140
+ return DEFAULT_RPC_BATCH_MAX_COUNT;
141
+ };
142
+ exports.determineRpcBatchMaxCount = determineRpcBatchMaxCount;
143
+ /**
144
+ * Retrieve chain ID from web3 provider.
145
+ * Handles both initialized and uninitialized provider states.
146
+ * @param provider Web3 provider instance
147
+ * @returns Numeric chain ID
148
+ */
149
+ const getChainId = async (provider) => {
150
+ try {
151
+ if (!provider?._network) {
152
+ return Number((await provider.getNetwork()).chainId);
153
+ }
154
+ else {
155
+ return Number(provider?._network.chainId);
156
+ }
157
+ }
158
+ catch (e) {
159
+ return Number((await provider.getNetwork()).chainId);
160
+ }
161
+ };
162
+ exports.getChainId = getChainId;
163
+ /**
164
+ * Create ethers contract instance with address validation.
165
+ * @param provider Web3 provider for contract calls
166
+ * @param address Contract address (must be checksummed)
167
+ * @param abi Contract ABI (must be declared with `as const`)
168
+ * @returns Strongly-typed contract instance or undefined if address invalid
169
+ */
170
+ function createContract({ provider, address, abi, }) {
171
+ const goodAddress = checkAddress(address);
172
+ if (!goodAddress)
173
+ return;
174
+ const contract = new ethers_1.Contract(address, abi, provider);
175
+ if (!contract) {
176
+ logger_1.Logger.log.error('createContract', 'contract does not exist on connected chain');
177
+ return;
178
+ }
179
+ return contract;
180
+ }
181
+ /**
182
+ * Create or reuse a cached JSON-RPC provider.
183
+ * Passing `chainId` enables ethers' `staticNetwork` to skip the `eth_chainId`
184
+ * round-trip on first use.
185
+ *
186
+ * @param rpcUrl RPC endpoint URL — must be a non-empty string.
187
+ * @param chainId Optional chain ID — enables staticNetwork
188
+ * @throws Error with a clear, remediable message when `rpcUrl` is missing or
189
+ * empty. Without this guard, ethers v6 silently defaults to
190
+ * `http://localhost:8545` and surfaces a cryptic
191
+ * `network is not available yet (NETWORK_ERROR)` on the first RPC call.
192
+ */
193
+ const createProvider = (rpcUrl, chainId) => {
194
+ if (typeof rpcUrl !== 'string' || rpcUrl.trim().length === 0) {
195
+ const forChain = chainId ? ` for chain ${chainId}` : '';
196
+ throw new Error(`createProvider: no RPC URL provided${forChain}. ` +
197
+ 'Pass an RPC URL when constructing UpshiftSDK ' +
198
+ '(`new UpshiftSDK({ providers: { [chainId]: url } })`), ' +
199
+ `or set the UPSHIFT_RPC_${chainId ?? '<chainId>'} environment variable.`);
200
+ }
201
+ const cacheKey = chainId ? `${rpcUrl}|${chainId}` : rpcUrl;
202
+ if (cache_1.CACHE.has(cacheKey))
203
+ return cache_1.CACHE.get(cacheKey);
204
+ // batchMaxCount respects server-side batch limits — providers reject the
205
+ // entire batch when it is exceeded (see determineRpcBatchMaxCount).
206
+ const batchMaxCount = (0, exports.determineRpcBatchMaxCount)(rpcUrl);
207
+ const provider = chainId
208
+ ? new ethers_1.JsonRpcProvider(rpcUrl, ethers_1.Network.from(chainId), {
209
+ staticNetwork: ethers_1.Network.from(chainId),
210
+ batchMaxCount,
211
+ })
212
+ : new ethers_1.JsonRpcProvider(rpcUrl, undefined, {
213
+ batchMaxCount,
214
+ });
215
+ cache_1.CACHE.set(cacheKey, provider);
216
+ return provider;
217
+ };
218
+ exports.createProvider = createProvider;
219
+ /**
220
+ * Create or reuse a cached Infura provider for the specified chain.
221
+ * @param infura Configuration with chain ID and API key
222
+ */
223
+ const getInfuraProvider = (infura) => {
224
+ let baseUrl = 'https://';
225
+ switch (infura.chainId) {
226
+ case 8453: {
227
+ baseUrl = baseUrl + 'base-mainnet';
228
+ break;
229
+ }
230
+ case 43114: {
231
+ baseUrl = baseUrl + 'avalanche-mainnet';
232
+ break;
233
+ }
234
+ case 137: {
235
+ baseUrl = baseUrl + 'polygon-mainnet';
236
+ break;
237
+ }
238
+ case 42161: {
239
+ baseUrl = baseUrl + 'arbitrum-mainnet';
240
+ break;
241
+ }
242
+ default: {
243
+ baseUrl = baseUrl + 'mainnet';
244
+ break;
245
+ }
246
+ }
247
+ const url = `${baseUrl}.infura.io/v3/${infura.apiKey}`;
248
+ return (0, exports.createProvider)(url, infura.chainId);
249
+ };
250
+ exports.getInfuraProvider = getInfuraProvider;
251
+ var explorer_link_1 = require("./explorer-link");
252
+ Object.defineProperty(exports, "explorerLink", { enumerable: true, get: function () { return explorer_link_1.explorerLink; } });
253
+ /**
254
+ * Stable per-provider identity for cache keys, or `null` when the chain cannot
255
+ * be determined at all — the distinction callers need to decide whether a cache
256
+ * key is trustworthy.
257
+ *
258
+ * `provider._network` is a *getter* in ethers v6 that throws
259
+ * `network is not available yet (NETWORK_ERROR)` until the provider has
260
+ * resolved its network (i.e. before its first successful request, when
261
+ * `staticNetwork` was not supplied at construction). Accessing it directly
262
+ * here used to leak that error out of every cached read (`getDecimals`,
263
+ * `getReceiptTokenAddress`, …) and surface as a confusing TVL failure on
264
+ * fresh provider instances. We swallow the throw and fall back to the
265
+ * connection URL — slightly less precise as a cache key but always safe.
266
+ *
267
+ * The runner is unwrapped to its provider first. An `IContractRunner` is either
268
+ * a `Provider` or a `Signer`, and a `Signer` carries the network on
269
+ * `signer.provider`, not on itself — without the unwrap every signer-backed
270
+ * read scoped to `'unknown'` and could never share a cache entry with the
271
+ * provider-backed read of the same token. The unwrap is a **no-op for
272
+ * providers**: ethers' `AbstractProvider` defines `get provider() { return
273
+ * this; }`, so read callers resolve to exactly the scope they did before.
274
+ *
275
+ * @returns `chain:<id>`, `url:<endpoint>`, or `null` when neither is available.
276
+ */
277
+ function resolveProviderScope(runner) {
278
+ const provider = runner?.provider ?? runner;
279
+ let chainId;
280
+ try {
281
+ const net = provider
282
+ ._network;
283
+ chainId = net?.chainId;
284
+ }
285
+ catch {
286
+ // ethers v6 throws when the lazy network hasn't been detected yet.
287
+ chainId = undefined;
288
+ }
289
+ if (chainId !== undefined && chainId !== null) {
290
+ return `chain:${String(chainId)}`;
291
+ }
292
+ const conn = provider._getConnection?.();
293
+ if (conn?.url)
294
+ return `url:${conn.url}`;
295
+ return null;
296
+ }
297
+ /**
298
+ * Scope string for cache keys, collapsing an unresolvable runner to the
299
+ * literal `'unknown'` bucket.
300
+ *
301
+ * This is the long-standing behaviour of every cached read in this module and
302
+ * is deliberately preserved: read callers keep keying exactly as they always
303
+ * have, `decimals-unknown-…` entries included. Only {@link getDecimalsOrThrow}
304
+ * treats an unresolvable scope specially — see the note there on why the write
305
+ * path holds itself to a higher bar than the read path.
306
+ */
307
+ function providerScope(runner) {
308
+ return resolveProviderScope(runner) ?? 'unknown';
309
+ }
310
+ /**
311
+ * Cache key for a token's `decimals()`, shared by **every** decimals reader in
312
+ * the SDK — the lenient {@link getDecimals} used by read paths and the strict
313
+ * {@link getDecimalsOrThrow} used by write paths. One namespace is the point:
314
+ * a page that reads a vault and then deposits into it must not pay the RPC
315
+ * twice (CLAUDE.md §4.1, "reuse results across functions").
316
+ *
317
+ * **What the entry holds is the *effective share-scale* decimals for `address`,
318
+ * not necessarily `address`'s own `decimals()`.** {@link fetchDecimals} with
319
+ * `isVault` redirects an `evm-2` vault to its receipt token, and the result is
320
+ * still stored under the vault's key — that redirect is the whole point of the
321
+ * flag, and every reader of a vault address wants the receipt token's scale.
322
+ * Consequence for future callers: `getDecimalsOrThrow(runner, evm2VaultAddr)`
323
+ * reads directly (`isVault: false`) but can be served the receipt token's value
324
+ * from this shared entry. Write paths therefore pass the receipt-token address
325
+ * explicitly for `evm-2` vaults; do not add a caller that expects an `evm-2`
326
+ * vault's own `decimals()` back out of this cache.
327
+ *
328
+ * @param scope - Result of {@link providerScope} / {@link resolveProviderScope}.
329
+ * @param address - Token address, or a vault address whose entry holds the
330
+ * share-scale token's decimals (see above).
331
+ */
332
+ function decimalsCacheKey(scope, address) {
333
+ return `decimals-${scope}-${address}`;
334
+ }
335
+ /**
336
+ * In-flight `decimals()` reads, keyed exactly as the cache. Collapses a
337
+ * stampede of concurrent callers for the same uncached token into one RPC.
338
+ *
339
+ * The stored promise **may reject** — strict callers need the original error.
340
+ * Lenient callers therefore must not return it raw; {@link getDecimals} awaits
341
+ * it inside a `try`.
342
+ * @internal
343
+ */
344
+ const DECIMALS_REQUESTS = new Map();
345
+ /**
346
+ * Run (or join) the single in-flight `decimals()` read for `key`, caching the
347
+ * result on success. Failures are never cached, so a transient outage does not
348
+ * poison the entry.
349
+ *
350
+ * @param key - Cache key from {@link decimalsCacheKey}.
351
+ * @param fetch - Performs the actual read. Only invoked on a miss with nothing
352
+ * already in flight. Whichever caller registers the in-flight promise first
353
+ * supplies this closure — a concurrent {@link getDecimalsOrThrow} joining a
354
+ * {@link getDecimals}-initiated request inherits the lenient, no-retry read
355
+ * instead of its own `retryOnTransientRpc` wrap. Not a correctness bug (the
356
+ * strict caller still throws the original error), just a narrow window
357
+ * where the strict path's transient-retry guarantee is momentarily lost.
358
+ * @returns The decimals value.
359
+ * @throws Whatever `fetch` throws — callers that must not throw wrap this.
360
+ */
361
+ function sharedDecimalsRequest(key, fetch) {
362
+ const inflight = DECIMALS_REQUESTS.get(key);
363
+ if (inflight)
364
+ return inflight;
365
+ const request = (async () => {
366
+ const decimals = await fetch();
367
+ cache_1.CACHE.set(key, decimals);
368
+ return decimals;
369
+ })().finally(() => {
370
+ DECIMALS_REQUESTS.delete(key);
371
+ });
372
+ DECIMALS_REQUESTS.set(key, request);
373
+ return request;
374
+ }
375
+ /**
376
+ * Read `decimals()` off an ERC-20 at `address`, or — when `isVault` and the
377
+ * vault is `evm-2` — off its receipt token, which is where the share scale
378
+ * actually lives.
379
+ *
380
+ * @param runner - Provider or signer to read through.
381
+ * @param address - Token or vault address.
382
+ * @param isVault - Resolve `address` as a vault (may cost a backend metadata
383
+ * fetch plus a `lpTokenAddress()` read) rather than reading it directly.
384
+ * @returns The decimals value.
385
+ * @throws Whatever the underlying read throws.
386
+ */
387
+ async function fetchDecimals(runner, address, isVault) {
388
+ let realAddress = address;
389
+ if (isVault) {
390
+ const tokenizedVault = (await (0, fetcher_1.fetchTokenizedVault)(address, undefined, false, false))?.[0];
391
+ const version = (0, vault_version_1.getVaultVersionV2)(tokenizedVault);
392
+ if (version === 'evm-2') {
393
+ realAddress = await (0, exports.getReceiptTokenAddress)(runner, address);
394
+ }
395
+ }
396
+ const contract = new ethers_1.Contract(realAddress, [web3_1.MIN_ABIS.decimals], runner);
397
+ return Number(await contract.decimals());
398
+ }
399
+ /**
400
+ * Fetch token decimals from contract or Solana mint.
401
+ * Results are cached to minimize RPC calls.
402
+ *
403
+ * **Never throws** — a failed read resolves `undefined`. Callers that must not
404
+ * silently proceed on an unknown scale (any path that encodes an amount) should
405
+ * use {@link getDecimalsOrThrow} instead: feeding `undefined` into
406
+ * `toNormalizedBn` silently defaults to 18 decimals.
407
+ *
408
+ * Transient transport faults (provider rate limits, socket resets, an empty
409
+ * response to `decimals()`) are logged at `warn` — a breadcrumb, not a
410
+ * standalone Sentry issue — since the caller sees the same `undefined` either
411
+ * way. Everything else is logged at `error`. The read itself is **not**
412
+ * retried here; that is opt-in via {@link getDecimalsOrThrow}.
413
+ *
414
+ * @param provider Web3 provider
415
+ * @param address Token contract address or Solana mint
416
+ * @param isVault Resolve `address` as a vault (evm-2 vaults redirect to the
417
+ * receipt token) rather than reading it as a plain ERC-20. Defaults to `true`.
418
+ * @returns Number of decimals for the token, or `undefined` when the read failed.
419
+ */
420
+ const getDecimals = async (provider, address, isVault = true) => {
421
+ if (address === ethers_1.ZeroAddress) {
422
+ logger_1.Logger.log.info('getDecimals', 'address is zero address');
423
+ return 0;
424
+ }
425
+ if ((0, chain_address_1.isSolanaAddress)(address))
426
+ return constants_1.fallbackDecimals;
427
+ if (!(address && provider))
428
+ return;
429
+ const key = decimalsCacheKey(providerScope(provider), address);
430
+ if (cache_1.CACHE.has(key))
431
+ return cache_1.CACHE.get(key);
432
+ try {
433
+ return await sharedDecimalsRequest(key, () => fetchDecimals(provider, address, isVault));
434
+ }
435
+ catch (e) {
436
+ // Severity split. A provider rate-limit or socket fault is not a defect in
437
+ // the SDK or the caller's input — the read resolves `undefined` either way,
438
+ // exactly as documented, and logging it at error level made a QuickNode
439
+ // `50/second request limit reached` burst the fourth highest-volume issue
440
+ // in Sentry with nothing actionable in it. Genuine failures (a non-ERC-20
441
+ // at this address, a real revert) stay at error.
442
+ //
443
+ // Note this only changes the log level. The no-retry contract of this
444
+ // lenient reader is deliberate and unchanged — callers that must not
445
+ // proceed on an unknown scale use `getDecimalsOrThrow`, which retries.
446
+ const isTransient = (0, chain_error_1.isRetryableRpcError)(e) || (0, chain_error_1.isEmptyViewResponse)(e, DECIMALS_SELECTOR);
447
+ if (isTransient) {
448
+ logger_1.Logger.log.warn('getDecimals', {
449
+ address,
450
+ reason: 'transient RPC failure',
451
+ message: e instanceof Error ? e.message : String(e),
452
+ });
453
+ }
454
+ else {
455
+ logger_1.Logger.log.error('getDecimals', `${address}::${e}`);
456
+ }
457
+ return undefined;
458
+ }
459
+ };
460
+ exports.getDecimals = getDecimals;
461
+ /**
462
+ * `decimals()` — `keccak256("decimals()")[0..4]`. Scopes the empty-view-response
463
+ * retry to exactly the call this reader makes.
464
+ */
465
+ const DECIMALS_SELECTOR = '0x313ce567';
466
+ /**
467
+ * Read a token's `decimals()` with the **same cache and stampede protection as
468
+ * {@link getDecimals}**, but surfacing failures instead of swallowing them, and
469
+ * retrying the transient ones.
470
+ *
471
+ * Two reasons this exists rather than a flag on `getDecimals`:
472
+ *
473
+ * 1. **`getDecimals` must keep returning `undefined` on failure** — a dozen
474
+ * read paths depend on that. Amount-encoding paths need the opposite: an
475
+ * `undefined` reaching `toNormalizedBn` silently means 18 decimals, which
476
+ * misencodes the transaction. Here the original error propagates, so the
477
+ * caller's `UpshiftSDKError` keeps its cause and its Sentry grouping.
478
+ * 2. **Retry.** Providers intermittently return an empty response to
479
+ * `decimals()`, which ethers reports as
480
+ * `missing revert data (action="call", data="0x313ce567", …)` — a shape a
481
+ * deployed ERC-20 cannot legitimately produce, since `decimals()` takes no
482
+ * arguments. That is retried here via {@link isEmptyViewResponse} scoped to
483
+ * this exact selector, alongside ordinary transport faults
484
+ * ({@link isRetryableRpcError}). The empty-view widening applies **only**
485
+ * here; everywhere else the strict transport definition stands. A genuine
486
+ * revert (`CALL_EXCEPTION` carrying revert data) is never retried.
487
+ *
488
+ * Cache is shared with `getDecimals`, so a read-then-write flow against the
489
+ * same token costs one `decimals()` RPC in total, and N concurrent callers for
490
+ * an uncached token collapse to one.
491
+ *
492
+ * **Exception — an unresolvable chain scope bypasses the cache entirely.** When
493
+ * {@link resolveProviderScope} cannot determine the chain (a runner with no
494
+ * resolved network and no connection URL, e.g. a browser provider before its
495
+ * first request), the only available key is the shared `unknown` bucket. Read
496
+ * paths happily use that bucket, and this function deliberately does not: the
497
+ * two paths have different blast radii. A wrong cached `decimals` on a read
498
+ * renders a wrong number on screen; on a write it misencodes an amount the user
499
+ * then *signs*. The collision needed — the same address being a different token
500
+ * with different decimals on two chains, within one process, across a
501
+ * mid-session chain switch — is narrow, but "no worse than the read path" is
502
+ * not the bar for a value that ends up in a transaction. In that case the read
503
+ * is neither served from nor written to the cache, and it also skips the
504
+ * in-flight dedup map, since joining another caller under an untrusted key
505
+ * would reintroduce exactly the collision being avoided. Retries still apply;
506
+ * the only thing forgone is the RPC saving.
507
+ *
508
+ * RPC cost: 1 on a cache miss, 0 on a hit, up to 3 on a miss that keeps
509
+ * faulting (~750ms of added latency in the worst case before it gives up).
510
+ * Always ≥1 when the chain scope is unresolvable.
511
+ *
512
+ * @param runner - Provider or signer to read through. A signer is unwrapped to
513
+ * its provider for cache scoping, so it shares entries with reads on the
514
+ * same chain.
515
+ * @param address - Token address. Read directly as an ERC-20 — pass the
516
+ * receipt-token address yourself for an `evm-2` vault's share scale.
517
+ * @param tag - Low-cardinality label for retry breadcrumbs (e.g.
518
+ * `'vaultDeposit:poolDecimals'`).
519
+ * @returns The token's decimals.
520
+ * @throws The underlying read error once retries are exhausted, or immediately
521
+ * when the failure is neither a transport fault nor an empty view response.
522
+ *
523
+ * @example
524
+ * ```ts
525
+ * // Amount encoding must not proceed on a guessed scale.
526
+ * const decimals = await getDecimalsOrThrow(signer, token, 'deposit:decimals');
527
+ * const amount = toNormalizedBn(userInput, decimals);
528
+ * ```
529
+ */
530
+ const getDecimalsOrThrow = async (runner, address, tag) => {
531
+ const read = () => (0, chain_error_1.retryOnTransientRpc)(tag, () => fetchDecimals(runner, address, false), { address }, (error) => (0, chain_error_1.isRetryableRpcError)(error) ||
532
+ (0, chain_error_1.isEmptyViewResponse)(error, DECIMALS_SELECTOR));
533
+ const scope = resolveProviderScope(runner);
534
+ if (scope === null) {
535
+ // Untrusted key: no cache read, no cache write, no dedup. See the note
536
+ // above — a mis-scoped decimals value here gets signed into a transaction.
537
+ logger_1.Logger.log.warn(tag, 'decimals cache bypassed: chain scope unresolved', {
538
+ address,
539
+ });
540
+ return read();
541
+ }
542
+ const key = decimalsCacheKey(scope, address);
543
+ if (cache_1.CACHE.has(key))
544
+ return cache_1.CACHE.get(key);
545
+ return sharedDecimalsRequest(key, read);
546
+ };
547
+ exports.getDecimalsOrThrow = getDecimalsOrThrow;
548
+ /** @internal */
549
+ const WHITELISTED_ASSETS_REQUESTS = new Map();
550
+ /**
551
+ * Fetch the whitelisted deposit-asset addresses from a vault's whitelist
552
+ * contract. Cached with a 5-minute TTL; a fresh fetch fans out into N
553
+ * decimals/symbol reads downstream.
554
+ * @internal
555
+ */
556
+ const getWhitelistedAssets = async (provider, whitelistAddress) => {
557
+ if (!(whitelistAddress && provider)) {
558
+ throw new errors_1.UpshiftValidationError('INVALID_INPUT', 'getWhitelistedAssets: provider and whitelistAddress are required');
559
+ }
560
+ const key = `whitelisted-assets-${providerScope(provider)}-${whitelistAddress}`;
561
+ if (cache_1.CACHE.has(key))
562
+ return cache_1.CACHE.get(key);
563
+ const inflight = WHITELISTED_ASSETS_REQUESTS.get(key);
564
+ if (inflight)
565
+ return inflight;
566
+ const fetchPromise = (async () => {
567
+ try {
568
+ const contract = new ethers_1.Contract(whitelistAddress, TokenizedVaultV2WhitelistedAssets_1.ABI_TOKENIZED_VAULT_V2_WHITELISTED_ASSETS, provider);
569
+ const list = (await contract.getWhitelistedAssets());
570
+ cache_1.CACHE.set(key, list, { ttl: 5 * 60 * 1000 });
571
+ return list;
572
+ }
573
+ finally {
574
+ WHITELISTED_ASSETS_REQUESTS.delete(key);
575
+ }
576
+ })();
577
+ WHITELISTED_ASSETS_REQUESTS.set(key, fetchPromise);
578
+ return fetchPromise;
579
+ };
580
+ exports.getWhitelistedAssets = getWhitelistedAssets;
581
+ /**
582
+ * Read an `evm-2` vault's receipt (LP) token address via `lpTokenAddress()`,
583
+ * retrying only the transient transport faults and surfacing everything else
584
+ * unchanged.
585
+ *
586
+ * Why this exists: `lpTokenAddress()` sits immediately before the receipt-token
587
+ * `decimals()` read on every `evm-2` write path (approve / deposit / request
588
+ * redeem / swap-router deposit). Those `decimals()` reads were hardened against
589
+ * provider blips ({@link getDecimalsOrThrow}); the `lpTokenAddress()` read one
590
+ * line above them was not, so a single truncated `eth_call` response still
591
+ * failed the whole write with
592
+ * `missing revert data (action="call", data="0xf5ae497a", …)` — observed in
593
+ * production against a mainnet vault whose `lpTokenAddress()` demonstrably
594
+ * returns a real address when the provider is healthy.
595
+ *
596
+ * **This must never hide a misrouted vault, and does not.** `lpTokenAddress()`
597
+ * exists *only* on `evm-2` vaults. A vault wrongly classified as `evm-2` returns
598
+ * empty returndata for it, deterministically and forever, and that empty
599
+ * response is byte-identical to the transient one — it is the *only* signal the
600
+ * SDK gets that its version routing was wrong. So the retry here is deliberately
601
+ * shaped to preserve that signal:
602
+ *
603
+ * - it is **bounded** (3 attempts, ~750ms of backoff in total — see
604
+ * `retryOnTransientRpc`), never open-ended;
605
+ * - on exhaustion it **rethrows the original error object**, with its identity,
606
+ * message, `code` and `transaction.data` intact, so the caller's
607
+ * `UpshiftSDKError` cause and Sentry grouping are exactly what they are today;
608
+ * - it has **no fallback**: it never substitutes another address, never resolves
609
+ * the vault address itself, and never resolves `null`/`undefined`. It either
610
+ * returns a real receipt-token address or throws.
611
+ *
612
+ * Net effect: a blip costs a retry, a misroute costs three `eth_call`s and then
613
+ * fails exactly as loudly as before.
614
+ *
615
+ * **Deliberately not cached.** Unlike `decimals()`, the vault→receipt-token
616
+ * mapping is not memoized here. Caching it would make a misroute's first
617
+ * (failed) probe and every subsequent one diverge, and would put a
618
+ * vault-identity mapping in the cache on the money path. The lenient, cached
619
+ * reader is {@link getReceiptTokenAddress} — use that on read paths that can
620
+ * tolerate `undefined`.
621
+ *
622
+ * RPC cost: exactly 1 `eth_call` on success; at most 3 when the provider keeps
623
+ * faulting.
624
+ *
625
+ * @param runner - Provider or signer to read through.
626
+ * @param vault - Address of the `evm-2` tokenized vault.
627
+ * @param tag - Low-cardinality label for retry breadcrumbs (e.g.
628
+ * `'vaultRequestRedeem:receiptToken'`).
629
+ * @returns The vault's receipt (LP) token address. Never `null`/`undefined`.
630
+ * @throws The underlying read error, unmodified, once the bounded retries are
631
+ * exhausted — or immediately when the failure is neither a transport fault nor
632
+ * an empty response to this exact selector (e.g. a genuine revert carrying
633
+ * revert data).
634
+ *
635
+ * @example
636
+ * ```ts
637
+ * const receiptToken = await getReceiptTokenAddressOrThrow(
638
+ * signer,
639
+ * vault,
640
+ * 'vaultRequestRedeem:receiptToken',
641
+ * );
642
+ * const decimals = await getDecimalsOrThrow(signer, receiptToken, 'tag');
643
+ * ```
644
+ */
645
+ const getReceiptTokenAddressOrThrow = async (runner, vault, tag) => {
646
+ const contract = new ethers_1.Contract(vault, TokenizedVaultV2_1.ABI_TOKENIZED_VAULT_V2, runner);
647
+ return (0, chain_error_1.retryOnTransientRpc)(tag, () => contract.lpTokenAddress(), { vault }, (error) => (0, chain_error_1.isRetryableRpcError)(error) ||
648
+ (0, chain_error_1.isEmptyViewResponse)(error, chain_error_1.LP_TOKEN_ADDRESS_SELECTOR));
649
+ };
650
+ exports.getReceiptTokenAddressOrThrow = getReceiptTokenAddressOrThrow;
651
+ /** @internal */
652
+ const RECEIPT_TOKEN_REQUESTS = new Map();
653
+ /**
654
+ * Fetch receipt token address from tokenized vault contract.
655
+ * Results are cached to minimize RPC calls.
656
+ *
657
+ * **Never throws** — a failed read logs at error level and resolves
658
+ * `undefined`, which several read paths depend on. The underlying
659
+ * `lpTokenAddress()` call is made through
660
+ * {@link getReceiptTokenAddressOrThrow}, so a transient provider blip is
661
+ * absorbed by a bounded retry before that happens; a deterministic failure (a
662
+ * vault that has no `lpTokenAddress()` because it is not `evm-2`) still resolves
663
+ * `undefined` after the attempts are spent, exactly as before. Callers on a
664
+ * money path must use {@link getReceiptTokenAddressOrThrow} directly instead —
665
+ * an `undefined` receipt-token address there would silently mis-address a
666
+ * `decimals()` read.
667
+ *
668
+ * @param provider Web3 provider
669
+ * @param address Tokenized vault contract address
670
+ * @returns Receipt token address, or `undefined` when the read failed.
671
+ */
672
+ const getReceiptTokenAddress = async (provider, address) => {
673
+ if (!(address && provider))
674
+ return;
675
+ const key = `receipt-token-${providerScope(provider)}-${address}`;
676
+ if (cache_1.CACHE.has(key))
677
+ return cache_1.CACHE.get(key);
678
+ const inflight = RECEIPT_TOKEN_REQUESTS.get(key);
679
+ if (inflight)
680
+ return inflight;
681
+ const fetchPromise = (async () => {
682
+ try {
683
+ const receiptToken = await (0, exports.getReceiptTokenAddressOrThrow)(provider, address, 'getReceiptTokenAddress');
684
+ cache_1.CACHE.set(key, receiptToken);
685
+ return receiptToken;
686
+ }
687
+ catch (e) {
688
+ logger_1.Logger.log.error('getReceiptTokenAddress', e);
689
+ return undefined;
690
+ }
691
+ finally {
692
+ RECEIPT_TOKEN_REQUESTS.delete(key);
693
+ }
694
+ })();
695
+ RECEIPT_TOKEN_REQUESTS.set(key, fetchPromise);
696
+ return fetchPromise;
697
+ };
698
+ exports.getReceiptTokenAddress = getReceiptTokenAddress;
699
+ /** @internal */
700
+ const SYMBOL_REQUESTS = new Map();
701
+ /**
702
+ * Fetch token symbol from contract.
703
+ * Results are cached to minimize RPC calls.
704
+ * @param provider Web3 provider
705
+ * @param address Token contract address
706
+ * @returns Token symbol string
707
+ */
708
+ const getSymbol = async (provider, address, isVault = true) => {
709
+ if (address === ethers_1.ZeroAddress) {
710
+ logger_1.Logger.log.info('getSymbol', 'address is zero address');
711
+ return 'N/A';
712
+ }
713
+ if (!(address && provider))
714
+ return;
715
+ const key = `symbol-${providerScope(provider)}-${address}`;
716
+ if (cache_1.CACHE.has(key))
717
+ return cache_1.CACHE.get(key);
718
+ const inflight = SYMBOL_REQUESTS.get(key);
719
+ if (inflight)
720
+ return inflight;
721
+ const fetchPromise = (async () => {
722
+ try {
723
+ if (isVault) {
724
+ const tokenizedVault = (await (0, fetcher_1.fetchTokenizedVault)(address, undefined, false, false))?.[0];
725
+ const version = (0, vault_version_1.getVaultVersionV2)(tokenizedVault);
726
+ let realAddress = address;
727
+ if (version === 'evm-2') {
728
+ realAddress = await (0, exports.getReceiptTokenAddress)(provider, address);
729
+ }
730
+ const contract = new ethers_1.Contract(realAddress, [web3_1.MIN_ABIS.symbol], provider);
731
+ const symbol = (await contract.symbol());
732
+ cache_1.CACHE.set(key, symbol);
733
+ return symbol;
734
+ }
735
+ else {
736
+ const contract = new ethers_1.Contract(address, [web3_1.MIN_ABIS.symbol], provider);
737
+ const symbol = (await contract.symbol());
738
+ cache_1.CACHE.set(key, symbol);
739
+ return symbol;
740
+ }
741
+ }
742
+ catch (e) {
743
+ // Recovered failure, not an incident: the caller gets `undefined` and
744
+ // renders without a symbol. Logging it at `error` made every transient
745
+ // RPC hiccup ("socket hang up", connection reset) a standalone Sentry
746
+ // issue — ~24k events across one grouping, none actionable — and the
747
+ // interpolated `${address}::${e}` message put the token address in the
748
+ // issue title, so each address produced its own group.
749
+ //
750
+ // As a warning it becomes a breadcrumb on whatever the caller ultimately
751
+ // reports, with the address and reason as structured fields.
752
+ logger_1.Logger.log.warn('getSymbol', {
753
+ address,
754
+ isVault,
755
+ reason: e instanceof Error ? e.message : String(e),
756
+ });
757
+ return undefined;
758
+ }
759
+ finally {
760
+ SYMBOL_REQUESTS.delete(key);
761
+ }
762
+ })();
763
+ SYMBOL_REQUESTS.set(key, fetchPromise);
764
+ return fetchPromise;
765
+ };
766
+ exports.getSymbol = getSymbol;
767
+ /**
768
+ * Batch fetch multiple token metadata fields in a single call.
769
+ * Efficiently retrieves name, symbol, decimals, and totalSupply.
770
+ * @param provider Web3 provider
771
+ * @param asset Token contract address
772
+ * @param meta Array of metadata fields to fetch
773
+ * @returns Array of metadata values in same order as requested
774
+ */
775
+ async function getTokenMetadata(provider, asset, meta) {
776
+ if (!asset)
777
+ return [];
778
+ function buildAbi() {
779
+ const abiArr = [];
780
+ if (meta?.includes('name'))
781
+ abiArr.push(web3_1.MIN_ABIS.name);
782
+ if (meta?.includes('symbol'))
783
+ abiArr.push(web3_1.MIN_ABIS.symbol);
784
+ if (meta?.includes('decimals'))
785
+ abiArr.push(web3_1.MIN_ABIS.decimals);
786
+ if (meta?.includes('totalSupply'))
787
+ abiArr.push(web3_1.MIN_ABIS.totalSupply);
788
+ return abiArr;
789
+ }
790
+ try {
791
+ const contract = new ethers_1.Contract(asset, buildAbi(), provider);
792
+ const getterArr = [];
793
+ if (meta?.includes('name'))
794
+ getterArr.push(contract.name());
795
+ if (meta?.includes('symbol'))
796
+ getterArr.push(contract.symbol());
797
+ if (meta?.includes('decimals'))
798
+ getterArr.push(contract.decimals());
799
+ if (meta?.includes('totalSupply'))
800
+ getterArr.push(contract.totalSupply());
801
+ return await Promise.all(getterArr);
802
+ }
803
+ catch (e) {
804
+ logger_1.Logger.log.error('getTokenMetadata', e);
805
+ return [];
806
+ }
807
+ }
808
+ /**
809
+ * Fetch management fee percentage from vault contract.
810
+ *
811
+ * A single `eth_call` to `managementFeePercent()`. Vaults that do not expose
812
+ * the method (older lending pools) revert, which is absorbed and reported as
813
+ * `undefined` — the previous implementation issued an identical `staticCall`
814
+ * probe first, doubling the RPC cost for no extra information since a view
815
+ * call that reverts fails the same way either time.
816
+ *
817
+ * @param provider Web3 provider
818
+ * @param address Vault contract address
819
+ * @returns Management fee as a percentage (basis points ÷ 100), or `undefined`
820
+ * when the vault does not support the method or the call fails.
821
+ * @worstCaseRpcCalls 1 — one `eth_call`.
822
+ */
823
+ const getManagementFeePercent = async (provider, address) => {
824
+ if (!(address && provider))
825
+ return;
826
+ try {
827
+ const contract = new ethers_1.Contract(address, [web3_1.MIN_ABIS.managementFeePercent], provider);
828
+ const fee = (await contract.managementFeePercent());
829
+ return Number(fee) / 100;
830
+ }
831
+ catch (e) {
832
+ logger_1.Logger.log.error('getManagementFeePercent', e);
833
+ return undefined;
834
+ }
835
+ };
836
+ exports.getManagementFeePercent = getManagementFeePercent;
837
+ /**
838
+ * Simulate contract transaction without executing on-chain.
839
+ * Useful for testing transaction viability before sending.
840
+ * @param provider Web3 provider
841
+ * @param abi Contract ABI
842
+ * @param functionName Function to simulate
843
+ * @param options Transaction parameters including args, from, to
844
+ * @returns Decoded function result or false if simulation fails
845
+ */
846
+ async function simulateTransaction(provider, abi, functionName, options) {
847
+ try {
848
+ const customInterface = new ethers_1.Interface(abi);
849
+ const callData = customInterface.encodeFunctionData(functionName, options?.args);
850
+ const response = await provider.call({
851
+ from: options?.from,
852
+ to: options?.to,
853
+ data: callData,
854
+ });
855
+ if (!response)
856
+ return false;
857
+ return customInterface.decodeFunctionResult(functionName, response);
858
+ }
859
+ catch (e) {
860
+ logger_1.Logger.log.error('simulateTransaction', e);
861
+ if (String(e).includes('reverted with reason string "denied"'))
862
+ return true;
863
+ return false;
864
+ }
865
+ }
866
+ /**
867
+ * Validate Ethereum address format and checksum.
868
+ * @param address Address string to validate
869
+ * @param logger Optional logger for error messages
870
+ * @param type Context type for logging
871
+ * @returns True if address is valid and checksummed
872
+ */
873
+ function checkAddress(address, logger, type = 'contract') {
874
+ const _logger = logger ?? console;
875
+ if (!address) {
876
+ _logger.error(type, 'address is undefined');
877
+ return false;
878
+ }
879
+ if (typeof address !== 'string') {
880
+ _logger.error(type, 'address not of string type:', String(typeof address), String(address));
881
+ return false;
882
+ }
883
+ if (!(0, ethers_1.isAddress)(address)) {
884
+ _logger.error(type, 'address not checksummed:', String(address));
885
+ return false;
886
+ }
887
+ return true;
888
+ }
889
+ /**
890
+ * Fetch dynamic fee rate from on-chain oracle contract.
891
+ * Used for calculating loan interest repayment fees.
892
+ * @param provider Web3 provider
893
+ * @param category_id Fee category identifier
894
+ * @param address Context address for fee calculation
895
+ * @param chainId Optional chain ID (auto-detected if not provided)
896
+ * @returns Fee rate as decimal number
897
+ */
898
+ async function getLoanOracleFeeRate(provider, category_id, address, chainId) {
899
+ if (!(0, ethers_1.isAddress)(address)) {
900
+ logger_1.Logger.log.error('getLoanOracleFee', 'address is undefined or not a valid address');
901
+ return;
902
+ }
903
+ let _chainId = chainId;
904
+ if (!chainId) {
905
+ const chainIdRes = await (0, exports.getChainId)(provider);
906
+ _chainId = Number(chainIdRes);
907
+ }
908
+ if (!web3_1.ORACLE_CONTRACTS?.[_chainId]) {
909
+ logger_1.Logger.log.warn('getLoanOracleFeeRate', 'no oracle address for', _chainId);
910
+ return Number(0);
911
+ }
912
+ const oracleContract = createContract({
913
+ provider,
914
+ address: web3_1.ORACLE_CONTRACTS[_chainId],
915
+ abi: abis_1.ABI_FEE_ORACLE,
916
+ });
917
+ const hashedCategory = (0, ethers_1.keccak256)((0, ethers_1.toUtf8Bytes)(category_id));
918
+ const rawFee = await oracleContract.getContextFeeRate(hashedCategory, (0, ethers_1.getAddress)(address));
919
+ return Number((0, ethers_1.formatUnits)(rawFee, 6));
920
+ }
921
+ /**
922
+ * Query historical contract state across a block range.
923
+ * Useful for reconstructing historical data like TVL over time.
924
+ * @param provider Web3 provider
925
+ * @param contractAddress Target contract address
926
+ * @param abi Contract ABI
927
+ * @param methodName View function to call
928
+ * @param args Function arguments
929
+ * @param startBlock Starting block number
930
+ * @param endBlock Ending block number (current block if not specified)
931
+ * @param blockInterval Blocks between each query
932
+ * @returns Array of results with block numbers and timestamps
933
+ */
934
+ async function getHistoricalContractData(provider, contractAddress, abi, methodName, args = [], startBlock, endBlock, blockInterval = 1000) {
935
+ const contract = new ethers_1.ethers.Contract(contractAddress, abi, provider);
936
+ const results = [];
937
+ if (!endBlock) {
938
+ endBlock = await provider.getBlockNumber();
939
+ }
940
+ for (let blockNumber = startBlock; blockNumber <= endBlock; blockNumber += blockInterval) {
941
+ try {
942
+ const contractAtBlock = contract.connect(provider);
943
+ const result = await contractAtBlock[methodName](...args, {
944
+ blockTag: blockNumber,
945
+ });
946
+ const block = await provider.getBlock(blockNumber);
947
+ results.push({
948
+ blockNumber,
949
+ timestamp: block.timestamp,
950
+ result: result.toString(),
951
+ });
952
+ }
953
+ catch (error) {
954
+ logger_1.Logger.log.error('getHistoricalContractData', `Error at block ${blockNumber}`, error);
955
+ }
956
+ }
957
+ return results;
958
+ }
959
+ /**
960
+ * Query historical contract state at regular time intervals.
961
+ * Uses binary search to find blocks closest to target timestamps.
962
+ * @param provider Web3 provider
963
+ * @param contractAddress Target contract address
964
+ * @param abi Contract ABI
965
+ * @param methodName View function to call
966
+ * @param args Function arguments
967
+ * @param startDate Start date (string or timestamp)
968
+ * @param endDate End date (string or timestamp)
969
+ * @param intervalHours Hours between each query
970
+ * @returns Array of results with dates, blocks, and values
971
+ */
972
+ async function getHistoricalContractDataByDate(provider, contractAddress, abi, methodName, args = [], startDate, endDate, intervalHours = 24) {
973
+ const contract = new ethers_1.ethers.Contract(contractAddress, abi, provider);
974
+ const startTimestamp = Math.floor(new Date(startDate).getTime() / 1000);
975
+ const endTimestamp = Math.floor(new Date(endDate).getTime() / 1000);
976
+ const intervalSeconds = intervalHours * 3600;
977
+ const results = [];
978
+ // Binary search to find block closest to timestamp
979
+ async function findBlockByTimestamp(targetTimestamp) {
980
+ let left = 1;
981
+ let right = await provider.getBlockNumber();
982
+ let closestBlock = null;
983
+ while (left <= right) {
984
+ const mid = Math.floor((left + right) / 2);
985
+ const block = await provider.getBlock(mid);
986
+ if (!block) {
987
+ right = mid - 1;
988
+ continue;
989
+ }
990
+ if (block.timestamp === targetTimestamp) {
991
+ return block;
992
+ }
993
+ if (!closestBlock ||
994
+ Math.abs(block.timestamp - targetTimestamp) <
995
+ Math.abs(closestBlock.timestamp - targetTimestamp)) {
996
+ closestBlock = block;
997
+ }
998
+ if (block.timestamp < targetTimestamp) {
999
+ left = mid + 1;
1000
+ }
1001
+ else {
1002
+ right = mid - 1;
1003
+ }
1004
+ }
1005
+ return closestBlock;
1006
+ }
1007
+ try {
1008
+ // Loop through time intervals
1009
+ for (let timestamp = startTimestamp; timestamp <= endTimestamp; timestamp += intervalSeconds) {
1010
+ // Find closest block to timestamp
1011
+ const block = await findBlockByTimestamp(timestamp);
1012
+ if (!block) {
1013
+ logger_1.Logger.log.warn('getHistoricalContractDataByDate', `No block found for timestamp ${timestamp}`);
1014
+ continue;
1015
+ }
1016
+ // Make the contract call
1017
+ const result = await contract[methodName](...args, {
1018
+ blockTag: block.number,
1019
+ });
1020
+ results.push({
1021
+ date: new Date(timestamp * 1000).toISOString(),
1022
+ blockNumber: block.number,
1023
+ timestamp,
1024
+ result: result.toString(),
1025
+ });
1026
+ }
1027
+ return results;
1028
+ }
1029
+ catch (error) {
1030
+ logger_1.Logger.log.error('getHistoricalContractDataByDate', error);
1031
+ throw error;
1032
+ }
1033
+ }
1034
+ //# sourceMappingURL=web3.js.map