upshift-sdk 8.20.1

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 (316) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +349 -0
  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 +1043 -0
  40. package/lib/abis/SwapRouter.js +740 -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 +209 -0
  65. package/lib/adapters/evm/index.d.ts +353 -0
  66. package/lib/adapters/evm/index.js +434 -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/constants.d.ts +33 -0
  70. package/lib/adapters/solana/constants.js +52 -0
  71. package/lib/adapters/solana/getters.d.ts +11 -0
  72. package/lib/adapters/solana/getters.js +165 -0
  73. package/lib/adapters/solana/idl/vault-idl.d.ts +272 -0
  74. package/lib/adapters/solana/idl/vault-idl.js +1084 -0
  75. package/lib/adapters/solana/index.d.ts +233 -0
  76. package/lib/adapters/solana/index.js +291 -0
  77. package/lib/adapters/solana/types.d.ts +67 -0
  78. package/lib/adapters/solana/types.js +3 -0
  79. package/lib/adapters/solana/utils.d.ts +141 -0
  80. package/lib/adapters/solana/utils.js +595 -0
  81. package/lib/adapters/solana/vault.actions.d.ts +57 -0
  82. package/lib/adapters/solana/vault.actions.js +379 -0
  83. package/lib/adapters/stellar/actions.d.ts +28 -0
  84. package/lib/adapters/stellar/actions.js +77 -0
  85. package/lib/adapters/stellar/constants.d.ts +43 -0
  86. package/lib/adapters/stellar/constants.js +53 -0
  87. package/lib/adapters/stellar/getters.d.ts +68 -0
  88. package/lib/adapters/stellar/getters.js +290 -0
  89. package/lib/adapters/stellar/index.d.ts +114 -0
  90. package/lib/adapters/stellar/index.js +175 -0
  91. package/lib/adapters/stellar/soroban.d.ts +123 -0
  92. package/lib/adapters/stellar/soroban.js +613 -0
  93. package/lib/adapters/stellar/submit.d.ts +34 -0
  94. package/lib/adapters/stellar/submit.js +149 -0
  95. package/lib/adapters/stellar/types.d.ts +58 -0
  96. package/lib/adapters/stellar/types.js +6 -0
  97. package/lib/adapters/stellar/utils.d.ts +24 -0
  98. package/lib/adapters/stellar/utils.js +34 -0
  99. package/lib/adapters/sui/constants.d.ts +14 -0
  100. package/lib/adapters/sui/constants.js +29 -0
  101. package/lib/adapters/sui/getters.d.ts +9 -0
  102. package/lib/adapters/sui/getters.js +60 -0
  103. package/lib/adapters/sui/index.d.ts +45 -0
  104. package/lib/adapters/sui/index.js +101 -0
  105. package/lib/adapters/sui/transformer.d.ts +10 -0
  106. package/lib/adapters/sui/transformer.js +107 -0
  107. package/lib/adapters/sui/types.d.ts +66 -0
  108. package/lib/adapters/sui/types.js +3 -0
  109. package/lib/adapters/sui/utils.d.ts +10 -0
  110. package/lib/adapters/sui/utils.js +29 -0
  111. package/lib/core/analytics/chain-name.d.ts +9 -0
  112. package/lib/core/analytics/chain-name.js +34 -0
  113. package/lib/core/analytics/constants.d.ts +5 -0
  114. package/lib/core/analytics/constants.js +9 -0
  115. package/lib/core/analytics/env.d.ts +29 -0
  116. package/lib/core/analytics/env.js +59 -0
  117. package/lib/core/analytics/index.d.ts +36 -0
  118. package/lib/core/analytics/index.js +79 -0
  119. package/lib/core/analytics/instrumentation.d.ts +29 -0
  120. package/lib/core/analytics/instrumentation.js +277 -0
  121. package/lib/core/analytics/method-taxonomy.d.ts +19 -0
  122. package/lib/core/analytics/method-taxonomy.js +128 -0
  123. package/lib/core/analytics/metrics.d.ts +33 -0
  124. package/lib/core/analytics/metrics.js +116 -0
  125. package/lib/core/analytics/sanitize.d.ts +44 -0
  126. package/lib/core/analytics/sanitize.js +260 -0
  127. package/lib/core/analytics/sentry-runtime.d.ts +15 -0
  128. package/lib/core/analytics/sentry-runtime.js +97 -0
  129. package/lib/core/analytics/sentry.d.ts +67 -0
  130. package/lib/core/analytics/sentry.js +613 -0
  131. package/lib/core/analytics/types.d.ts +48 -0
  132. package/lib/core/analytics/types.js +3 -0
  133. package/lib/core/analytics/user-identity.d.ts +41 -0
  134. package/lib/core/analytics/user-identity.js +141 -0
  135. package/lib/core/analytics/version.d.ts +6 -0
  136. package/lib/core/analytics/version.js +10 -0
  137. package/lib/core/attribution.d.ts +111 -0
  138. package/lib/core/attribution.js +142 -0
  139. package/lib/core/auth/index.d.ts +1 -0
  140. package/lib/core/auth/index.js +18 -0
  141. package/lib/core/auth/verify.d.ts +2 -0
  142. package/lib/core/auth/verify.js +31 -0
  143. package/lib/core/base.class.d.ts +152 -0
  144. package/lib/core/base.class.js +172 -0
  145. package/lib/core/cache.d.ts +9 -0
  146. package/lib/core/cache.js +31 -0
  147. package/lib/core/constants/adapters.d.ts +103 -0
  148. package/lib/core/constants/adapters.js +180 -0
  149. package/lib/core/constants/core.d.ts +133 -0
  150. package/lib/core/constants/core.js +209 -0
  151. package/lib/core/constants/swap-router.d.ts +150 -0
  152. package/lib/core/constants/swap-router.js +169 -0
  153. package/lib/core/constants/vaults.d.ts +93 -0
  154. package/lib/core/constants/vaults.js +273 -0
  155. package/lib/core/constants/web3.d.ts +91 -0
  156. package/lib/core/constants/web3.js +229 -0
  157. package/lib/core/errors/index.d.ts +114 -0
  158. package/lib/core/errors/index.js +183 -0
  159. package/lib/core/fetcher.d.ts +198 -0
  160. package/lib/core/fetcher.js +903 -0
  161. package/lib/core/helpers/adapters.d.ts +13 -0
  162. package/lib/core/helpers/adapters.js +39 -0
  163. package/lib/core/helpers/chain-address.d.ts +13 -0
  164. package/lib/core/helpers/chain-address.js +47 -0
  165. package/lib/core/helpers/chain-error.d.ts +207 -0
  166. package/lib/core/helpers/chain-error.js +682 -0
  167. package/lib/core/helpers/chain-support.d.ts +80 -0
  168. package/lib/core/helpers/chain-support.js +115 -0
  169. package/lib/core/helpers/core.d.ts +68 -0
  170. package/lib/core/helpers/core.js +285 -0
  171. package/lib/core/helpers/explorer-link.d.ts +16 -0
  172. package/lib/core/helpers/explorer-link.js +26 -0
  173. package/lib/core/helpers/multicall.d.ts +68 -0
  174. package/lib/core/helpers/multicall.js +103 -0
  175. package/lib/core/helpers/revert-decode.d.ts +248 -0
  176. package/lib/core/helpers/revert-decode.js +515 -0
  177. package/lib/core/helpers/signer.d.ts +52 -0
  178. package/lib/core/helpers/signer.js +145 -0
  179. package/lib/core/helpers/swap-router.d.ts +211 -0
  180. package/lib/core/helpers/swap-router.js +480 -0
  181. package/lib/core/helpers/vault-version.d.ts +23 -0
  182. package/lib/core/helpers/vault-version.js +75 -0
  183. package/lib/core/helpers/vaults.d.ts +89 -0
  184. package/lib/core/helpers/vaults.js +235 -0
  185. package/lib/core/helpers/web3.d.ts +353 -0
  186. package/lib/core/helpers/web3.js +992 -0
  187. package/lib/core/index.d.ts +23 -0
  188. package/lib/core/index.js +40 -0
  189. package/lib/core/logger/index.d.ts +98 -0
  190. package/lib/core/logger/index.js +144 -0
  191. package/lib/core/logger/slack.d.ts +16 -0
  192. package/lib/core/logger/slack.js +57 -0
  193. package/lib/core/vault-metadata.d.ts +12 -0
  194. package/lib/core/vault-metadata.js +42 -0
  195. package/lib/core/version-check.d.ts +58 -0
  196. package/lib/core/version-check.js +182 -0
  197. package/lib/evm/index.d.ts +2 -0
  198. package/lib/evm/index.js +19 -0
  199. package/lib/evm/methods/crossChainVault.d.ts +128 -0
  200. package/lib/evm/methods/crossChainVault.js +853 -0
  201. package/lib/evm/methods/crossChainVaultRegistry.d.ts +93 -0
  202. package/lib/evm/methods/crossChainVaultRegistry.js +240 -0
  203. package/lib/evm/methods/index.d.ts +2 -0
  204. package/lib/evm/methods/index.js +19 -0
  205. package/lib/evm/types/crossChain.d.ts +363 -0
  206. package/lib/evm/types/crossChain.js +20 -0
  207. package/lib/evm/types/index.d.ts +1 -0
  208. package/lib/evm/types/index.js +18 -0
  209. package/lib/index.d.ts +30 -0
  210. package/lib/index.js +52 -0
  211. package/lib/main.d.ts +527 -0
  212. package/lib/main.js +601 -0
  213. package/lib/modules/api/fetcher.d.ts +82 -0
  214. package/lib/modules/api/fetcher.js +150 -0
  215. package/lib/modules/api/index.d.ts +1 -0
  216. package/lib/modules/api/index.js +6 -0
  217. package/lib/modules/api/main.d.ts +313 -0
  218. package/lib/modules/api/main.js +479 -0
  219. package/lib/modules/sub-accounts/fetcher.d.ts +53 -0
  220. package/lib/modules/sub-accounts/fetcher.js +120 -0
  221. package/lib/modules/sub-accounts/index.d.ts +2 -0
  222. package/lib/modules/sub-accounts/index.js +19 -0
  223. package/lib/modules/sub-accounts/main.d.ts +243 -0
  224. package/lib/modules/sub-accounts/main.js +205 -0
  225. package/lib/modules/sub-accounts/utils.d.ts +106 -0
  226. package/lib/modules/sub-accounts/utils.js +112 -0
  227. package/lib/modules/vaults/adapter.helpers.d.ts +64 -0
  228. package/lib/modules/vaults/adapter.helpers.js +184 -0
  229. package/lib/modules/vaults/fetcher.d.ts +147 -0
  230. package/lib/modules/vaults/fetcher.js +368 -0
  231. package/lib/modules/vaults/getters.d.ts +570 -0
  232. package/lib/modules/vaults/getters.js +3051 -0
  233. package/lib/modules/vaults/index.d.ts +20 -0
  234. package/lib/modules/vaults/index.js +44 -0
  235. package/lib/modules/vaults/main.d.ts +601 -0
  236. package/lib/modules/vaults/main.js +1623 -0
  237. package/lib/modules/vaults/prefetch.d.ts +65 -0
  238. package/lib/modules/vaults/prefetch.js +120 -0
  239. package/lib/modules/vaults/read.actions.d.ts +225 -0
  240. package/lib/modules/vaults/read.actions.js +596 -0
  241. package/lib/modules/vaults/types.d.ts +71 -0
  242. package/lib/modules/vaults/types.js +3 -0
  243. package/lib/modules/vaults/utils/call-data-decoder.d.ts +61 -0
  244. package/lib/modules/vaults/utils/call-data-decoder.js +194 -0
  245. package/lib/modules/vaults/utils/date-utils.d.ts +50 -0
  246. package/lib/modules/vaults/utils/date-utils.js +84 -0
  247. package/lib/modules/vaults/utils.d.ts +140 -0
  248. package/lib/modules/vaults/utils.js +799 -0
  249. package/lib/modules/vaults/write.actions.d.ts +529 -0
  250. package/lib/modules/vaults/write.actions.js +1749 -0
  251. package/lib/polyfills.d.ts +1 -0
  252. package/lib/polyfills.js +12 -0
  253. package/lib/sdk.d.ts +26721 -0
  254. package/lib/services/coingecko/fetcher.d.ts +15 -0
  255. package/lib/services/coingecko/fetcher.js +67 -0
  256. package/lib/services/coingecko/index.d.ts +2 -0
  257. package/lib/services/coingecko/index.js +19 -0
  258. package/lib/services/coingecko/utils.d.ts +1 -0
  259. package/lib/services/coingecko/utils.js +24 -0
  260. package/lib/services/debank/fetcher.d.ts +126 -0
  261. package/lib/services/debank/fetcher.js +47 -0
  262. package/lib/services/debank/index.d.ts +2 -0
  263. package/lib/services/debank/index.js +19 -0
  264. package/lib/services/debank/utils.d.ts +38 -0
  265. package/lib/services/debank/utils.js +297 -0
  266. package/lib/services/layerzero/deposits.d.ts +49 -0
  267. package/lib/services/layerzero/deposits.js +166 -0
  268. package/lib/services/layerzero/redeems.d.ts +20 -0
  269. package/lib/services/layerzero/redeems.js +92 -0
  270. package/lib/services/layerzero/utils.d.ts +9 -0
  271. package/lib/services/layerzero/utils.js +22 -0
  272. package/lib/services/octavfi/fetcher.d.ts +9 -0
  273. package/lib/services/octavfi/fetcher.js +103 -0
  274. package/lib/services/octavfi/index.d.ts +3 -0
  275. package/lib/services/octavfi/index.js +20 -0
  276. package/lib/services/octavfi/types.d.ts +34 -0
  277. package/lib/services/octavfi/types.js +3 -0
  278. package/lib/services/octavfi/utils.d.ts +16 -0
  279. package/lib/services/octavfi/utils.js +212 -0
  280. package/lib/services/subgraph/fetcher.d.ts +2 -0
  281. package/lib/services/subgraph/fetcher.js +61 -0
  282. package/lib/services/subgraph/index.d.ts +2 -0
  283. package/lib/services/subgraph/index.js +19 -0
  284. package/lib/services/subgraph/schema.d.ts +45 -0
  285. package/lib/services/subgraph/schema.js +75 -0
  286. package/lib/services/subgraph/vaults.d.ts +25 -0
  287. package/lib/services/subgraph/vaults.js +1106 -0
  288. package/lib/services/swap-quotes/index.d.ts +105 -0
  289. package/lib/services/swap-quotes/index.js +77 -0
  290. package/lib/services/swap-quotes/paraswap.d.ts +51 -0
  291. package/lib/services/swap-quotes/paraswap.js +138 -0
  292. package/lib/types/api.d.ts +280 -0
  293. package/lib/types/api.js +3 -0
  294. package/lib/types/index.d.ts +12 -0
  295. package/lib/types/index.js +28 -0
  296. package/lib/types/points.d.ts +28 -0
  297. package/lib/types/points.js +3 -0
  298. package/lib/types/pools.d.ts +144 -0
  299. package/lib/types/pools.js +3 -0
  300. package/lib/types/staking.d.ts +28 -0
  301. package/lib/types/staking.js +3 -0
  302. package/lib/types/sub-accounts.d.ts +140 -0
  303. package/lib/types/sub-accounts.js +3 -0
  304. package/lib/types/subgraph.d.ts +74 -0
  305. package/lib/types/subgraph.js +3 -0
  306. package/lib/types/typed-contract.d.ts +102 -0
  307. package/lib/types/typed-contract.js +3 -0
  308. package/lib/types/user.d.ts +1 -0
  309. package/lib/types/user.js +3 -0
  310. package/lib/types/vaults.d.ts +714 -0
  311. package/lib/types/vaults.js +24 -0
  312. package/lib/types/web3.d.ts +51 -0
  313. package/lib/types/web3.js +14 -0
  314. package/lib/types/webserver.d.ts +698 -0
  315. package/lib/types/webserver.js +3 -0
  316. package/package.json +78 -0
@@ -0,0 +1,853 @@
1
+ "use strict";
2
+ /**
3
+ * Cross-Chain Vault Methods
4
+ *
5
+ * Implements LayerZero OVault cross-chain deposit and redemption operations.
6
+ * These functions are generic — callers provide their own ICrossChainVaultConfig
7
+ * rather than relying on hardcoded vault addresses.
8
+ *
9
+ * @module evm/methods/crossChainVault
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ exports.LAYERZERO_ENDPOINT_IDS = void 0;
46
+ exports.buildCrossChainVaultTx = buildCrossChainVaultTx;
47
+ exports.quoteCrossChainDeposit = quoteCrossChainDeposit;
48
+ exports.quoteCrossChainRedeem = quoteCrossChainRedeem;
49
+ exports.needsCrossChainApproval = needsCrossChainApproval;
50
+ exports.approveCrossChain = approveCrossChain;
51
+ exports.crossChainVaultDeposit = crossChainVaultDeposit;
52
+ exports.crossChainVaultRedeem = crossChainVaultRedeem;
53
+ exports.isNoPeerError = isNoPeerError;
54
+ exports.classifyCrossChainError = classifyCrossChainError;
55
+ exports.canDepositFromChain = canDepositFromChain;
56
+ exports.canRedeemFromChain = canRedeemFromChain;
57
+ exports.isCrossChainOperation = isCrossChainOperation;
58
+ exports.getAvailableChains = getAvailableChains;
59
+ exports.formatCrossChainError = formatCrossChainError;
60
+ exports.getLayerZeroScanUrl = getLayerZeroScanUrl;
61
+ const attribution_1 = require("../../core/attribution");
62
+ const core_1 = require("../../core");
63
+ const crossChain_1 = require("../types/crossChain");
64
+ const OFT_1 = require("../../abis/OFT");
65
+ // ---------------------------------------------------------------------------
66
+ // Constants
67
+ // ---------------------------------------------------------------------------
68
+ /**
69
+ * lzReceive option hex for 500,000 gas.
70
+ * The Agora OFT has no enforced options for SEND_AND_CALL, so we must provide
71
+ * lzReceive gas explicitly or the executor reverts with Executor_ZeroLzReceiveGasProvided.
72
+ */
73
+ const LZ_RECEIVE_OPTION_HEX = '010011010000000000000000000000000007a120';
74
+ /** Default fee buffer percentage applied to LayerZero native fees */
75
+ const DEFAULT_FEE_BUFFER_PERCENT = 50;
76
+ /** Default slippage in basis points (1%) */
77
+ const DEFAULT_SLIPPAGE_BPS = 100;
78
+ /** Default gas buffer for deposits (1.5x) */
79
+ const DEFAULT_DEPOSIT_GAS_BUFFER_PERCENT = 150;
80
+ /** Default gas buffer for redeems (2x) */
81
+ const DEFAULT_REDEEM_GAS_BUFFER_PERCENT = 200;
82
+ /** Default minimum gas limit for redeem operations */
83
+ const DEFAULT_MIN_REDEEM_GAS = 5000000n;
84
+ /** Default hub lzCompose gas limit */
85
+ const DEFAULT_HUB_LZ_COMPOSE_GAS_LIMIT = 2000000n;
86
+ // ---------------------------------------------------------------------------
87
+ // Mutex for SDK monkey-patches
88
+ //
89
+ // Multi-asset and hub-redeem flows mutate the shared `OVaultSyncMessageBuilder`
90
+ // — module-level bindings inside the SDK mean a shallow clone is unsafe, so we
91
+ // serialize patched calls. The timeout THROWS rather than silently racing
92
+ // concurrent callers' patches.
93
+ // ---------------------------------------------------------------------------
94
+ const PATCH_LOCK_TIMEOUT_MS = 30_000;
95
+ let patchLock = Promise.resolve();
96
+ // ---------------------------------------------------------------------------
97
+ // Internal helpers
98
+ // ---------------------------------------------------------------------------
99
+ /**
100
+ * Try available previewRedeem signatures, falling back to 1:1 if the vault
101
+ * does not expose a working preview function.
102
+ */
103
+ async function redeemPreview(publicClient, vaultAddress, shares) {
104
+ // 1. previewRedemption(uint256, bool) — multi-asset vault
105
+ try {
106
+ const result = await publicClient.readContract({
107
+ address: vaultAddress,
108
+ abi: OFT_1.ABI_MULTI_ASSET_PREVIEW_REDEEM,
109
+ functionName: 'previewRedemption',
110
+ args: [shares, false],
111
+ });
112
+ const [, assetsAfterFee] = result;
113
+ return assetsAfterFee;
114
+ }
115
+ catch {
116
+ /* try next */
117
+ }
118
+ // 2. Standard ERC4626: previewRedeem(uint256)
119
+ try {
120
+ return (await publicClient.readContract({
121
+ address: vaultAddress,
122
+ abi: OFT_1.ABI_STANDARD_PREVIEW_REDEEM,
123
+ functionName: 'previewRedeem',
124
+ args: [shares],
125
+ }));
126
+ }
127
+ catch {
128
+ /* try next */
129
+ }
130
+ // 3. Fallback: 1:1 ratio. Slippage protects against discrepancy.
131
+ return shares;
132
+ }
133
+ /**
134
+ * Wraps generateOVaultInputs with monkey-patches for:
135
+ * 1. REDEEM from hub — skip quoteOFT (reverts for same-chain)
136
+ * 2. Multi-asset DEPOSIT — previewDeposit(address, uint256)
137
+ * 3. Multi-asset REDEEM — try previewRedeem variants, fallback to 1:1
138
+ * 4. Multi-asset composed messages — add lzReceive gas to SEND_AND_CALL
139
+ */
140
+ async function generateOVaultInputsWithOverride(input, config, hubPublicClient, ovaultSdk) {
141
+ const OVaultSyncMessageBuilder = ovaultSdk.OVaultSyncMessageBuilder;
142
+ const OVaultSyncOperations = ovaultSdk.OVaultSyncOperations;
143
+ const isRedeemFromHub = input.operation === OVaultSyncOperations.REDEEM &&
144
+ input.srcEid === input.hubEid;
145
+ const isMultiAsset = !!config.multiAsset;
146
+ // Fast path — no patches needed
147
+ if (!isMultiAsset && !isRedeemFromHub) {
148
+ return OVaultSyncMessageBuilder.generateOVaultInputs(input);
149
+ }
150
+ // Serialize: wait for the prior holder or REJECT on timeout (silent races
151
+ // produced wrong cross-chain quotes).
152
+ const prev = patchLock;
153
+ let unlock;
154
+ patchLock = new Promise((r) => {
155
+ unlock = r;
156
+ });
157
+ let timeoutHandle;
158
+ try {
159
+ await Promise.race([
160
+ prev,
161
+ new Promise((_resolve, reject) => {
162
+ timeoutHandle = setTimeout(() => reject(new Error(`crossChainVault patch lock timed out after ${PATCH_LOCK_TIMEOUT_MS}ms — a prior call may be hung`)), PATCH_LOCK_TIMEOUT_MS);
163
+ }),
164
+ ]);
165
+ }
166
+ catch (err) {
167
+ unlock();
168
+ if (timeoutHandle)
169
+ clearTimeout(timeoutHandle);
170
+ throw err;
171
+ }
172
+ if (timeoutHandle)
173
+ clearTimeout(timeoutHandle);
174
+ const Builder = OVaultSyncMessageBuilder;
175
+ const origGetOutputAmount = Builder.getOutputAmount;
176
+ const origQuoteOVaultOutput = Builder.quoteOVaultOutput;
177
+ const origBuildSendParams = Builder.buildSendParams;
178
+ const origBuildApproval = Builder.buildApproval;
179
+ try {
180
+ if (isRedeemFromHub) {
181
+ // Patch 1a: skip quoteOFT (reverts for same-chain calls).
182
+ Builder.getOutputAmount = async () => input.amount;
183
+ // Patch 1b: skip SDK's allowance check — multi-asset vault contracts
184
+ // may not implement ERC20. Caller handles approval directly.
185
+ Builder.buildApproval = async () => undefined;
186
+ }
187
+ if (isMultiAsset) {
188
+ const { hubAssetAddress } = config.multiAsset;
189
+ // Patch 2/3: Multi-asset quoteOVaultOutput
190
+ Builder.quoteOVaultOutput = async (quoteInput) => {
191
+ const getOutputAmount = Builder.getOutputAmount;
192
+ const sourceAmount = await getOutputAmount({
193
+ ...quoteInput,
194
+ dstAmount: 0n,
195
+ minDstAmount: 0n,
196
+ dstAddress: quoteInput.walletAddress,
197
+ tokenAddress: quoteInput.tokenAddress ?? quoteInput.oftAddress,
198
+ });
199
+ let outputAmount;
200
+ if (quoteInput.operation === OVaultSyncOperations.REDEEM) {
201
+ outputAmount = await redeemPreview(hubPublicClient, config.vaultAddress, sourceAmount);
202
+ }
203
+ else {
204
+ const [depositOutput] = (await hubPublicClient.readContract({
205
+ address: config.vaultAddress,
206
+ abi: OFT_1.ABI_MULTI_ASSET_PREVIEW_DEPOSIT,
207
+ functionName: 'previewDeposit',
208
+ args: [hubAssetAddress, sourceAmount],
209
+ }));
210
+ outputAmount = depositOutput;
211
+ }
212
+ const slippageBps = BigInt(Math.round(quoteInput.slippage * 10000));
213
+ const slippageAmount = (outputAmount * slippageBps) / 10000n;
214
+ return {
215
+ dstAmount: outputAmount,
216
+ minDstAmount: outputAmount - slippageAmount,
217
+ };
218
+ };
219
+ // Patch 4: Ensure lzReceive gas in extraOptions for composed messages
220
+ Builder.buildSendParams = async (spInput) => {
221
+ const origFn = origBuildSendParams;
222
+ const result = await origFn.call(Builder, spInput);
223
+ if (result.composeMsg && result.composeMsg !== '0x') {
224
+ const opts = result.extraOptions;
225
+ if (opts && opts.length >= 6 && opts.startsWith('0x0003')) {
226
+ const body = opts.slice(6);
227
+ const hasLzReceive = body.startsWith('01001101');
228
+ if (!hasLzReceive) {
229
+ result.extraOptions = `0x0003${LZ_RECEIVE_OPTION_HEX}${body}`;
230
+ }
231
+ }
232
+ }
233
+ return result;
234
+ };
235
+ }
236
+ return (await OVaultSyncMessageBuilder.generateOVaultInputs(input));
237
+ }
238
+ finally {
239
+ Builder.getOutputAmount = origGetOutputAmount;
240
+ Builder.quoteOVaultOutput = origQuoteOVaultOutput;
241
+ Builder.buildSendParams = origBuildSendParams;
242
+ Builder.buildApproval = origBuildApproval;
243
+ unlock();
244
+ }
245
+ }
246
+ // ---------------------------------------------------------------------------
247
+ // Public API: Build
248
+ // ---------------------------------------------------------------------------
249
+ /**
250
+ * Build LayerZero OVault transaction inputs for cross-chain deposits and
251
+ * redemptions. Does NOT execute the transaction.
252
+ *
253
+ * Overridable defaults on `props`: `slippageBps` = 100 (range 1–10000),
254
+ * `feeBufferPercent` = 50% (added to LZ `nativeFee`),
255
+ * `hubLzComposeGasLimit` = 2_000_000n (multi-asset composed messages).
256
+ *
257
+ * @returns Transaction inputs or `{ success: false, error }`.
258
+ */
259
+ async function buildCrossChainVaultTx(props) {
260
+ try {
261
+ const { config } = props;
262
+ // Validate slippage at the public boundary (BPS, not decimal)
263
+ const slippageBps = props.slippageBps ?? DEFAULT_SLIPPAGE_BPS;
264
+ if (slippageBps < 1 || slippageBps > 10000) {
265
+ return {
266
+ success: false,
267
+ error: `Slippage must be between 1 and 10000 basis points (got ${slippageBps})`,
268
+ };
269
+ }
270
+ // Validate chains
271
+ const hubChain = props.hubChain;
272
+ const sourceChain = props.sourceChain;
273
+ if (!hubChain || !sourceChain) {
274
+ return {
275
+ success: false,
276
+ error: 'Hub chain and source chain are required',
277
+ };
278
+ }
279
+ // Reject unknown chain IDs up front so a typo can't fall through to
280
+ // `|| hub` and route shares to a chain the user didn't pick.
281
+ const supportedChains = [
282
+ config.hubChainId,
283
+ ...Object.keys(config.layerZeroEids.spokes).map((k) => Number(k)),
284
+ ];
285
+ const supportedChainsList = supportedChains.join(', ');
286
+ if (props.destinationChainId !== undefined &&
287
+ props.destinationChainId !== config.hubChainId &&
288
+ !config.layerZeroEids.spokes[props.destinationChainId]) {
289
+ return {
290
+ success: false,
291
+ error: `Destination chain ${props.destinationChainId} is not configured for this vault. Supported chains: ${supportedChainsList}.`,
292
+ };
293
+ }
294
+ if (props.operation === 'DEPOSIT' &&
295
+ props.userChainId !== config.hubChainId &&
296
+ !config.layerZeroEids.spokes[props.userChainId]) {
297
+ return {
298
+ success: false,
299
+ error: `Source chain ${props.userChainId} is not configured for this vault. Supported chains: ${supportedChainsList}.`,
300
+ };
301
+ }
302
+ const resolveDstEid = (chainId) => {
303
+ if (chainId === undefined)
304
+ return undefined;
305
+ if (chainId === config.hubChainId)
306
+ return config.layerZeroEids.hub;
307
+ return config.layerZeroEids.spokes[chainId];
308
+ };
309
+ // Determine source and destination EIDs based on operation
310
+ let srcEid;
311
+ let dstEid;
312
+ let tokenAddress;
313
+ let oftAddress;
314
+ switch (props.operation) {
315
+ case 'DEPOSIT': {
316
+ const resolvedSrc = resolveDstEid(props.userChainId);
317
+ srcEid =
318
+ resolvedSrc !== undefined ? resolvedSrc : config.layerZeroEids.hub;
319
+ const resolved = resolveDstEid(props.destinationChainId);
320
+ dstEid = resolved !== undefined ? resolved : srcEid;
321
+ tokenAddress = config.contracts.assetToken[props.userChainId];
322
+ oftAddress = config.contracts.assetOFT[props.userChainId];
323
+ if (!tokenAddress || !oftAddress) {
324
+ return {
325
+ success: false,
326
+ error: `Chain ${props.userChainId} is not configured for deposits on this vault`,
327
+ };
328
+ }
329
+ break;
330
+ }
331
+ case 'REDEEM': {
332
+ srcEid = config.layerZeroEids.hub;
333
+ const resolved = resolveDstEid(props.destinationChainId) ??
334
+ resolveDstEid(props.userChainId);
335
+ dstEid = resolved !== undefined ? resolved : config.layerZeroEids.hub;
336
+ tokenAddress = config.vaultAddress;
337
+ oftAddress = config.contracts.shareOFTAdapter;
338
+ break;
339
+ }
340
+ default:
341
+ return {
342
+ success: false,
343
+ error: `Invalid operation: ${props.operation}`,
344
+ };
345
+ }
346
+ // Normalize amount
347
+ const normalizedAmount = (0, core_1.toNormalizedBn)(props.amount, props.decimals);
348
+ // Build LayerZero OVault inputs
349
+ const input = {
350
+ srcEid,
351
+ hubEid: config.layerZeroEids.hub,
352
+ dstEid,
353
+ dstAddress: props.recipientAddress || props.walletAddress,
354
+ walletAddress: props.walletAddress,
355
+ vaultAddress: config.vaultAddress,
356
+ composerAddress: config.contracts.composerSync,
357
+ hubChain: hubChain,
358
+ sourceChain: sourceChain,
359
+ operation: props.operation === 'DEPOSIT'
360
+ ? props.ovaultSdk.OVaultSyncOperations.DEPOSIT
361
+ : props.ovaultSdk.OVaultSyncOperations.REDEEM,
362
+ amount: BigInt(normalizedAmount.raw),
363
+ slippage: slippageBps / 10000,
364
+ tokenAddress,
365
+ oftAddress,
366
+ hubLzComposeGasLimit: props.hubLzComposeGasLimit ?? DEFAULT_HUB_LZ_COMPOSE_GAS_LIMIT,
367
+ };
368
+ // Create a minimal public client for hub chain reads (multi-asset patches)
369
+ const hubRpc = hubChain?.rpcUrls;
370
+ const hubRpcUrl = hubRpc?.default?.http?.[0];
371
+ let hubPublicClient;
372
+ try {
373
+ // Dynamic import to keep viem optional at the module level
374
+ const viem = await Promise.resolve().then(() => __importStar(require('viem')));
375
+ hubPublicClient = viem.createPublicClient({
376
+ chain: hubChain,
377
+ transport: viem.http(hubRpcUrl),
378
+ });
379
+ }
380
+ catch {
381
+ // If viem is not available, provide a stub that will fail on multi-asset only
382
+ hubPublicClient = {
383
+ readContract: async () => {
384
+ throw new Error('viem is required for multi-asset vault operations. Install viem as a peer dependency.');
385
+ },
386
+ };
387
+ }
388
+ // Generate OVault transaction inputs (with monkey-patches for multi-asset)
389
+ const inputs = await generateOVaultInputsWithOverride(input, config, hubPublicClient, props.ovaultSdk);
390
+ // Apply fee buffer to LZ fees (post-hoc to avoid SDK double-compounding)
391
+ const feeBufferPercent = props.feeBufferPercent ?? DEFAULT_FEE_BUFFER_PERCENT;
392
+ const rawNativeFee = inputs.messageFee.nativeFee;
393
+ const rawMessageValue = inputs.messageValue;
394
+ const feeBuffer = (rawNativeFee * BigInt(feeBufferPercent)) / 100n;
395
+ inputs.messageFee.nativeFee = rawNativeFee + feeBuffer;
396
+ inputs.messageValue = rawMessageValue + feeBuffer;
397
+ // Patch fee into txArgs in place for OFT.send(); warn if the LayerZero
398
+ // SDK ever returns a different entry-point name (silent no-op would
399
+ // underpay the LZ fee).
400
+ if (inputs.contractFunctionName === 'send') {
401
+ const sendFee = inputs.txArgs[1];
402
+ sendFee.nativeFee = inputs.messageFee.nativeFee;
403
+ }
404
+ else {
405
+ core_1.Logger.log.warn('crossChainVault:fee-patch', `Unexpected contractFunctionName "${inputs.contractFunctionName}" from LayerZero SDK — fee buffer not applied to txArgs in-place`, { contractFunctionName: inputs.contractFunctionName });
406
+ }
407
+ return {
408
+ success: true,
409
+ data: {
410
+ contractAddress: inputs.contractAddress,
411
+ contractFunctionName: inputs.contractFunctionName,
412
+ abi: inputs.abi,
413
+ txArgs: inputs.txArgs,
414
+ messageValue: inputs.messageValue,
415
+ messageFee: {
416
+ nativeFee: inputs.messageFee.nativeFee,
417
+ lzTokenFee: inputs.messageFee.lzTokenFee,
418
+ },
419
+ dstAmount: inputs.dstAmount.amount,
420
+ minDstAmount: inputs.dstAmount.minAmount,
421
+ approval: inputs.approval
422
+ ? {
423
+ tokenAddress: inputs.approval.tokenAddress,
424
+ spender: inputs.approval.spender,
425
+ amount: inputs.approval.amount,
426
+ }
427
+ : undefined,
428
+ },
429
+ };
430
+ }
431
+ catch (error) {
432
+ return {
433
+ success: false,
434
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
435
+ };
436
+ }
437
+ }
438
+ // ---------------------------------------------------------------------------
439
+ // Public API: Quote
440
+ // ---------------------------------------------------------------------------
441
+ /**
442
+ * Estimate fees and expected shares for a cross-chain deposit. The returned
443
+ * quote carries `quotedAt`/`expiresAt`; re-quote via {@link isQuoteStale}
444
+ * before submitting.
445
+ */
446
+ async function quoteCrossChainDeposit(props) {
447
+ const result = await buildCrossChainVaultTx({
448
+ ovaultSdk: props.ovaultSdk,
449
+ config: props.config,
450
+ operation: 'DEPOSIT',
451
+ amount: props.amount,
452
+ decimals: props.decimals,
453
+ userChainId: props.userChainId,
454
+ walletAddress: props.walletAddress,
455
+ destinationChainId: props.destinationChainId,
456
+ slippageBps: props.slippageBps,
457
+ feeBufferPercent: props.feeBufferPercent,
458
+ hubChain: props.hubChain,
459
+ sourceChain: props.sourceChain,
460
+ });
461
+ if (!result.success || !result.data)
462
+ return null;
463
+ const quotedAt = Date.now();
464
+ return {
465
+ nativeFee: result.data.messageFee.nativeFee,
466
+ dstAmount: result.data.dstAmount,
467
+ minDstAmount: result.data.minDstAmount,
468
+ quotedAt,
469
+ expiresAt: quotedAt + crossChain_1.CROSS_CHAIN_QUOTE_TTL_MS,
470
+ };
471
+ }
472
+ /**
473
+ * Estimate fees and expected assets for a cross-chain redeem. The returned
474
+ * quote carries `quotedAt`/`expiresAt`; re-quote via {@link isQuoteStale}
475
+ * before submitting.
476
+ */
477
+ async function quoteCrossChainRedeem(props) {
478
+ const result = await buildCrossChainVaultTx({
479
+ ovaultSdk: props.ovaultSdk,
480
+ config: props.config,
481
+ operation: 'REDEEM',
482
+ amount: props.amount,
483
+ decimals: props.decimals,
484
+ userChainId: props.config.hubChainId,
485
+ walletAddress: props.walletAddress,
486
+ destinationChainId: props.destinationChainId,
487
+ slippageBps: props.slippageBps,
488
+ feeBufferPercent: props.feeBufferPercent,
489
+ hubChain: props.hubChain,
490
+ sourceChain: props.sourceChain,
491
+ });
492
+ if (!result.success || !result.data)
493
+ return null;
494
+ const quotedAt = Date.now();
495
+ return {
496
+ nativeFee: result.data.messageFee.nativeFee,
497
+ dstAmount: result.data.dstAmount,
498
+ minDstAmount: result.data.minDstAmount,
499
+ quotedAt,
500
+ expiresAt: quotedAt + crossChain_1.CROSS_CHAIN_QUOTE_TTL_MS,
501
+ };
502
+ }
503
+ // ---------------------------------------------------------------------------
504
+ // Public API: Approval
505
+ // ---------------------------------------------------------------------------
506
+ /**
507
+ * Returns `true` when the wallet's ERC-20 allowance is insufficient for the
508
+ * cross-chain operation. On RPC failure also returns `true` (defensive
509
+ * default — better a redundant approval than a downstream revert).
510
+ */
511
+ async function needsCrossChainApproval(tokenAddress, spenderAddress, walletAddress, amount, publicClient) {
512
+ try {
513
+ const allowance = (await publicClient.readContract({
514
+ address: tokenAddress,
515
+ abi: OFT_1.ABI_CROSS_CHAIN_ERC20,
516
+ functionName: 'allowance',
517
+ args: [walletAddress, spenderAddress],
518
+ }));
519
+ return allowance < amount;
520
+ }
521
+ catch {
522
+ // If we can't check allowance, assume approval is needed
523
+ return true;
524
+ }
525
+ }
526
+ /**
527
+ * Execute a token approval for a cross-chain operation.
528
+ *
529
+ * @returns Transaction hash of the approval
530
+ */
531
+ async function approveCrossChain(tokenAddress, spenderAddress, amount, walletClient, publicClient) {
532
+ if (!walletClient.account) {
533
+ throw new Error('Wallet not ready — please reconnect your wallet');
534
+ }
535
+ const hash = await walletClient.writeContract({
536
+ account: walletClient.account,
537
+ address: tokenAddress,
538
+ abi: OFT_1.ABI_CROSS_CHAIN_ERC20,
539
+ functionName: 'approve',
540
+ args: [spenderAddress, amount],
541
+ dataSuffix: (0, attribution_1.getAttributionSuffix)(),
542
+ });
543
+ const receipt = await publicClient.waitForTransactionReceipt({
544
+ hash: hash,
545
+ });
546
+ if (receipt.status === 'reverted') {
547
+ throw new Error(`Approval transaction reverted on-chain (tx ${hash}). Check token allowance limits or that the token contract supports approve().`);
548
+ }
549
+ return hash;
550
+ }
551
+ // ---------------------------------------------------------------------------
552
+ // Public API: Execute
553
+ // ---------------------------------------------------------------------------
554
+ /**
555
+ * Execute a cross-chain deposit via LayerZero OVault. Validates the source
556
+ * chain, ensures approval, estimates gas with a buffer, and waits for the
557
+ * on-chain receipt. Defaults: `slippageBps` 100, `feeBufferPercent` 50,
558
+ * `gasBufferPercent` 150 — all overridable on the request.
559
+ *
560
+ * @throws Error on unsupported chain, approval revert, gas-estimation
561
+ * failure, or on-chain revert.
562
+ */
563
+ async function crossChainVaultDeposit(props) {
564
+ if (props.userChainId !== props.config.hubChainId &&
565
+ !props.config.spokeChainIds.includes(props.userChainId)) {
566
+ throw new Error(`Chain ${props.userChainId} is not supported for deposits to this vault — switch to the hub (${props.config.hubChainId}) or a configured spoke (${props.config.spokeChainIds.join(', ')}).`);
567
+ }
568
+ const walletClient = props.walletClient;
569
+ const publicClient = props.publicClient;
570
+ // 1. Build transaction inputs
571
+ const result = await buildCrossChainVaultTx({
572
+ ovaultSdk: props.ovaultSdk,
573
+ config: props.config,
574
+ operation: 'DEPOSIT',
575
+ amount: props.amount,
576
+ decimals: props.decimals,
577
+ userChainId: props.userChainId,
578
+ walletAddress: props.walletAddress,
579
+ destinationChainId: props.destinationChainId,
580
+ slippageBps: props.slippageBps,
581
+ feeBufferPercent: props.feeBufferPercent,
582
+ hubChain: props.hubChain,
583
+ sourceChain: props.sourceChain,
584
+ });
585
+ if (!result.success || !result.data) {
586
+ throw new Error(result.error || 'Failed to build transaction');
587
+ }
588
+ const txInputs = result.data;
589
+ // 2. Check and execute approval if needed
590
+ if (!props.skipApprovalCheck) {
591
+ const tokenAddr = txInputs.approval?.tokenAddress ??
592
+ props.config.contracts.assetToken[props.userChainId];
593
+ const spenderAddr = txInputs.approval?.spender ?? txInputs.contractAddress;
594
+ const approvalAmount = txInputs.approval?.amount ??
595
+ BigInt((0, core_1.toNormalizedBn)(props.amount, props.decimals).raw);
596
+ const approvalNeeded = await needsCrossChainApproval(tokenAddr, spenderAddr, props.walletAddress, approvalAmount, publicClient);
597
+ if (approvalNeeded) {
598
+ await approveCrossChain(tokenAddr, spenderAddr, approvalAmount, walletClient, publicClient);
599
+ }
600
+ }
601
+ // 3. Estimate gas with buffer
602
+ if (!walletClient.account) {
603
+ throw new Error('Wallet not ready — please reconnect your wallet');
604
+ }
605
+ const gasBufferPercent = props.gasBufferPercent ?? DEFAULT_DEPOSIT_GAS_BUFFER_PERCENT;
606
+ let gasLimit;
607
+ try {
608
+ const estimated = await publicClient.estimateContractGas({
609
+ account: walletClient.account,
610
+ address: txInputs.contractAddress,
611
+ abi: txInputs.abi,
612
+ functionName: txInputs.contractFunctionName,
613
+ args: txInputs.txArgs,
614
+ value: txInputs.messageValue,
615
+ });
616
+ gasLimit = (estimated * BigInt(gasBufferPercent)) / 100n;
617
+ }
618
+ catch (estimateErr) {
619
+ throw new Error(`Gas estimation failed for cross-chain deposit. Ensure you have sufficient native tokens for the LayerZero fee (${txInputs.messageFee.nativeFee.toString()} wei). Original error: ${estimateErr instanceof Error ? estimateErr.message : String(estimateErr)}`);
620
+ }
621
+ // 4. Execute the transaction
622
+ const hash = await walletClient.writeContract({
623
+ account: walletClient.account,
624
+ address: txInputs.contractAddress,
625
+ abi: txInputs.abi,
626
+ functionName: txInputs.contractFunctionName,
627
+ args: txInputs.txArgs,
628
+ value: txInputs.messageValue,
629
+ gas: gasLimit,
630
+ dataSuffix: (0, attribution_1.getAttributionSuffix)(props.userChainId),
631
+ });
632
+ // 5. Wait for confirmation
633
+ const receipt = await publicClient.waitForTransactionReceipt({
634
+ hash: hash,
635
+ });
636
+ if (receipt.status === 'reverted') {
637
+ throw new Error('Transaction reverted on-chain. This may be due to insufficient gas for the LayerZero compose call or a contract error.');
638
+ }
639
+ return {
640
+ transactionHash: hash,
641
+ success: true,
642
+ };
643
+ }
644
+ /**
645
+ * Execute a cross-chain redemption via LayerZero OVault. User must be on the
646
+ * hub chain. Defaults: `slippageBps` 100, `feeBufferPercent` 50,
647
+ * `gasBufferPercent` 200 (compose calls under-estimate on mainnet),
648
+ * `minGasLimit` 5_000_000n — all overridable on the request.
649
+ */
650
+ async function crossChainVaultRedeem(props) {
651
+ // 1. Validate: redemptions must be from hub chain
652
+ if (props.userChainId !== props.config.hubChainId) {
653
+ throw new Error(`Redemptions must be executed from hub chain (${props.config.hubChainId}). ` +
654
+ `Please switch to the hub chain first.`);
655
+ }
656
+ const walletClient = props.walletClient;
657
+ const publicClient = props.publicClient;
658
+ // 2. Build transaction inputs
659
+ const result = await buildCrossChainVaultTx({
660
+ ovaultSdk: props.ovaultSdk,
661
+ config: props.config,
662
+ operation: 'REDEEM',
663
+ amount: props.amount,
664
+ decimals: props.decimals,
665
+ userChainId: props.userChainId,
666
+ walletAddress: props.walletAddress,
667
+ destinationChainId: props.destinationChainId ?? props.userChainId,
668
+ slippageBps: props.slippageBps,
669
+ feeBufferPercent: props.feeBufferPercent,
670
+ hubChain: props.hubChain,
671
+ sourceChain: props.sourceChain,
672
+ });
673
+ if (!result.success || !result.data) {
674
+ throw new Error(result.error || 'Failed to build transaction');
675
+ }
676
+ const txInputs = result.data;
677
+ // 3. Check and execute approval if needed
678
+ if (!props.skipApprovalCheck && txInputs.approval) {
679
+ const approvalNeeded = await needsCrossChainApproval(txInputs.approval.tokenAddress, txInputs.approval.spender, props.walletAddress, txInputs.approval.amount, publicClient);
680
+ if (approvalNeeded) {
681
+ await approveCrossChain(txInputs.approval.tokenAddress, txInputs.approval.spender, txInputs.approval.amount, walletClient, publicClient);
682
+ }
683
+ }
684
+ // 4. Estimate gas with buffer
685
+ if (!walletClient.account) {
686
+ throw new Error('Wallet account not available');
687
+ }
688
+ const gasBufferPercent = props.gasBufferPercent ?? DEFAULT_REDEEM_GAS_BUFFER_PERCENT;
689
+ const minGasLimit = props.minGasLimit ?? DEFAULT_MIN_REDEEM_GAS;
690
+ let gasLimit;
691
+ try {
692
+ const estimated = await publicClient.estimateContractGas({
693
+ account: walletClient.account,
694
+ address: txInputs.contractAddress,
695
+ abi: txInputs.abi,
696
+ functionName: txInputs.contractFunctionName,
697
+ args: txInputs.txArgs,
698
+ value: txInputs.messageValue,
699
+ });
700
+ const buffered = (estimated * BigInt(gasBufferPercent)) / 100n;
701
+ gasLimit = buffered > minGasLimit ? buffered : minGasLimit;
702
+ }
703
+ catch (estimateErr) {
704
+ throw new Error(`Gas estimation failed for cross-chain redeem. Ensure you have sufficient native tokens for the LayerZero fee (${txInputs.messageFee.nativeFee.toString()} wei). Original error: ${estimateErr instanceof Error ? estimateErr.message : String(estimateErr)}`);
705
+ }
706
+ // 5. Execute the transaction
707
+ const hash = await walletClient.writeContract({
708
+ account: walletClient.account,
709
+ address: txInputs.contractAddress,
710
+ abi: txInputs.abi,
711
+ functionName: txInputs.contractFunctionName,
712
+ args: txInputs.txArgs,
713
+ value: txInputs.messageValue,
714
+ gas: gasLimit,
715
+ dataSuffix: (0, attribution_1.getAttributionSuffix)(props.config.hubChainId),
716
+ });
717
+ // 6. Wait for confirmation
718
+ const receipt = await publicClient.waitForTransactionReceipt({
719
+ hash: hash,
720
+ });
721
+ if (receipt.status === 'reverted') {
722
+ throw new Error('Transaction reverted on-chain. This may be due to insufficient gas for the LayerZero compose call or a contract error.');
723
+ }
724
+ return {
725
+ transactionHash: hash,
726
+ success: true,
727
+ };
728
+ }
729
+ // ---------------------------------------------------------------------------
730
+ // Public API: Error Classification
731
+ // ---------------------------------------------------------------------------
732
+ /**
733
+ * Check if an error indicates a missing LayerZero peer configuration.
734
+ */
735
+ function isNoPeerError(errStr) {
736
+ return errStr.includes('0xf6ff4fb7') || errStr.includes('NoPeer');
737
+ }
738
+ /**
739
+ * Classify a cross-chain contract error into a known error type.
740
+ */
741
+ function classifyCrossChainError(error) {
742
+ const err = error;
743
+ const msg = err?.message ?? String(error);
744
+ const lower = msg.toLowerCase();
745
+ if (msg.includes('0x79012fb2') || msg.includes('ERC4626ExceededMaxDeposit'))
746
+ return 'max-deposit';
747
+ if (lower.includes('nonce') || msg.includes('invalid nonce'))
748
+ return 'nonce';
749
+ if (lower.includes('user rejected') || msg.includes('User denied'))
750
+ return 'user-rejected';
751
+ if (isNoPeerError(msg))
752
+ return 'no-peer';
753
+ if (msg.includes('quoteSend') ||
754
+ lower.includes('layerzero') ||
755
+ lower.includes('lz fee') ||
756
+ lower.includes('oft') ||
757
+ lower.includes('unable to decode signature'))
758
+ return 'bridge-fee';
759
+ if (msg.includes('insufficient') || lower.includes('balance'))
760
+ return 'insufficient-balance';
761
+ if (lower.includes('gas'))
762
+ return 'gas';
763
+ if (lower.includes('network') || lower.includes('connection'))
764
+ return 'network';
765
+ return 'unknown';
766
+ }
767
+ // ---------------------------------------------------------------------------
768
+ // Public API: Utility helpers
769
+ // ---------------------------------------------------------------------------
770
+ /**
771
+ * Check if a user can deposit from their current chain.
772
+ */
773
+ function canDepositFromChain(config, userChainId) {
774
+ return (userChainId === config.hubChainId ||
775
+ config.spokeChainIds.includes(userChainId));
776
+ }
777
+ /**
778
+ * Check if a user can redeem from their current chain (must be hub).
779
+ */
780
+ function canRedeemFromChain(config, userChainId) {
781
+ return userChainId === config.hubChainId;
782
+ }
783
+ /**
784
+ * Check if an operation is cross-chain (needs LayerZero).
785
+ *
786
+ * An operation is cross-chain when:
787
+ * - The user's chain differs from the hub chain, OR
788
+ * - The destination chain differs from the hub chain
789
+ * (e.g. deposit from hub but receive shares on a spoke)
790
+ */
791
+ function isCrossChainOperation(config, userChainId, destinationChainId) {
792
+ if (userChainId !== config.hubChainId)
793
+ return true;
794
+ if (destinationChainId !== undefined &&
795
+ destinationChainId !== config.hubChainId)
796
+ return true;
797
+ return false;
798
+ }
799
+ /**
800
+ * Get all chain IDs that a vault supports (hub + all spokes).
801
+ */
802
+ function getAvailableChains(config) {
803
+ return [config.hubChainId, ...config.spokeChainIds];
804
+ }
805
+ /**
806
+ * Format a cross-chain error into a user-friendly message.
807
+ *
808
+ * Mirrors the `classifyCrossChainDepositError` from upshift-app but is
809
+ * generic enough for any SDK consumer.
810
+ */
811
+ function formatCrossChainError(errMsg) {
812
+ // If the error already contains specific gas fee info, show it directly
813
+ if (errMsg.includes('Cross-chain fee is') ||
814
+ errMsg.includes('Gas estimation failed'))
815
+ return errMsg;
816
+ const lower = errMsg.toLowerCase();
817
+ if (lower.includes('nonce') || errMsg.includes('invalid nonce'))
818
+ return 'Transaction nonce conflict. Please cancel any pending transactions and try again.';
819
+ if (errMsg.includes('quoteSend') ||
820
+ lower.includes('layerzero') ||
821
+ lower.includes('oft') ||
822
+ lower.includes('unable to decode signature'))
823
+ return 'Cross-chain fee estimation failed. The bridge may be temporarily unavailable — please try again.';
824
+ if (lower.includes('user rejected'))
825
+ return 'Transaction cancelled by user.';
826
+ if (lower.includes('hub chain'))
827
+ return errMsg; // Already descriptive
828
+ if (errMsg.includes('reverted'))
829
+ return 'Transaction reverted on-chain. This may be due to insufficient gas or a contract error.';
830
+ return 'Cross-chain transaction failed. Please try again.';
831
+ }
832
+ /**
833
+ * Get the LayerZero Scan URL for tracking a cross-chain message.
834
+ */
835
+ function getLayerZeroScanUrl(txHash, testnet = false) {
836
+ const baseUrl = testnet
837
+ ? 'https://testnet.layerzeroscan.com'
838
+ : 'https://layerzeroscan.com';
839
+ return `${baseUrl}/tx/${txHash}`;
840
+ }
841
+ /**
842
+ * Well-known LayerZero Endpoint IDs for common chains.
843
+ * SDK consumers can use these when building their ICrossChainVaultConfig.
844
+ */
845
+ exports.LAYERZERO_ENDPOINT_IDS = {
846
+ ETHEREUM: 30101,
847
+ ARBITRUM: 30110,
848
+ AVALANCHE: 30106,
849
+ BASE: 30184,
850
+ SONIC: 30332,
851
+ MONAD: 30390,
852
+ };
853
+ //# sourceMappingURL=crossChainVault.js.map