@hyperlane-xyz/sdk 41.3.1 → 43.0.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 (324) hide show
  1. package/dist/ccip/consts.d.ts.map +1 -1
  2. package/dist/ccip/consts.js +20 -0
  3. package/dist/ccip/consts.js.map +1 -1
  4. package/dist/consts/multisigIsm.d.ts.map +1 -1
  5. package/dist/consts/multisigIsm.js +32 -0
  6. package/dist/consts/multisigIsm.js.map +1 -1
  7. package/dist/contracts/contracts.d.ts.map +1 -1
  8. package/dist/contracts/contracts.js +15 -0
  9. package/dist/contracts/contracts.js.map +1 -1
  10. package/dist/contracts/contracts.test.d.ts +2 -0
  11. package/dist/contracts/contracts.test.d.ts.map +1 -0
  12. package/dist/contracts/contracts.test.js +88 -0
  13. package/dist/contracts/contracts.test.js.map +1 -0
  14. package/dist/core/types.d.ts +48 -4158
  15. package/dist/core/types.d.ts.map +1 -1
  16. package/dist/core/types.js +21 -3
  17. package/dist/core/types.js.map +1 -1
  18. package/dist/core/types.test.js +58 -0
  19. package/dist/core/types.test.js.map +1 -1
  20. package/dist/deploy/HyperlaneDeployer.js +1 -1
  21. package/dist/deploy/proxy.d.ts +8 -40
  22. package/dist/deploy/proxy.d.ts.map +1 -1
  23. package/dist/deploy/proxy.js +4 -4
  24. package/dist/deploy/proxy.js.map +1 -1
  25. package/dist/deploy/types.d.ts +1 -19
  26. package/dist/deploy/types.d.ts.map +1 -1
  27. package/dist/deploy/warp.d.ts +119 -3
  28. package/dist/deploy/warp.d.ts.map +1 -1
  29. package/dist/deploy/warp.js +696 -33
  30. package/dist/deploy/warp.js.map +1 -1
  31. package/dist/deploy/warp.test.d.ts +2 -0
  32. package/dist/deploy/warp.test.d.ts.map +1 -0
  33. package/dist/deploy/warp.test.js +358 -0
  34. package/dist/deploy/warp.test.js.map +1 -0
  35. package/dist/deploy/warpHybridPlan.d.ts +104 -0
  36. package/dist/deploy/warpHybridPlan.d.ts.map +1 -0
  37. package/dist/deploy/warpHybridPlan.js +178 -0
  38. package/dist/deploy/warpHybridPlan.js.map +1 -0
  39. package/dist/deploy/warpHybridPlan.test.d.ts +2 -0
  40. package/dist/deploy/warpHybridPlan.test.d.ts.map +1 -0
  41. package/dist/deploy/warpHybridPlan.test.js +141 -0
  42. package/dist/deploy/warpHybridPlan.test.js.map +1 -0
  43. package/dist/fee/EvmTokenFeeDeployer.d.ts.map +1 -1
  44. package/dist/fee/EvmTokenFeeDeployer.hardhat-test.js +68 -0
  45. package/dist/fee/EvmTokenFeeDeployer.hardhat-test.js.map +1 -1
  46. package/dist/fee/EvmTokenFeeDeployer.js +18 -10
  47. package/dist/fee/EvmTokenFeeDeployer.js.map +1 -1
  48. package/dist/fee/EvmTokenFeeModule.d.ts.map +1 -1
  49. package/dist/fee/EvmTokenFeeModule.hardhat-test.js +18 -8
  50. package/dist/fee/EvmTokenFeeModule.hardhat-test.js.map +1 -1
  51. package/dist/fee/EvmTokenFeeModule.js +8 -1
  52. package/dist/fee/EvmTokenFeeModule.js.map +1 -1
  53. package/dist/fee/EvmTokenFeeReader.d.ts +2 -2
  54. package/dist/fee/EvmTokenFeeReader.d.ts.map +1 -1
  55. package/dist/fee/EvmTokenFeeReader.js.map +1 -1
  56. package/dist/fee/feeConfigMapping.d.ts.map +1 -1
  57. package/dist/fee/feeConfigMapping.js +1 -0
  58. package/dist/fee/feeConfigMapping.js.map +1 -1
  59. package/dist/fee/types.d.ts +147 -646
  60. package/dist/fee/types.d.ts.map +1 -1
  61. package/dist/fee/types.js +26 -17
  62. package/dist/fee/types.js.map +1 -1
  63. package/dist/fork/types.d.ts +125 -1924
  64. package/dist/fork/types.d.ts.map +1 -1
  65. package/dist/fork/types.js +3 -3
  66. package/dist/fork/types.js.map +1 -1
  67. package/dist/gas/oracle/types.d.ts +5 -56
  68. package/dist/gas/oracle/types.d.ts.map +1 -1
  69. package/dist/hook/EvmHookModule.d.ts +1 -1
  70. package/dist/hook/EvmHookModule.d.ts.map +1 -1
  71. package/dist/hook/EvmHookModule.hardhat-test.js +12 -1
  72. package/dist/hook/EvmHookModule.hardhat-test.js.map +1 -1
  73. package/dist/hook/EvmHookModule.js +19 -3
  74. package/dist/hook/EvmHookModule.js.map +1 -1
  75. package/dist/hook/EvmHookReader.d.ts +10 -0
  76. package/dist/hook/EvmHookReader.d.ts.map +1 -1
  77. package/dist/hook/EvmHookReader.js +92 -11
  78. package/dist/hook/EvmHookReader.js.map +1 -1
  79. package/dist/hook/EvmHookReader.test.js +155 -1
  80. package/dist/hook/EvmHookReader.test.js.map +1 -1
  81. package/dist/hook/HyperlaneHookDeployer.d.ts +12 -0
  82. package/dist/hook/HyperlaneHookDeployer.d.ts.map +1 -1
  83. package/dist/hook/HyperlaneHookDeployer.hardhat-test.d.ts +2 -0
  84. package/dist/hook/HyperlaneHookDeployer.hardhat-test.d.ts.map +1 -0
  85. package/dist/hook/HyperlaneHookDeployer.hardhat-test.js +322 -0
  86. package/dist/hook/HyperlaneHookDeployer.hardhat-test.js.map +1 -0
  87. package/dist/hook/HyperlaneHookDeployer.js +262 -5
  88. package/dist/hook/HyperlaneHookDeployer.js.map +1 -1
  89. package/dist/hook/types.d.ts +72 -2223
  90. package/dist/hook/types.d.ts.map +1 -1
  91. package/dist/hook/types.js +66 -10
  92. package/dist/hook/types.js.map +1 -1
  93. package/dist/hook/types.test.js +21 -1
  94. package/dist/hook/types.test.js.map +1 -1
  95. package/dist/hook/updates.d.ts +1 -0
  96. package/dist/hook/updates.d.ts.map +1 -1
  97. package/dist/hook/updates.js +2 -2
  98. package/dist/hook/updates.js.map +1 -1
  99. package/dist/hook/utils.d.ts +28 -1
  100. package/dist/hook/utils.d.ts.map +1 -1
  101. package/dist/hook/utils.js +89 -0
  102. package/dist/hook/utils.js.map +1 -1
  103. package/dist/ica/types.d.ts +35 -1570
  104. package/dist/ica/types.d.ts.map +1 -1
  105. package/dist/ica/types.js +1 -1
  106. package/dist/ica/types.js.map +1 -1
  107. package/dist/index.d.ts +7 -6
  108. package/dist/index.d.ts.map +1 -1
  109. package/dist/index.js +7 -6
  110. package/dist/index.js.map +1 -1
  111. package/dist/ism/EvmIsmModule.d.ts +29 -3
  112. package/dist/ism/EvmIsmModule.d.ts.map +1 -1
  113. package/dist/ism/EvmIsmModule.hardhat-test.js +512 -3
  114. package/dist/ism/EvmIsmModule.hardhat-test.js.map +1 -1
  115. package/dist/ism/EvmIsmModule.js +239 -21
  116. package/dist/ism/EvmIsmModule.js.map +1 -1
  117. package/dist/ism/EvmIsmReader.d.ts.map +1 -1
  118. package/dist/ism/EvmIsmReader.js +77 -1
  119. package/dist/ism/EvmIsmReader.js.map +1 -1
  120. package/dist/ism/EvmIsmReader.test.js +245 -4
  121. package/dist/ism/EvmIsmReader.test.js.map +1 -1
  122. package/dist/ism/HyperlaneIsmFactory.d.ts +17 -5
  123. package/dist/ism/HyperlaneIsmFactory.d.ts.map +1 -1
  124. package/dist/ism/HyperlaneIsmFactory.hardhat-test.js +431 -4
  125. package/dist/ism/HyperlaneIsmFactory.hardhat-test.js.map +1 -1
  126. package/dist/ism/HyperlaneIsmFactory.js +121 -11
  127. package/dist/ism/HyperlaneIsmFactory.js.map +1 -1
  128. package/dist/ism/delayedFlow.d.ts +31 -0
  129. package/dist/ism/delayedFlow.d.ts.map +1 -0
  130. package/dist/ism/delayedFlow.js +33 -0
  131. package/dist/ism/delayedFlow.js.map +1 -0
  132. package/dist/ism/types.d.ts +86 -237
  133. package/dist/ism/types.d.ts.map +1 -1
  134. package/dist/ism/types.js +270 -20
  135. package/dist/ism/types.js.map +1 -1
  136. package/dist/ism/types.test.js +433 -1
  137. package/dist/ism/types.test.js.map +1 -1
  138. package/dist/ism/utils.d.ts.map +1 -1
  139. package/dist/ism/utils.js +113 -3
  140. package/dist/ism/utils.js.map +1 -1
  141. package/dist/ism/utils.test.js +44 -0
  142. package/dist/ism/utils.test.js.map +1 -1
  143. package/dist/metadata/agentConfig.d.ts +537 -10384
  144. package/dist/metadata/agentConfig.d.ts.map +1 -1
  145. package/dist/metadata/agentConfig.js +14 -16
  146. package/dist/metadata/agentConfig.js.map +1 -1
  147. package/dist/metadata/chainMetadataTypes.d.ts +70 -4678
  148. package/dist/metadata/chainMetadataTypes.d.ts.map +1 -1
  149. package/dist/metadata/chainMetadataTypes.js +5 -5
  150. package/dist/metadata/chainMetadataTypes.js.map +1 -1
  151. package/dist/metadata/customZodTypes.d.ts +5 -5
  152. package/dist/metadata/customZodTypes.d.ts.map +1 -1
  153. package/dist/metadata/customZodTypes.js +4 -5
  154. package/dist/metadata/customZodTypes.js.map +1 -1
  155. package/dist/metadata/deploymentArtifacts.d.ts +1 -13
  156. package/dist/metadata/deploymentArtifacts.d.ts.map +1 -1
  157. package/dist/metadata/matchingList.d.ts +12 -40
  158. package/dist/metadata/matchingList.d.ts.map +1 -1
  159. package/dist/metadata/warpRouteConfig.d.ts +8 -114
  160. package/dist/metadata/warpRouteConfig.d.ts.map +1 -1
  161. package/dist/metadata/warpRouteConfig.js +3 -3
  162. package/dist/metadata/warpRouteConfig.js.map +1 -1
  163. package/dist/middleware/account/InterchainAccount.d.ts.map +1 -1
  164. package/dist/middleware/account/InterchainAccount.js +12 -8
  165. package/dist/middleware/account/InterchainAccount.js.map +1 -1
  166. package/dist/middleware/account/InterchainAccount.test.js +22 -3
  167. package/dist/middleware/account/InterchainAccount.test.js.map +1 -1
  168. package/dist/middleware/account/icaCalls.d.ts +13 -145
  169. package/dist/middleware/account/icaCalls.d.ts.map +1 -1
  170. package/dist/middleware/account/types.d.ts +11 -105
  171. package/dist/middleware/account/types.d.ts.map +1 -1
  172. package/dist/predicate/PredicateApiClient.d.ts +1 -11
  173. package/dist/predicate/PredicateApiClient.d.ts.map +1 -1
  174. package/dist/providers/MultiProvider.d.ts.map +1 -1
  175. package/dist/providers/MultiProvider.js +3 -2
  176. package/dist/providers/MultiProvider.js.map +1 -1
  177. package/dist/providers/MultiProviderAdapter.d.ts +3 -3
  178. package/dist/providers/MultiProviderAdapter.d.ts.map +1 -1
  179. package/dist/providers/MultiProviderAdapter.js +2 -1
  180. package/dist/providers/MultiProviderAdapter.js.map +1 -1
  181. package/dist/providers/SmartProvider/HyperlaneJsonRpcProvider.d.ts.map +1 -1
  182. package/dist/providers/SmartProvider/HyperlaneJsonRpcProvider.js +4 -0
  183. package/dist/providers/SmartProvider/HyperlaneJsonRpcProvider.js.map +1 -1
  184. package/dist/providers/transactionFeeEstimators.d.ts +16 -9
  185. package/dist/providers/transactionFeeEstimators.d.ts.map +1 -1
  186. package/dist/providers/transactionFeeEstimators.js +66 -17
  187. package/dist/providers/transactionFeeEstimators.js.map +1 -1
  188. package/dist/providers/transactionFeeEstimators.test.js +153 -1
  189. package/dist/providers/transactionFeeEstimators.test.js.map +1 -1
  190. package/dist/providers/transactions/submitter/builder/types.d.ts +13 -280
  191. package/dist/providers/transactions/submitter/builder/types.d.ts.map +1 -1
  192. package/dist/providers/transactions/submitter/builder/types.js +2 -4
  193. package/dist/providers/transactions/submitter/builder/types.js.map +1 -1
  194. package/dist/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.d.ts +2 -0
  195. package/dist/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.d.ts.map +1 -1
  196. package/dist/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.js +15 -7
  197. package/dist/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.js.map +1 -1
  198. package/dist/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.test.d.ts +2 -0
  199. package/dist/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.test.d.ts.map +1 -0
  200. package/dist/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.test.js +145 -0
  201. package/dist/providers/transactions/submitter/ethersV5/EV5GnosisSafeTxSubmitter.test.js.map +1 -0
  202. package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.d.ts +14 -1
  203. package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.d.ts.map +1 -1
  204. package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.js +46 -5
  205. package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.js.map +1 -1
  206. package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.test.d.ts +2 -0
  207. package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.test.d.ts.map +1 -0
  208. package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.test.js +125 -0
  209. package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.test.js.map +1 -0
  210. package/dist/providers/transactions/submitter/ethersV5/types.d.ts +30 -102
  211. package/dist/providers/transactions/submitter/ethersV5/types.d.ts.map +1 -1
  212. package/dist/providers/transactions/submitter/ethersV5/types.js +3 -21
  213. package/dist/providers/transactions/submitter/ethersV5/types.js.map +1 -1
  214. package/dist/providers/transactions/submitter/types.d.ts +5 -51
  215. package/dist/providers/transactions/submitter/types.d.ts.map +1 -1
  216. package/dist/providers/transactions/types.d.ts +1 -9
  217. package/dist/providers/transactions/types.d.ts.map +1 -1
  218. package/dist/router/HyperlaneRouterDeployer.d.ts +3 -1
  219. package/dist/router/HyperlaneRouterDeployer.d.ts.map +1 -1
  220. package/dist/router/HyperlaneRouterDeployer.js +4 -2
  221. package/dist/router/HyperlaneRouterDeployer.js.map +1 -1
  222. package/dist/router/types.d.ts +33 -1824
  223. package/dist/router/types.d.ts.map +1 -1
  224. package/dist/router/types.js +4 -4
  225. package/dist/router/types.js.map +1 -1
  226. package/dist/rpc/evm/EvmEventLogsReader.d.ts +19 -27
  227. package/dist/rpc/evm/EvmEventLogsReader.d.ts.map +1 -1
  228. package/dist/rpc/evm/EvmEventLogsReader.js +23 -0
  229. package/dist/rpc/evm/EvmEventLogsReader.js.map +1 -1
  230. package/dist/rpc/evm/EvmEventLogsReader.test.js +53 -1
  231. package/dist/rpc/evm/EvmEventLogsReader.test.js.map +1 -1
  232. package/dist/signers/turnkeyClient.d.ts +1 -15
  233. package/dist/signers/turnkeyClient.d.ts.map +1 -1
  234. package/dist/test/testUtils.d.ts +1 -1
  235. package/dist/test/testUtils.d.ts.map +1 -1
  236. package/dist/test/testUtils.js +17 -0
  237. package/dist/test/testUtils.js.map +1 -1
  238. package/dist/test/testUtils.test.d.ts +2 -0
  239. package/dist/test/testUtils.test.d.ts.map +1 -0
  240. package/dist/test/testUtils.test.js +28 -0
  241. package/dist/test/testUtils.test.js.map +1 -0
  242. package/dist/timelock/types.d.ts +8 -32
  243. package/dist/timelock/types.d.ts.map +1 -1
  244. package/dist/token/EvmWarpModule.d.ts +44 -8
  245. package/dist/token/EvmWarpModule.d.ts.map +1 -1
  246. package/dist/token/EvmWarpModule.hardhat-test.js +574 -4
  247. package/dist/token/EvmWarpModule.hardhat-test.js.map +1 -1
  248. package/dist/token/EvmWarpModule.js +237 -25
  249. package/dist/token/EvmWarpModule.js.map +1 -1
  250. package/dist/token/EvmWarpRouteReader.d.ts.map +1 -1
  251. package/dist/token/EvmWarpRouteReader.js +36 -19
  252. package/dist/token/EvmWarpRouteReader.js.map +1 -1
  253. package/dist/token/EvmWarpRouteReader.test.js +219 -2
  254. package/dist/token/EvmWarpRouteReader.test.js.map +1 -1
  255. package/dist/token/EvmXERC20Reader.d.ts +9 -21
  256. package/dist/token/EvmXERC20Reader.d.ts.map +1 -1
  257. package/dist/token/EvmXERC20Reader.js +24 -48
  258. package/dist/token/EvmXERC20Reader.js.map +1 -1
  259. package/dist/token/EvmXERC20Reader.test.js +97 -0
  260. package/dist/token/EvmXERC20Reader.test.js.map +1 -1
  261. package/dist/token/ITokenMetadata.d.ts +12 -130
  262. package/dist/token/ITokenMetadata.d.ts.map +1 -1
  263. package/dist/token/ITokenMetadata.js +2 -2
  264. package/dist/token/ITokenMetadata.js.map +1 -1
  265. package/dist/token/TokenConnection.d.ts +3 -35
  266. package/dist/token/TokenConnection.d.ts.map +1 -1
  267. package/dist/token/configUtils.d.ts +17 -7
  268. package/dist/token/configUtils.d.ts.map +1 -1
  269. package/dist/token/configUtils.js +124 -0
  270. package/dist/token/configUtils.js.map +1 -1
  271. package/dist/token/configUtils.test.js +212 -11
  272. package/dist/token/configUtils.test.js.map +1 -1
  273. package/dist/token/deploy.d.ts +3 -1
  274. package/dist/token/deploy.d.ts.map +1 -1
  275. package/dist/token/deploy.hardhat-test.js +617 -6
  276. package/dist/token/deploy.hardhat-test.js.map +1 -1
  277. package/dist/token/deploy.js +25 -6
  278. package/dist/token/deploy.js.map +1 -1
  279. package/dist/token/types.d.ts +4373 -25929
  280. package/dist/token/types.d.ts.map +1 -1
  281. package/dist/token/types.js +10 -12
  282. package/dist/token/types.js.map +1 -1
  283. package/dist/token/types.test.js +48 -0
  284. package/dist/token/types.test.js.map +1 -1
  285. package/dist/token/warpCheck.d.ts.map +1 -1
  286. package/dist/token/warpCheck.js +36 -1
  287. package/dist/token/warpCheck.js.map +1 -1
  288. package/dist/token/xerc20-abi.d.ts +31 -0
  289. package/dist/token/xerc20-abi.d.ts.map +1 -1
  290. package/dist/token/xerc20-abi.js +40 -0
  291. package/dist/token/xerc20-abi.js.map +1 -1
  292. package/dist/token/xerc20-limits.d.ts +63 -0
  293. package/dist/token/xerc20-limits.d.ts.map +1 -0
  294. package/dist/token/xerc20-limits.js +80 -0
  295. package/dist/token/xerc20-limits.js.map +1 -0
  296. package/dist/token/xerc20.d.ts +30 -1
  297. package/dist/token/xerc20.d.ts.map +1 -1
  298. package/dist/token/xerc20.js +137 -54
  299. package/dist/token/xerc20.js.map +1 -1
  300. package/dist/token/xerc20.test.js +461 -169
  301. package/dist/token/xerc20.test.js.map +1 -1
  302. package/dist/types.d.ts +6 -36
  303. package/dist/types.d.ts.map +1 -1
  304. package/dist/types.js +1 -1
  305. package/dist/types.js.map +1 -1
  306. package/dist/utils/cosmos.d.ts +113 -844
  307. package/dist/utils/cosmos.d.ts.map +1 -1
  308. package/dist/utils/cosmos.js +50 -91
  309. package/dist/utils/cosmos.js.map +1 -1
  310. package/dist/utils/ism.d.ts +167 -1
  311. package/dist/utils/ism.d.ts.map +1 -1
  312. package/dist/utils/ism.js +412 -2
  313. package/dist/utils/ism.js.map +1 -1
  314. package/dist/utils/ism.test.d.ts +2 -0
  315. package/dist/utils/ism.test.d.ts.map +1 -0
  316. package/dist/utils/ism.test.js +372 -0
  317. package/dist/utils/ism.test.js.map +1 -0
  318. package/dist/utils/schemas.d.ts +3 -3
  319. package/dist/utils/schemas.d.ts.map +1 -1
  320. package/dist/utils/schemas.js +2 -1
  321. package/dist/utils/schemas.js.map +1 -1
  322. package/dist/warp/types.d.ts +25 -367
  323. package/dist/warp/types.d.ts.map +1 -1
  324. package/package.json +11 -11
@@ -1,22 +1,26 @@
1
1
  import { expect } from 'chai';
2
2
  import { ethers } from 'ethers';
3
3
  import hre from 'hardhat';
4
- import { AtomicLocalRebalancingBridge__factory, CrossCollateralRouter__factory, ERC20Test__factory, GasRouter__factory, HypXERC20Lockbox__factory, LinearFee__factory, MailboxClient__factory, ProxyAdmin__factory, RateLimitedIsm__factory, RoutingFee__factory, StaticAggregationIsm__factory, TokenRouter__factory, TransparentUpgradeableProxy__factory, XERC20LockboxTest__factory, XERC20Test__factory, } from '@hyperlane-xyz/core';
5
- import { ProtocolType, assert, addressToBytes32, deepCopy, eqAddress, isZeroishAddress, objMap, } from '@hyperlane-xyz/utils';
4
+ import sinon from 'sinon';
5
+ import { AtomicLocalRebalancingBridge__factory, CrossCollateralRouter__factory, DelayedFlowRouterHookIsm__factory, ERC20Test__factory, GasRouter__factory, HypXERC20Lockbox__factory, LinearFee__factory, MailboxClient__factory, NetFlowRateLimitedHookIsm__factory, ProxyAdmin__factory, RateLimitedIsm__factory, RoutingFee__factory, StaticAggregationIsm__factory, TokenRouter__factory, TransparentUpgradeableProxy__factory, XERC20LockboxTest__factory, XERC20Test__factory, XERC20VSTest__factory, } from '@hyperlane-xyz/core';
6
+ import { ProtocolType, addressToBytes32, assert, deepCopy, eqAddress, isNullish, isZeroishAddress, objMap, } from '@hyperlane-xyz/utils';
6
7
  import { TestChainName } from '../consts/testChains.js';
7
8
  import { TestCoreDeployer } from '../core/TestCoreDeployer.js';
8
9
  import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer.js';
9
- import { executeWarpDeploy } from '../deploy/warp.js';
10
+ import { enrollCrossChainRouters, executeWarpDeploy } from '../deploy/warp.js';
10
11
  import { TokenFeeType } from '../fee/types.js';
12
+ import { HookType } from '../hook/types.js';
11
13
  import { HyperlaneIsmFactory } from '../ism/HyperlaneIsmFactory.js';
12
14
  import { IsmType, } from '../ism/types.js';
13
15
  import { MultiProvider } from '../providers/MultiProvider.js';
16
+ import { randomAddress } from '../test/testUtils.js';
17
+ import { collectHybridIsmNodes } from '../utils/ism.js';
14
18
  import { EvmWarpRouteReader } from './EvmWarpRouteReader.js';
15
19
  import { TokenStandard } from './TokenStandard.js';
16
20
  import { TokenType } from './config.js';
17
21
  import { checkWarpRouteDeployConfig } from './warpCheck.js';
18
22
  import { HypERC20Deployer } from './deploy.js';
19
- import { isAtomicLocalRebalancingBridgeTokenConfig, isDepositAddressTokenConfig, } from './types.js';
23
+ import { XERC20Type, isAtomicLocalRebalancingBridgeTokenConfig, isDepositAddressTokenConfig, } from './types.js';
20
24
  const chain = TestChainName.test1;
21
25
  function addOverridesToConfig(config, ownerOverrides) {
22
26
  return Object.fromEntries(Object.entries(config).map(([chain, config]) => {
@@ -29,6 +33,12 @@ function addOverridesToConfig(config, ownerOverrides) {
29
33
  ];
30
34
  }));
31
35
  }
36
+ function assertEvmTransaction(transaction, chain) {
37
+ assert('to' in transaction &&
38
+ typeof transaction.to === 'string' &&
39
+ 'data' in transaction &&
40
+ typeof transaction.data === 'string', `Expected an EVM transaction for ${chain}`);
41
+ }
32
42
  describe('TokenDeployer', async () => {
33
43
  let signer;
34
44
  let deployer;
@@ -289,12 +299,25 @@ describe('TokenDeployer', async () => {
289
299
  chainName: currentChain,
290
300
  })),
291
301
  });
302
+ // XERC20Test answers mintingMaxLimitOf/burningMaxLimitOf with the uint256
303
+ // max for every bridge, so a deploy config declaring no limits reads as
304
+ // drift against what the token holds.
305
+ const MAX_UINT256 = '115792089237316195423570985008687907853269984665640564039457584007913129639935';
292
306
  beforeEach(async () => {
293
- // @ts-expect-error - Test assigns varying token types to config
294
307
  config[chain] = {
295
308
  ...config[chain],
296
309
  type,
310
+ // @ts-expect-error - Test assigns varying token types to config
297
311
  token: token(),
312
+ xERC20: type === TokenType.XERC20
313
+ ? {
314
+ warpRouteLimits: {
315
+ type: XERC20Type.Standard,
316
+ mint: MAX_UINT256,
317
+ burn: MAX_UINT256,
318
+ },
319
+ }
320
+ : undefined,
298
321
  };
299
322
  contractsMap = await deployer.deploy(config);
300
323
  });
@@ -563,10 +586,10 @@ describe('TokenDeployer', async () => {
563
586
  reader = new EvmWarpRouteReader(multiProvider, TestChainName.test1);
564
587
  });
565
588
  beforeEach(async () => {
566
- // @ts-expect-error - Test assigns varying token types to config
567
589
  config[chain] = {
568
590
  ...config[chain],
569
591
  type,
592
+ // @ts-expect-error - Test assigns varying token types to config
570
593
  token: token(),
571
594
  };
572
595
  const warpRoute = await deployer.deploy(config);
@@ -578,6 +601,153 @@ describe('TokenDeployer', async () => {
578
601
  });
579
602
  });
580
603
  }
604
+ describe('checkWarpRouteDeployConfig for a Velodrome xERC20', () => {
605
+ // The values the CLI e2e uses, which XERC20VSTest accepts for addBridge.
606
+ const BUFFER_CAP = '1000000000000000000000';
607
+ const RATE_LIMIT_PER_SECOND = '1000000000000000000';
608
+ const VELO_LIMITS = {
609
+ type: XERC20Type.Velo,
610
+ bufferCap: BUFFER_CAP,
611
+ rateLimitPerSecond: RATE_LIMIT_PER_SECOND,
612
+ };
613
+ let sandbox;
614
+ let veloToken;
615
+ let extraBridge;
616
+ let contractsMap;
617
+ const veloConfig = (extraBridges) => ({
618
+ ...config,
619
+ [chain]: {
620
+ ...config[chain],
621
+ type: TokenType.XERC20,
622
+ token: veloToken.address,
623
+ xERC20: { warpRouteLimits: VELO_LIMITS, extraBridges },
624
+ },
625
+ });
626
+ // CAST: the check only reads chainName and addressOrDenom off each token,
627
+ // and filling in the rest of WarpCoreConfig's token metadata would state
628
+ // things about the deployment this test does not know.
629
+ const getWarpCoreConfig = () => ({
630
+ tokens: Object.keys(config).map((currentChain) => ({
631
+ addressOrDenom: contractsMap[currentChain][currentChain === chain
632
+ ? TokenType.XERC20
633
+ : config[currentChain].type].address,
634
+ chainName: currentChain,
635
+ })),
636
+ });
637
+ beforeEach(async () => {
638
+ sandbox = sinon.createSandbox();
639
+ // The test chains declare a placeholder Etherscan explorer, and the bridge
640
+ // scan would spend a network round trip on it before falling through. The
641
+ // RPC is the path this exercises.
642
+ sandbox.stub(multiProvider, 'tryGetEvmExplorerMetadataList').returns([]);
643
+ const { name, decimals, symbol } = config[chain];
644
+ assert(name && symbol && !isNullish(decimals), `Missing token metadata for ${chain}`);
645
+ veloToken = await new XERC20VSTest__factory(signer).deploy(name, symbol, totalSupply, decimals);
646
+ contractsMap = await deployer.deploy(veloConfig());
647
+ // The route's own router is a bridge of the token like any other, and the
648
+ // extra bridge is an address nothing but the token's events knows about.
649
+ extraBridge = randomAddress();
650
+ for (const bridge of [
651
+ contractsMap[chain][TokenType.XERC20].address,
652
+ extraBridge,
653
+ ]) {
654
+ await veloToken
655
+ .addBridge({
656
+ bridge,
657
+ bufferCap: BUFFER_CAP,
658
+ rateLimitPerSecond: RATE_LIMIT_PER_SECOND,
659
+ })
660
+ .then((tx) => tx.wait());
661
+ }
662
+ });
663
+ afterEach(() => {
664
+ sandbox.restore();
665
+ });
666
+ it('reports no violations for a bridge discovered from the token', async () => {
667
+ const result = await checkWarpRouteDeployConfig({
668
+ multiProvider,
669
+ warpCoreConfig: getWarpCoreConfig(),
670
+ warpDeployConfig: veloConfig([
671
+ { lockbox: extraBridge, limits: VELO_LIMITS },
672
+ ]),
673
+ });
674
+ expect(result.diff).to.deep.equal({});
675
+ expect(result.violations).to.deep.equal([]);
676
+ expect(result.isValid).to.equal(true);
677
+ });
678
+ // Discovery follows the order the token announced its bridges in and the
679
+ // deploy config follows whoever wrote it. Comparing the two index by index
680
+ // reports the same set of bridges in two orders as drift.
681
+ it('reports no violations when the deploy config names the bridges in another order', async () => {
682
+ const laterBridge = randomAddress();
683
+ await veloToken
684
+ .addBridge({
685
+ bridge: laterBridge,
686
+ bufferCap: BUFFER_CAP,
687
+ rateLimitPerSecond: RATE_LIMIT_PER_SECOND,
688
+ })
689
+ .then((tx) => tx.wait());
690
+ const result = await checkWarpRouteDeployConfig({
691
+ multiProvider,
692
+ warpCoreConfig: getWarpCoreConfig(),
693
+ // Announced extraBridge first, so name it last.
694
+ warpDeployConfig: veloConfig([
695
+ { lockbox: laterBridge, limits: VELO_LIMITS },
696
+ { lockbox: extraBridge, limits: VELO_LIMITS },
697
+ ]),
698
+ });
699
+ expect(result.diff).to.deep.equal({});
700
+ expect(result.violations).to.deep.equal([]);
701
+ expect(result.isValid).to.equal(true);
702
+ });
703
+ // Guards the assertion above against passing because the declared limits
704
+ // were echoed back rather than read: declare the same bridge with limits
705
+ // the token does not hold and the check has to report it.
706
+ it('reports drift when the declared limits are not the ones the token holds', async () => {
707
+ const result = await checkWarpRouteDeployConfig({
708
+ multiProvider,
709
+ warpCoreConfig: getWarpCoreConfig(),
710
+ warpDeployConfig: veloConfig([
711
+ {
712
+ lockbox: extraBridge,
713
+ limits: {
714
+ type: XERC20Type.Velo,
715
+ bufferCap: '1',
716
+ rateLimitPerSecond: '1',
717
+ },
718
+ },
719
+ ]),
720
+ });
721
+ expect(result.isValid).to.equal(false);
722
+ expect(result.violations.map(({ chain: violationChain, name }) => ({
723
+ chain: violationChain,
724
+ name,
725
+ }))).to.deep.equal([
726
+ { chain, name: 'xERC20.extraBridges.0.limits.bufferCap' },
727
+ { chain, name: 'xERC20.extraBridges.0.limits.rateLimitPerSecond' },
728
+ ]);
729
+ });
730
+ // A bridge on chain that the deploy config does not name is drift the check
731
+ // exists to surface, and it is only visible because the bridge set is read
732
+ // from the token rather than from the config.
733
+ it('reports a bridge the deploy config does not name', async () => {
734
+ const result = await checkWarpRouteDeployConfig({
735
+ multiProvider,
736
+ warpCoreConfig: getWarpCoreConfig(),
737
+ warpDeployConfig: veloConfig(),
738
+ });
739
+ expect(result.isValid).to.equal(false);
740
+ // isValid alone is true of any drift on any field, so the violation has
741
+ // to name the bridge for this to be about the bridge.
742
+ expect(result.violations.map(({ chain: violationChain, name, actual }) => ({
743
+ chain: violationChain,
744
+ name,
745
+ namesTheBridge: actual.includes(extraBridge.toLowerCase()),
746
+ }))).to.deep.equal([
747
+ { chain, name: 'xERC20.extraBridges', namesTheBridge: true },
748
+ ]);
749
+ });
750
+ });
581
751
  describe('RateLimitedIsm with non-deployer warp owner', () => {
582
752
  let ismDeployer;
583
753
  let ismFactory;
@@ -683,6 +853,447 @@ describe('TokenDeployer', async () => {
683
853
  expect((await router.owner()).toLowerCase()).to.equal(warpOwner.toLowerCase());
684
854
  });
685
855
  });
856
+ describe('Hybrid hook/ISM deferred deploy', () => {
857
+ let registryAddresses;
858
+ const remoteChain = TestChainName.test2;
859
+ before(async () => {
860
+ const pfd = new HyperlaneProxyFactoryDeployer(multiProvider);
861
+ const factories = await pfd.deploy(multiProvider.mapKnownChains(() => ({})));
862
+ registryAddresses = {};
863
+ for (const chainName of [chain, remoteChain]) {
864
+ registryAddresses[chainName] = {
865
+ ...objMap(factories[chainName], (_, contract) => contract.address),
866
+ mailbox: coreApp.getContracts(chainName).mailbox.address,
867
+ };
868
+ }
869
+ // DELAYED_FLOW_ROUTER refuses nonce-0 mailboxes. Seed both test
870
+ // mailboxes before any staged deploy spends gas.
871
+ for (const [origin, destination] of [
872
+ [chain, remoteChain],
873
+ [remoteChain, chain],
874
+ ]) {
875
+ const mailbox = coreApp.getContracts(origin).mailbox;
876
+ if ((await mailbox.nonce()) !== 0)
877
+ continue;
878
+ const recipient = addressToBytes32(signer.address);
879
+ const quote = await mailbox['quoteDispatch(uint32,bytes32,bytes)'](multiProvider.getDomainId(destination), recipient, '0x');
880
+ await multiProvider.handleTx(origin, mailbox['dispatch(uint32,bytes32,bytes)'](multiProvider.getDomainId(destination), recipient, '0x', { value: quote }));
881
+ }
882
+ });
883
+ function delayedFlowTree(owner) {
884
+ return {
885
+ type: IsmType.AGGREGATION,
886
+ threshold: 2,
887
+ modules: [
888
+ {
889
+ type: IsmType.TRUSTED_RELAYER,
890
+ relayer: signer.address,
891
+ },
892
+ {
893
+ type: IsmType.DELAYED_FLOW_ROUTER,
894
+ thresholdBps: 10000,
895
+ maxDelay: 3600,
896
+ duration: 86400n,
897
+ owner,
898
+ },
899
+ ],
900
+ };
901
+ }
902
+ const hookView = (ism) => {
903
+ const nodes = collectHybridIsmNodes(ism);
904
+ assert(nodes.length === 1, `Expected one hybrid node, found ${nodes.length}`);
905
+ const node = nodes[0];
906
+ return node.type === IsmType.DELAYED_FLOW_ROUTER
907
+ ? { ...node, type: HookType.DELAYED_FLOW_ROUTER }
908
+ : { ...node, type: HookType.NET_FLOW_RATE_LIMITED };
909
+ };
910
+ async function deployHybridRoute(warpConfig, isms) {
911
+ const planned = objMap(warpConfig, (chainName, chainConfig) => {
912
+ const ism = isms[chainName];
913
+ assert(ism, `Missing hybrid ISM tree for ${chainName}`);
914
+ return {
915
+ ...chainConfig,
916
+ interchainSecurityModule: ism,
917
+ hook: hookView(ism),
918
+ };
919
+ });
920
+ const routers = await executeWarpDeploy(planned, multiProvider, {}, registryAddresses, {});
921
+ return objMap(routers, (_, address) => ({
922
+ synthetic: { address },
923
+ }));
924
+ }
925
+ async function expectDeployRejection(promise, message) {
926
+ const error = await promise.then(() => undefined, (thrown) => thrown instanceof Error ? thrown : new Error(String(thrown)));
927
+ assert(error, 'Expected staged deploy to reject');
928
+ expect(error.message).to.include(message);
929
+ }
930
+ it('wires a nested delayedFlowRouterHookIsm as ISM member AND hook, keeping deployer ownership for enrollment', async () => {
931
+ const warpOwner = ethers.Wallet.createRandom().address;
932
+ const warpConfig = {
933
+ [chain]: {
934
+ ...config[chain],
935
+ type: TokenType.synthetic,
936
+ owner: warpOwner,
937
+ },
938
+ };
939
+ const contracts = await deployHybridRoute(warpConfig, {
940
+ [chain]: delayedFlowTree(warpOwner),
941
+ });
942
+ const routerAddress = contracts[chain].synthetic.address;
943
+ const provider = multiProvider.getProvider(chain);
944
+ const tokenClient = MailboxClient__factory.connect(routerAddress, provider);
945
+ const ismAddress = await tokenClient.interchainSecurityModule();
946
+ expect(isZeroishAddress(ismAddress)).to.be.false;
947
+ // hook must be wired to the hybrid instance
948
+ const hookAddress = await tokenClient.hook();
949
+ expect(isZeroishAddress(hookAddress)).to.be.false;
950
+ // the hook is the DelayedFlowRouterHookIsm paired with this router
951
+ const delayedIsm = DelayedFlowRouterHookIsm__factory.connect(hookAddress, provider);
952
+ expect(eqAddress(await delayedIsm.warpRouter(), routerAddress)).to.be
953
+ .true;
954
+ expect(await delayedIsm.maxDelay()).to.equal(3600);
955
+ // the same instance must be a member of the aggregation ISM
956
+ const aggregationIsm = StaticAggregationIsm__factory.connect(ismAddress, provider);
957
+ const [modules] = await aggregationIsm.modulesAndThreshold(ethers.constants.AddressZero);
958
+ expect(modules.map((m) => m.toLowerCase())).to.include(hookAddress.toLowerCase());
959
+ // Both stay with the deployer until the final router-enrollment pass.
960
+ expect(eqAddress(await delayedIsm.owner(), signer.address)).to.be.true;
961
+ const router = GasRouter__factory.connect(routerAddress, provider);
962
+ expect((await router.owner()).toLowerCase()).to.equal(signer.address.toLowerCase());
963
+ });
964
+ it('does not proceed to the ISM phase when installing the hook fails', async () => {
965
+ const setHookSighash = MailboxClient__factory.createInterface().getSighash('setHook');
966
+ const setIsmSighash = MailboxClient__factory.createInterface().getSighash('setInterchainSecurityModule');
967
+ let sawIsmInstall = false;
968
+ const sendTransaction = multiProvider.sendTransaction.bind(multiProvider);
969
+ const sandbox = sinon.createSandbox();
970
+ sandbox
971
+ .stub(multiProvider, 'sendTransaction')
972
+ .callsFake(async (txChain, txProm, options) => {
973
+ const tx = await txProm;
974
+ if (typeof tx.data === 'string' &&
975
+ tx.data.startsWith(setIsmSighash)) {
976
+ sawIsmInstall = true;
977
+ }
978
+ if (typeof tx.data === 'string' &&
979
+ tx.data.startsWith(setHookSighash)) {
980
+ throw new Error('execution reverted: setHook');
981
+ }
982
+ return sendTransaction(txChain, tx, options);
983
+ });
984
+ try {
985
+ const warpConfig = {
986
+ [chain]: {
987
+ ...config[chain],
988
+ type: TokenType.synthetic,
989
+ owner: signer.address,
990
+ },
991
+ };
992
+ const error = await deployHybridRoute(warpConfig, {
993
+ [chain]: delayedFlowTree(signer.address),
994
+ }).then(() => undefined, (thrown) => thrown instanceof Error ? thrown : new Error(String(thrown)));
995
+ assert(error, 'expected the deploy to fail');
996
+ expect(error.message).to.include('execution reverted: setHook');
997
+ expect(sawIsmInstall).to.be.false;
998
+ }
999
+ finally {
1000
+ sandbox.restore();
1001
+ }
1002
+ });
1003
+ it('rejects a hybrid on an unmeterable router type before staged deploy', async () => {
1004
+ const warpConfig = {
1005
+ [chain]: {
1006
+ ...config[chain],
1007
+ type: TokenType.collateralVault,
1008
+ token: erc20.address,
1009
+ owner: signer.address,
1010
+ },
1011
+ };
1012
+ await expectDeployRejection(deployHybridRoute(warpConfig, {
1013
+ [chain]: delayedFlowTree(signer.address),
1014
+ }), 'cannot meter');
1015
+ });
1016
+ it('rejects a hybrid combined with a predicateWrapper before staged deploy', async () => {
1017
+ const warpConfig = {
1018
+ [chain]: {
1019
+ ...config[chain],
1020
+ type: TokenType.synthetic,
1021
+ owner: signer.address,
1022
+ predicateWrapper: {
1023
+ predicateRegistry: randomAddress(),
1024
+ policyId: 'test-policy',
1025
+ owner: signer.address,
1026
+ },
1027
+ },
1028
+ };
1029
+ await expectDeployRejection(deployHybridRoute(warpConfig, {
1030
+ [chain]: delayedFlowTree(signer.address),
1031
+ }), 'both must own');
1032
+ });
1033
+ it('rejects a top-level netFlowRateLimitedHookIsm (no authenticating ISM)', async () => {
1034
+ const warpOwner = ethers.Wallet.createRandom().address;
1035
+ const warpConfig = {
1036
+ [chain]: {
1037
+ ...config[chain],
1038
+ type: TokenType.synthetic,
1039
+ owner: warpOwner,
1040
+ },
1041
+ };
1042
+ // moduleType() is NULL: alone it authenticates nothing, so a forged
1043
+ // message would mint on this synthetic leg subject only to bucket
1044
+ // capacity. The composition rule must reject it.
1045
+ const netFlowConfig = {
1046
+ type: IsmType.NET_FLOW_RATE_LIMITED,
1047
+ thresholdBps: 500,
1048
+ duration: 86400n,
1049
+ owner: warpOwner,
1050
+ };
1051
+ await expectDeployRejection(deployHybridRoute(warpConfig, { [chain]: netFlowConfig }), 'verifies flow, not message authenticity');
1052
+ });
1053
+ it('wires a netFlowRateLimitedHookIsm composed under an authenticating aggregation as both hook and ISM', async () => {
1054
+ const warpOwner = ethers.Wallet.createRandom().address;
1055
+ const warpConfig = {
1056
+ [chain]: {
1057
+ ...config[chain],
1058
+ type: TokenType.synthetic,
1059
+ owner: warpOwner,
1060
+ },
1061
+ };
1062
+ const netFlowConfig = {
1063
+ type: IsmType.AGGREGATION,
1064
+ threshold: 2,
1065
+ modules: [
1066
+ { type: IsmType.TRUSTED_RELAYER, relayer: signer.address },
1067
+ {
1068
+ type: IsmType.NET_FLOW_RATE_LIMITED,
1069
+ thresholdBps: 500,
1070
+ duration: 86400n,
1071
+ owner: warpOwner,
1072
+ },
1073
+ ],
1074
+ };
1075
+ const contracts = await deployHybridRoute(warpConfig, {
1076
+ [chain]: netFlowConfig,
1077
+ });
1078
+ const routerAddress = contracts[chain].synthetic.address;
1079
+ const provider = multiProvider.getProvider(chain);
1080
+ // Mirror the CLI's post-deploy pass. It enrolls route peers and hands
1081
+ // the router, ProxyAdmin, and shared hybrid to their final owner.
1082
+ const enrollmentTxs = await enrollCrossChainRouters({
1083
+ multiProvider,
1084
+ altVmSigners: {},
1085
+ registryAddresses,
1086
+ warpDeployConfig: warpConfig,
1087
+ }, { [chain]: routerAddress });
1088
+ for (const rawTx of enrollmentTxs[chain] ?? []) {
1089
+ assertEvmTransaction(rawTx, chain);
1090
+ await multiProvider.sendTransaction(chain, {
1091
+ to: rawTx.to,
1092
+ data: rawTx.data,
1093
+ value: rawTx.value,
1094
+ });
1095
+ }
1096
+ const tokenClient = MailboxClient__factory.connect(routerAddress, provider);
1097
+ const ismAddress = await tokenClient.interchainSecurityModule();
1098
+ const hookAddress = await tokenClient.hook();
1099
+ expect(isZeroishAddress(hookAddress)).to.be.false;
1100
+ // The hook is the hybrid leaf inside the installed aggregation, not the
1101
+ // aggregation itself.
1102
+ const [modules] = await StaticAggregationIsm__factory.connect(ismAddress, provider).modulesAndThreshold(ethers.constants.AddressZero);
1103
+ expect(modules.map((m) => m.toLowerCase())).to.include(hookAddress.toLowerCase());
1104
+ const netFlowIsm = NetFlowRateLimitedHookIsm__factory.connect(hookAddress, provider);
1105
+ expect(eqAddress(await netFlowIsm.warpRouter(), routerAddress)).to.be
1106
+ .true;
1107
+ // Ownership transfers only after all route wiring is complete.
1108
+ expect((await netFlowIsm.owner()).toLowerCase()).to.equal(warpOwner.toLowerCase());
1109
+ expect((await TokenRouter__factory.connect(routerAddress, provider).owner()).toLowerCase()).to.equal(warpOwner.toLowerCase());
1110
+ const secondEnrollmentTxs = await enrollCrossChainRouters({
1111
+ multiProvider,
1112
+ altVmSigners: {},
1113
+ registryAddresses,
1114
+ warpDeployConfig: warpConfig,
1115
+ }, { [chain]: routerAddress });
1116
+ expect(Object.keys(secondEnrollmentTxs)).to.have.length(0);
1117
+ });
1118
+ it('defaults an omitted netFlowRateLimitedHookIsm owner to the chain config owner', async () => {
1119
+ const warpOwner = ethers.Wallet.createRandom().address;
1120
+ const warpConfig = {
1121
+ [chain]: {
1122
+ ...config[chain],
1123
+ type: TokenType.synthetic,
1124
+ owner: warpOwner,
1125
+ },
1126
+ };
1127
+ const ownerlessNetFlow = {
1128
+ type: IsmType.AGGREGATION,
1129
+ threshold: 2,
1130
+ modules: [
1131
+ { type: IsmType.TRUSTED_RELAYER, relayer: signer.address },
1132
+ {
1133
+ type: IsmType.NET_FLOW_RATE_LIMITED,
1134
+ thresholdBps: 500,
1135
+ duration: 86400n,
1136
+ },
1137
+ ],
1138
+ };
1139
+ const contracts = await deployHybridRoute(warpConfig, {
1140
+ [chain]: ownerlessNetFlow,
1141
+ });
1142
+ const routerAddress = contracts[chain].synthetic.address;
1143
+ const provider = multiProvider.getProvider(chain);
1144
+ const enrollmentTxs = await enrollCrossChainRouters({
1145
+ multiProvider,
1146
+ altVmSigners: {},
1147
+ registryAddresses,
1148
+ warpDeployConfig: warpConfig,
1149
+ }, { [chain]: routerAddress });
1150
+ for (const rawTx of enrollmentTxs[chain] ?? []) {
1151
+ assertEvmTransaction(rawTx, chain);
1152
+ await multiProvider.sendTransaction(chain, {
1153
+ to: rawTx.to,
1154
+ data: rawTx.data,
1155
+ value: rawTx.value,
1156
+ });
1157
+ }
1158
+ // the hybrid leaf is the router's hook; the ISM is the aggregation
1159
+ const hookAddress = await MailboxClient__factory.connect(routerAddress, provider).hook();
1160
+ const netFlowIsm = NetFlowRateLimitedHookIsm__factory.connect(hookAddress, provider);
1161
+ // The final pass defaults an omitted hybrid owner to the chain owner.
1162
+ expect((await netFlowIsm.owner()).toLowerCase()).to.equal(warpOwner.toLowerCase());
1163
+ });
1164
+ it('enrolls delayedFlowRouterHookIsm counterparts cross-chain and transfers ownership last', async () => {
1165
+ const warpOwner = ethers.Wallet.createRandom().address;
1166
+ const warpDeployConfig = {
1167
+ [chain]: {
1168
+ ...config[chain],
1169
+ type: TokenType.synthetic,
1170
+ owner: warpOwner,
1171
+ interchainSecurityModule: delayedFlowTree(warpOwner),
1172
+ },
1173
+ [remoteChain]: {
1174
+ ...config[remoteChain],
1175
+ type: TokenType.synthetic,
1176
+ owner: warpOwner,
1177
+ interchainSecurityModule: delayedFlowTree(warpOwner),
1178
+ },
1179
+ };
1180
+ const contracts = await deployHybridRoute(warpDeployConfig, {
1181
+ [chain]: delayedFlowTree(warpOwner),
1182
+ [remoteChain]: delayedFlowTree(warpOwner),
1183
+ });
1184
+ const deployedContracts = objMap(contracts, (_, c) => c.synthetic.address);
1185
+ const enrollTxs = await enrollCrossChainRouters({
1186
+ multiProvider,
1187
+ altVmSigners: {},
1188
+ registryAddresses,
1189
+ warpDeployConfig,
1190
+ }, deployedContracts);
1191
+ for (const [txChain, txs] of Object.entries(enrollTxs)) {
1192
+ for (const rawTx of txs) {
1193
+ assertEvmTransaction(rawTx, txChain);
1194
+ // drop the annotated chainId: every hardhat test chain shares one
1195
+ // node (chainId 31337) while test-chain metadata carries fake ids
1196
+ await multiProvider.sendTransaction(txChain, {
1197
+ to: rawTx.to,
1198
+ data: rawTx.data,
1199
+ value: rawTx.value,
1200
+ });
1201
+ }
1202
+ }
1203
+ const localProvider = multiProvider.getProvider(chain);
1204
+ const remoteProvider = multiProvider.getProvider(remoteChain);
1205
+ const localDfr = DelayedFlowRouterHookIsm__factory.connect(await MailboxClient__factory.connect(deployedContracts[chain], localProvider).hook(), localProvider);
1206
+ const remoteDfr = DelayedFlowRouterHookIsm__factory.connect(await MailboxClient__factory.connect(deployedContracts[remoteChain], remoteProvider).hook(), remoteProvider);
1207
+ // mutual DFR enrollment
1208
+ const localDomain = multiProvider.getDomainId(chain);
1209
+ const remoteDomain = multiProvider.getDomainId(remoteChain);
1210
+ expect(await localDfr.routers(remoteDomain)).to.equal(addressToBytes32(remoteDfr.address).toLowerCase());
1211
+ expect(await remoteDfr.routers(localDomain)).to.equal(addressToBytes32(localDfr.address).toLowerCase());
1212
+ // ownership handed to the configured owner after enrollment
1213
+ expect((await localDfr.owner()).toLowerCase()).to.equal(warpOwner.toLowerCase());
1214
+ expect((await remoteDfr.owner()).toLowerCase()).to.equal(warpOwner.toLowerCase());
1215
+ // a second pass converges to zero transactions
1216
+ const secondEnrollTxs = await enrollCrossChainRouters({
1217
+ multiProvider,
1218
+ altVmSigners: {},
1219
+ registryAddresses,
1220
+ warpDeployConfig,
1221
+ }, deployedContracts);
1222
+ expect(Object.keys(secondEnrollTxs)).to.have.length(0);
1223
+ });
1224
+ it('reconciles delayedFlowRouterHookIsm enrollment drift', async () => {
1225
+ // Owner is the deployer so the drift can be introduced directly and the
1226
+ // corrective transactions can be submitted from this test.
1227
+ const owner = signer.address;
1228
+ const warpDeployConfig = {
1229
+ [chain]: {
1230
+ ...config[chain],
1231
+ type: TokenType.synthetic,
1232
+ owner,
1233
+ interchainSecurityModule: delayedFlowTree(owner),
1234
+ },
1235
+ [remoteChain]: {
1236
+ ...config[remoteChain],
1237
+ type: TokenType.synthetic,
1238
+ owner,
1239
+ interchainSecurityModule: delayedFlowTree(owner),
1240
+ },
1241
+ };
1242
+ const contracts = await deployHybridRoute(objMap(warpDeployConfig, (_, c) => ({
1243
+ ...c,
1244
+ interchainSecurityModule: undefined,
1245
+ })), {
1246
+ [chain]: delayedFlowTree(owner),
1247
+ [remoteChain]: delayedFlowTree(owner),
1248
+ });
1249
+ const deployedContracts = objMap(contracts, (_, c) => c.synthetic.address);
1250
+ const submit = async (txs) => {
1251
+ for (const [txChain, chainTxs] of Object.entries(txs)) {
1252
+ for (const rawTx of chainTxs) {
1253
+ assertEvmTransaction(rawTx, txChain);
1254
+ await multiProvider.sendTransaction(txChain, {
1255
+ to: rawTx.to,
1256
+ data: rawTx.data,
1257
+ value: rawTx.value,
1258
+ });
1259
+ }
1260
+ }
1261
+ };
1262
+ await submit(await enrollCrossChainRouters({
1263
+ multiProvider,
1264
+ altVmSigners: {},
1265
+ registryAddresses,
1266
+ warpDeployConfig,
1267
+ }, deployedContracts));
1268
+ const localProvider = multiProvider.getProvider(chain);
1269
+ const localDfrAddress = await MailboxClient__factory.connect(deployedContracts[chain], localProvider).hook();
1270
+ const localDfr = DelayedFlowRouterHookIsm__factory.connect(localDfrAddress, multiProvider.getSigner(chain));
1271
+ const remoteDomain = multiProvider.getDomainId(remoteChain);
1272
+ // Introduce drift: point the local instance at a bogus counterpart.
1273
+ const bogusRouter = addressToBytes32(randomAddress()).toLowerCase();
1274
+ await multiProvider.handleTx(chain, localDfr.enrollRemoteRouter(remoteDomain, bogusRouter));
1275
+ expect(await localDfr.routers(remoteDomain)).to.equal(bogusRouter);
1276
+ // Re-running enrollment must emit corrective transactions...
1277
+ const driftTxs = await enrollCrossChainRouters({
1278
+ multiProvider,
1279
+ altVmSigners: {},
1280
+ registryAddresses,
1281
+ warpDeployConfig,
1282
+ }, deployedContracts);
1283
+ expect(Object.keys(driftTxs)).to.include(chain);
1284
+ await submit(driftTxs);
1285
+ // ...restoring the real counterpart, and then converging again.
1286
+ const remoteDfrAddress = await MailboxClient__factory.connect(deployedContracts[remoteChain], multiProvider.getProvider(remoteChain)).hook();
1287
+ expect(await localDfr.routers(remoteDomain)).to.equal(addressToBytes32(remoteDfrAddress).toLowerCase());
1288
+ const convergedTxs = await enrollCrossChainRouters({
1289
+ multiProvider,
1290
+ altVmSigners: {},
1291
+ registryAddresses,
1292
+ warpDeployConfig,
1293
+ }, deployedContracts);
1294
+ expect(Object.keys(convergedTxs)).to.have.length(0);
1295
+ });
1296
+ });
686
1297
  describe('TokenFee with optional token for synthetic', () => {
687
1298
  it('should deploy LinearFee without token and resolve to router address', async () => {
688
1299
  const syntheticConfig = {