diff --git a/packages/hardhat/.gitignore b/packages/hardhat/.gitignore index 7c693176..43dc355f 100644 --- a/packages/hardhat/.gitignore +++ b/packages/hardhat/.gitignore @@ -7,6 +7,7 @@ typechain-types # Hardhat files cache +cache_forge/ artifacts gas-report.txt solidity-files-cache.json @@ -14,3 +15,4 @@ deployments/sepolia deployments/goerli deployments/optimismGoerli results.sarif +broadcast/ diff --git a/packages/hardhat/foundry.toml b/packages/hardhat/foundry.toml index b382b7b9..088e28b9 100644 --- a/packages/hardhat/foundry.toml +++ b/packages/hardhat/foundry.toml @@ -9,3 +9,11 @@ allow_paths = ["../../node_modules"] # See https://github.com/foundry-rs/foundry/issues/4988#issuecomment-1556331314 evm_version = 'paris' solc_version = '0.8.19' + +[rpc_endpoints] +goerli = "${GOERLI_RPC_URL}" +optimism_goerli = "${OPTIMISM_GOERLI_RPC_URL}" + +[etherscan] +goerli = { key = "${ETHERSCAN_API_KEY}" } +optimism_goerli = { key = "${ETHERSCAN_API_KEY}" } diff --git a/packages/hardhat/script/jump.sol b/packages/hardhat/script/jump.sol new file mode 100644 index 00000000..741b2ae5 --- /dev/null +++ b/packages/hardhat/script/jump.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +import 'forge-std/console2.sol'; +import 'forge-std/Script.sol'; +import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; +import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol'; +import {IWarpLink} from 'contracts/interfaces/IWarpLink.sol'; +import {WarpLinkCommandTypes} from 'contracts/facets/WarpLink.sol'; +import {IStargateRouter} from 'test/foundry/helpers/IStargateRouter.sol'; +import {IAllowanceTransfer} from 'contracts/interfaces/external/IAllowanceTransfer.sol'; +import {PermitParams} from 'contracts/libraries/PermitParams.sol'; +import {Goerli, OptimismGoerli, Addresses} from '../test/foundry/helpers/Networks.sol'; +import {PermitSignature} from 'test/foundry/helpers/PermitSignature.sol'; + +/** + * Bridge mock USDC from Goerli to Optimism-Goerli and invoke WarpLink on the other + * side, but with no commands. + * + * Adding commands would be even more interesting, but + * there are no actions that can be made using Statgate mock USDC. + * + * Invoke this script using: + * forge script script/jump.sol --rpc-url goerli --no-storage-caching -vvvv --broadcast + */ +contract JumpGoerli is Script, WarpLinkCommandTypes, PermitSignature { + address goerliDiamondAddr = 0x2A104392321e978495dBC91b68914eDbA3126D9c; + + function setUp() public {} + + function run() public { + uint256 privateKey = vm.deriveKey(vm.envString('DEV_MNEMONIC'), 1); + address user = vm.rememberKey(privateKey); + + console2.log('User: %s', user); + + uint48 deadline = uint48(block.timestamp) + 60 * 60 * 24 * 365; + + IWarpLink.Params memory destParams = IWarpLink.Params({ + tokenIn: address(0), // Unused + tokenOut: OptimismGoerli.STARGATE_MOCK_USDC_ADDR, + commands: abi.encodePacked( + (uint8)(0) // Command count + ), + amountIn: 0, // Unused + amountOut: 0, // TODO + recipient: user, + partner: address(0), + feeBps: 0, + slippageBps: 0, + deadline: deadline + }); + + bytes memory destParamsEncoded = abi.encode(destParams); + + uint256 srcAmountIn = 100 * (10 ** 6); + uint256 dstGasForCall = 500_000; + uint16 dstChainId = OptimismGoerli.STARGATE_CHAIN_ID; + + bytes memory sourceCommands = bytes.concat( + abi.encodePacked( + (uint8)(1), // Command count + (uint8)(COMMAND_TYPE_JUMP_STARGATE), + (uint16)(dstChainId), // dstChainId (Goerli) + (uint8)(1), // srcPoolId (USDC) + (uint8)(1), // dstPoolId (USDC), + uint32(dstGasForCall), // dstGasForCall + uint256(destParamsEncoded.length) // NOTE: Unnecessarily large type + ), + destParamsEncoded + ); + + (uint256 nativeWei, ) = IStargateRouter(Goerli.STARGATE_ROUTER_ADDR).quoteLayerZeroFee({ + _dstChainId: dstChainId, // Goerli + _functionType: 1, // Swap remote + _toAddress: abi.encodePacked(goerliDiamondAddr), + _transferAndCallPayload: destParamsEncoded, + _lzTxParams: IStargateRouter.lzTxObj({ + dstGasForCall: dstGasForCall, + dstNativeAmount: 0, + dstNativeAddr: '' + }) + }); + + console2.log('Native fee: %s', nativeWei); + + { + uint256 srcTokenInAllowance = IERC20(Goerli.STARGATE_MOCK_USDC_ADDR).allowance( + user, + address(Addresses.PERMIT2) + ); + + console2.log('srcTokenInAllowance: %s', srcTokenInAllowance); + + if (srcTokenInAllowance < srcAmountIn) { + vm.startBroadcast(user); + + SafeERC20.forceApprove( + IERC20(Goerli.STARGATE_MOCK_USDC_ADDR), + address(Addresses.PERMIT2), + type(uint256).max + ); + + vm.stopBroadcast(); + } + } + + PermitParams memory permitParams; + + { + (, , uint48 nonce) = Addresses.PERMIT2.allowance( + user, + Goerli.STARGATE_MOCK_USDC_ADDR, + address(goerliDiamondAddr) + ); + + IAllowanceTransfer.PermitSingle memory permit = IAllowanceTransfer.PermitSingle( + IAllowanceTransfer.PermitDetails({ + token: Goerli.STARGATE_MOCK_USDC_ADDR, + amount: uint160(srcAmountIn), + expiration: deadline, + nonce: nonce + }), + address(goerliDiamondAddr), + deadline + ); + + bytes memory sig = getPermitSignature( + permit, + privateKey, + Addresses.PERMIT2.DOMAIN_SEPARATOR() + ); + + permitParams = PermitParams({nonce: nonce, signature: sig}); + } + + vm.startBroadcast(user); + + IWarpLink(goerliDiamondAddr).warpLinkEngage{value: nativeWei}( + IWarpLink.Params({ + tokenIn: Goerli.STARGATE_MOCK_USDC_ADDR, + tokenOut: Goerli.STARGATE_MOCK_USDC_ADDR, + commands: sourceCommands, + amountIn: srcAmountIn, + amountOut: 0, // TODO + recipient: address(0), // Unused + partner: address(0), // Unused + feeBps: 0, // Unused + slippageBps: 0, + deadline: deadline + }), + permitParams + ); + + vm.stopBroadcast(); + } +} diff --git a/packages/hardhat/test/foundry/WarpLink.t.sol b/packages/hardhat/test/foundry/WarpLink.t.sol index 1db8ff68..ad7fd628 100644 --- a/packages/hardhat/test/foundry/WarpLink.t.sol +++ b/packages/hardhat/test/foundry/WarpLink.t.sol @@ -20,22 +20,7 @@ import {IAllowanceTransfer} from 'contracts/interfaces/external/IAllowanceTransf import {IPermit2} from 'contracts/interfaces/external/IPermit2.sol'; import {PermitParams} from 'contracts/libraries/PermitParams.sol'; import {PermitSignature} from './helpers/PermitSignature.sol'; - -interface IStargateRouter { - struct lzTxObj { - uint256 dstGasForCall; - uint256 dstNativeAmount; - bytes dstNativeAddr; - } - - function quoteLayerZeroFee( - uint16 _dstChainId, - uint8 _functionType, - bytes calldata _toAddress, - bytes calldata _transferAndCallPayload, - lzTxObj memory _lzTxParams - ) external view returns (uint256, uint256); -} +import {IStargateRouter} from './helpers/IStargateRouter.sol'; contract WarpLinkTestBase is FacetTest, PermitSignature, WarpLinkCommandTypes { event CollectedFee( diff --git a/packages/hardhat/test/foundry/helpers/IStargateRouter.sol b/packages/hardhat/test/foundry/helpers/IStargateRouter.sol new file mode 100644 index 00000000..63720b16 --- /dev/null +++ b/packages/hardhat/test/foundry/helpers/IStargateRouter.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +interface IStargateRouter { + struct lzTxObj { + uint256 dstGasForCall; + uint256 dstNativeAmount; + bytes dstNativeAddr; + } + + function quoteLayerZeroFee( + uint16 _dstChainId, + uint8 _functionType, + bytes calldata _toAddress, + bytes calldata _transferAndCallPayload, + lzTxObj memory _lzTxParams + ) external view returns (uint256, uint256); +} diff --git a/packages/hardhat/test/foundry/helpers/Networks.sol b/packages/hardhat/test/foundry/helpers/Networks.sol index c699c126..8e7d5eb3 100644 --- a/packages/hardhat/test/foundry/helpers/Networks.sol +++ b/packages/hardhat/test/foundry/helpers/Networks.sol @@ -1,3 +1,6 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol'; import {IPermit2} from '../../../contracts/interfaces/external/IPermit2.sol'; @@ -43,6 +46,12 @@ library Optimism { address public constant STARGATE_ROUTER_ADDR = 0x53Bf833A5d6c4ddA888F69c22C88C9f356a41614; } +library OptimismGoerli { + address public constant STARGATE_ROUTER_ADDR = 0x95461eF0e0ecabC049a5c4a6B98Ca7B335FAF068; + address public constant STARGATE_MOCK_USDC_ADDR = 0x0CEDBAF2D0bFF895C861c5422544090EEdC653Bf; + uint16 public constant STARGATE_CHAIN_ID = 10132; +} + library Avalanche { uint256 public constant CHAIN_ID = 43114; address public constant STARGATE_ROUTER_ADDR = 0x45A01E4e04F14f7A4a6702c74187c5F6222033cd; @@ -51,6 +60,8 @@ library Avalanche { library Goerli { uint256 public constant CHAIN_ID = 5; address public constant STARGATE_ROUTER_ADDR = 0x7612aE2a34E5A363E137De748801FB4c86499152; + address public constant STARGATE_MOCK_USDC_ADDR = 0xDf0360Ad8C5ccf25095Aa97ee5F2785c8d848620; + uint16 public constant STARGATE_CHAIN_ID = 10121; } library Addresses {