diff --git a/contracts/handlers/HypercoreDepositorHandler.sol b/contracts/handlers/HypercoreDepositorHandler.sol new file mode 100644 index 000000000..0ccee3204 --- /dev/null +++ b/contracts/handlers/HypercoreDepositorHandler.sol @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-3.0-only +pragma solidity ^0.8.0; + +import "../interfaces/SpokePoolMessageHandler.sol"; +import "@openzeppelin/contracts-v4/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts-v4/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts-v4/security/ReentrancyGuard.sol"; +import { CoreWriterLib, PrecompileLib } from "hyper-evm-lib/src/CoreWriterLib.sol"; +import { HLConversions } from "hyper-evm-lib/src/common/HLConversions.sol"; + +/** + * @title Allows caller to bridge tokens from HyperEVM to Hypercore and send them to an end user's account + * on Hypercore. + * @dev This contract should only be deployed on HyperEVM. + * @dev This contract can replace a MulticallHandler on HyperEVM if the intent only wants to deposit tokens into + * Hypercore and bypass the other complex arbitrary calldata logic. + * @dev This contract can also be called directly to deposit tokens into Hypercore on behalf of an end user. + */ +contract HypercoreDepositorHandler is AcrossMessageHandler, ReentrancyGuard { + using SafeERC20 for IERC20; + + /** + * @notice Bridges tokens from HyperEVM to Hypercore and sends them to the end user's account on Hypercore. + * @dev Requires msg.sender to have approved this contract to spend the tokens. + * @param token The address of the token to deposit. + * @param amount The amount of tokens on HyperEVM to deposit. + * @param user The address of the user on Hypercore to send the tokens to. + */ + function depositToHypercore(address token, uint256 amount, address user) external nonReentrant { + IERC20(token).safeTransferFrom(msg.sender, address(this), amount); + _bridgeToCore(token, amount); + _spotSend(user, token, amount); + } + + /** + * @notice Entrypoint function if this contract is called by the SpokePool contract following an intent fill. + * @dev Deposits tokens into Hypercore and sends them to the end user's account on Hypercore. + * @param token The address of the token sent. + * @param amount The amount of tokens received by this contract. + * @param message Encoded end user address. + */ + function handleV3AcrossMessage( + address token, + uint256 amount, + address /* relayer */, + bytes memory message + ) external nonReentrant { + address user = abi.decode(message, (address)); + _bridgeToCore(token, amount); + _spotSend(user, token, amount); + } + + function _bridgeToCore(address token, uint256 evmAmount) internal { + // Bridge tokens from HyperEVM to Hypercore. This call should revert if this contract has insufficient balance. + CoreWriterLib.bridgeToCore(token, evmAmount); + } + + function _spotSend(address user, address token, uint256 evmAmount) internal { + // Convert EVM amount to wei amount (used in HyperCore) + uint64 tokenIndex = PrecompileLib.getTokenIndex(token); + uint64 coreAmount = HLConversions.evmToWei(tokenIndex, evmAmount); + + // use CoreWriterLib to call the spotSend CoreWriter action and send tokens to end user. + // @dev: the following call does not execute atomically with the deposit into Hypercore. + // Therefore, this contract will maintain a balance of tokens for one block until the spot send into Hypercore + // is confirmed. + CoreWriterLib.spotSend(user, tokenIndex, coreAmount); + } + + // Native tokens are not supported by this contract. +} diff --git a/foundry.toml b/foundry.toml index 68f1ad17d..3527f3d53 100644 --- a/foundry.toml +++ b/foundry.toml @@ -24,6 +24,7 @@ remappings = [ "eth-gas-reporter/=node_modules/eth-gas-reporter/", "hardhat-deploy/=node_modules/hardhat-deploy/", "hardhat/=node_modules/hardhat/", + "hyper-evm-lib=node_modules/hyper-evm-lib/", ] via_ir = true optimizer_runs = 800 diff --git a/generated/constants.json b/generated/constants.json index 9a3eb5b42..9d2dc425c 100644 --- a/generated/constants.json +++ b/generated/constants.json @@ -43,13 +43,23 @@ "137": { "name": "Polygon", "family": "NONE", - "nativeToken": "MATIC", + "nativeToken": "POL", "publicRPC": "https://polygon-rpc.com", "blockExplorer": "https://polygonscan.com", "cctpDomain": 7, "oftEid": 30109, "hypDomainId": 137 }, + "143": { + "name": "Monad Mainnet", + "family": "NONE", + "nativeToken": "MON", + "publicRPC": "https://rpc-mainnet.monadinfra.com/", + "blockExplorer": "https://monadscan.com/", + "cctpDomain": 15, + "oftEid": 30390, + "hypDomainId": 143 + }, "232": { "name": "Lens", "family": "ZK_STACK", @@ -230,6 +240,16 @@ "oftEid": -1, "hypDomainId": -1 }, + "10143": { + "name": "Monad Testnet", + "family": "NONE", + "nativeToken": "MON", + "publicRPC": "https://testnet-rpc.monad.xyz/", + "blockExplorer": "https://testnet.monvision.io/", + "cctpDomain": 15, + "oftEid": 40204, + "hypDomainId": 10143 + }, "34443": { "name": "Mode", "family": "OP_STACK", @@ -293,7 +313,7 @@ "80002": { "name": "Polygon Amoy", "family": "NONE", - "nativeToken": "MATIC", + "nativeToken": "POL", "publicRPC": "https://rpc-amoy.polygon.technology", "blockExplorer": "https://amoy.polygonscan.com", "cctpDomain": 7, @@ -447,6 +467,7 @@ "LISK": 1135, "MAINNET": 1, "MODE": 34443, + "MONAD": 143, "OPTIMISM": 10, "PLASMA": 9745, "POLYGON": 137, @@ -469,6 +490,7 @@ "LENS_SEPOLIA": 37111, "LISK_SEPOLIA": 4202, "MODE_SEPOLIA": 919, + "MONAD_TESTNET": 10143, "OPTIMISM_SEPOLIA": 11155420, "PLASMA_TESTNET": 9746, "POLYGON_AMOY": 80002, @@ -479,8 +501,8 @@ "SOLANA_DEVNET": 133268194659241 }, "TESTNET_CHAIN_IDs": [ - 421614, 84532, 168587773, 808813, 998, 763373, 129399, 37111, 4202, 919, 11155420, 9746, 80002, 534351, 11155111, - 1301, 300, 133268194659241 + 421614, 84532, 168587773, 808813, 998, 763373, 129399, 37111, 4202, 919, 10143, 11155420, 9746, 80002, 534351, + 11155111, 1301, 300, 133268194659241 ], "WETH": { "1": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", @@ -527,6 +549,7 @@ "56": "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c", "130": "0x4200000000000000000000000000000000000006", "137": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + "143": "0x3bd359C1119dA7Da1D913D1C4D2B7c461115433A", "288": "0xDeadDeAddeAddEAddeadDEaDDEAdDeaDDeAD0000", "300": "0x2D6Db36B3117802E996f13073A08A685D3FeF7eD", "324": "0x5AEa5775959fBC2557Cc8789bC1bf90A239D9a91", @@ -542,6 +565,7 @@ "8453": "0x4200000000000000000000000000000000000006", "9745": "0x6100E367285b01F48D07953803A2d8dCA5D19873", "9746": "0x6100E367285b01F48D07953803A2d8dCA5D19873", + "10143": "0x760AfE86e5de5fa0Ee542fc7B7B713e1c5425701", "34443": "0x4200000000000000000000000000000000000006", "37111": "0xeee5a340Cdc9c179Db25dea45AcfD5FE8d4d3eB8", "41455": "0xb7Da55D7040ef9C887e20374D76A88F93A59119E", @@ -855,12 +879,14 @@ "10": "0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85", "130": "0x078D782b760474a361dDA0AF3839290b0EF57AD6", "137": "0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359", + "143": "0x754704Bc059F8C67012fEd69BC8A327a5aafb603", "232": "0x88F08E304EC4f90D644Cec3Fb69b8aD414acf884", "480": "0x79A02482A880bCE3F13e09Da970dC34db4CD24d1", "998": "0x2B3370eE501B4a559b57D449569354196457D8Ab", "999": "0xb88339CB7199b77E23DB6E890353E22632Ba630f", "1301": "0x31d0220469e10c4E71834a79b1f276d740d3768F", "8453": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", + "10143": "0xf817257fed379853cDe0fa4F97AB987181B1E5Ea", "42161": "0xaf88d065e77c8cC2239327C5EDb3A432268e5831", "59144": "0x176211869cA2b568f2A7D4EE941E073a821EE1ff", "80002": "0x41E94Eb019C0762f9Bfcf9Fb1E58725BfB0e7582", @@ -890,7 +916,7 @@ "232": "0x6bDc36E20D267Ff0dd6097799f82e78907105e2F" }, "WMATIC": { - "1": "0x7D1AfA7B718fb893dB30A3aBc0Cfc608AaCfeBB0", + "1": "0x455e53CBB86018Ac2B8092FdCd39d8444aFFC3F6", "137": "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", "80002": "0x360ad4f9a9A8EFe9A8DCB5f461c4Cc1047E1Dcf9", "11155111": "0x3fd0A53F4Bf853985a95F4Eb3F9C9FDE1F8e2b53" diff --git a/package.json b/package.json index 27971fd9c..00eebffb9 100644 --- a/package.json +++ b/package.json @@ -77,6 +77,7 @@ "@uma/contracts-node": "^0.4.17", "axios": "^1.7.4", "bs58": "^6.0.0", + "hyper-evm-lib": "hyperliquid-dev/hyper-evm-lib", "prettier-plugin-rust": "^0.1.9", "yargs": "^17.7.2", "zksync-web3": "^0.14.3" diff --git a/yarn.lock b/yarn.lock index f08f387af..038ca1c53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10761,6 +10761,10 @@ husky@^4.2.3: slash "^3.0.0" which-pm-runs "^1.0.0" +hyper-evm-lib@hyperliquid-dev/hyper-evm-lib: + version "0.0.0" + resolved "https://codeload.github.com/hyperliquid-dev/hyper-evm-lib/tar.gz/ee5e5e8593e9265fca35719e4efaa4fd3092123e" + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"