From 0ebb77e163c7fc2b41668cb4ce42cbf21dba3c3a Mon Sep 17 00:00:00 2001 From: Maxime Date: Fri, 11 Oct 2024 11:49:49 +0300 Subject: [PATCH 1/8] test: init test file --- test/HypLSP7.t.sol | 338 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 338 insertions(+) create mode 100644 test/HypLSP7.t.sol diff --git a/test/HypLSP7.t.sol b/test/HypLSP7.t.sol new file mode 100644 index 0000000..cddf748 --- /dev/null +++ b/test/HypLSP7.t.sol @@ -0,0 +1,338 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.13; + + +import "forge-std/Test.sol"; + +import {TypeCasts} from "@hyperlane-xyz/core/libs/TypeCasts.sol"; +import {TestMailbox} from "@hyperlane-xyz/core/test/TestMailbox.sol"; +import {ERC20Test} from "@hyperlane-xyz/core/test/ERC20Test.sol"; +import {TestPostDispatchHook} from "@hyperlane-xyz/core/test/TestPostDispatchHook.sol"; +import {TestInterchainGasPaymaster} from "@hyperlane-xyz/core/test/TestInterchainGasPaymaster.sol"; +import {GasRouter} from "@hyperlane-xyz/core/client/GasRouter.sol"; + +import {HypLSP7} from "../src/HypLSP7.sol"; +import {HypERC20Collateral} from "@hyperlane-xyz/core/token/HypERC20Collateral.sol"; +import {HypNative} from "@hyperlane-xyz/core/token/HypNative.sol"; +import {TokenRouter} from "@hyperlane-xyz/core/token/libs/TokenRouter.sol"; + +abstract contract HypTokenTest is Test { + using TypeCasts for address; + uint32 internal constant ORIGIN = 11; + uint32 internal constant DESTINATION = 12; + uint8 internal constant DECIMALS = 18; + uint256 internal constant TOTAL_SUPPLY = 1_000_000e18; + uint256 internal REQUIRED_VALUE; // initialized in setUp + uint256 internal constant GAS_LIMIT = 10_000; + uint256 internal constant TRANSFER_AMT = 100e18; + string internal constant NAME = "HyperlaneInu"; + string internal constant SYMBOL = "HYP"; + address internal constant ALICE = address(0x1); + address internal constant BOB = address(0x2); + + ERC20Test internal primaryToken; + TokenRouter internal localToken; + HypLSP7 internal remoteToken; + TestMailbox internal localMailbox; + TestMailbox internal remoteMailbox; + TestPostDispatchHook internal noopHook; + TestInterchainGasPaymaster internal igp; + + event SentTransferRemote( + uint32 indexed destination, + bytes32 indexed recipient, + uint256 amount + ); + + event ReceivedTransferRemote( + uint32 indexed origin, + bytes32 indexed recipient, + uint256 amount + ); + + function setUp() public virtual { + localMailbox = new TestMailbox(ORIGIN); + remoteMailbox = new TestMailbox(DESTINATION); + + primaryToken = new ERC20Test(NAME, SYMBOL, TOTAL_SUPPLY, DECIMALS); + + noopHook = new TestPostDispatchHook(); + localMailbox.setDefaultHook(address(noopHook)); + localMailbox.setRequiredHook(address(noopHook)); + + REQUIRED_VALUE = noopHook.quoteDispatch("", ""); + + remoteToken = new HypLSP7(DECIMALS, address(remoteMailbox)); + remoteToken.initialize(TOTAL_SUPPLY, NAME, SYMBOL); + remoteToken.enrollRemoteRouter( + ORIGIN, + address(localToken).addressToBytes32() + ); + igp = new TestInterchainGasPaymaster(); + vm.deal(ALICE, 125000); + } + + function _enrollRemoteTokenRouter() internal { + remoteToken.enrollRemoteRouter( + ORIGIN, + address(localToken).addressToBytes32() + ); + } + + function _expectRemoteBalance(address _user, uint256 _balance) internal { + assertEq(remoteToken.balanceOf(_user), _balance); + } + + function _processTransfers(address _recipient, uint256 _amount) internal { + vm.prank(address(remoteMailbox)); + remoteToken.handle( + ORIGIN, + address(localToken).addressToBytes32(), + abi.encodePacked(_recipient.addressToBytes32(), _amount) + ); + } + + function _setCustomGasConfig() internal { + localToken.setHook(address(igp)); + + TokenRouter.GasRouterConfig[] + memory config = new TokenRouter.GasRouterConfig[](1); + config[0] = GasRouter.GasRouterConfig({ + domain: DESTINATION, + gas: GAS_LIMIT + }); + localToken.setDestinationGas(config); + } + + function _performRemoteTransfer( + uint256 _msgValue, + uint256 _amount + ) internal { + vm.prank(ALICE); + localToken.transferRemote{value: _msgValue}( + DESTINATION, + BOB.addressToBytes32(), + _amount + ); + + vm.expectEmit(true, true, false, true); + emit ReceivedTransferRemote(ORIGIN, BOB.addressToBytes32(), _amount); + _processTransfers(BOB, _amount); + + assertEq(remoteToken.balanceOf(BOB), _amount); + } + + function _performRemoteTransferAndGas( + uint256 _msgValue, + uint256 _amount, + uint256 _gasOverhead + ) internal { + uint256 ethBalance = ALICE.balance; + _performRemoteTransfer(_msgValue + _gasOverhead, _amount); + assertEq(ALICE.balance, ethBalance - REQUIRED_VALUE - _gasOverhead); + } + + function _performRemoteTransferWithEmit( + uint256 _msgValue, + uint256 _amount, + uint256 _gasOverhead + ) internal { + vm.expectEmit(true, true, false, true); + emit SentTransferRemote(DESTINATION, BOB.addressToBytes32(), _amount); + _performRemoteTransferAndGas(_msgValue, _amount, _gasOverhead); + } + + function testBenchmark_overheadGasUsage() public { + vm.prank(address(localMailbox)); + + uint256 gasBefore = gasleft(); + localToken.handle( + DESTINATION, + address(remoteToken).addressToBytes32(), + abi.encodePacked(BOB.addressToBytes32(), TRANSFER_AMT) + ); + uint256 gasAfter = gasleft(); + console.log("Overhead gas usage: %d", gasBefore - gasAfter); + } +} + +contract HypERC20Test is HypTokenTest { + using TypeCasts for address; + HypERC20 internal erc20Token; + + function setUp() public override { + super.setUp(); + + localToken = new HypERC20(DECIMALS, address(localMailbox)); + erc20Token = HypERC20(address(localToken)); + + erc20Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL); + + erc20Token.enrollRemoteRouter( + DESTINATION, + address(remoteToken).addressToBytes32() + ); + erc20Token.transfer(ALICE, 1000e18); + + _enrollRemoteTokenRouter(); + } + + function testInitialize_revert_ifAlreadyInitialized() public { + vm.expectRevert("Initializable: contract is already initialized"); + erc20Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL); + } + + function testTotalSupply() public { + assertEq(erc20Token.totalSupply(), TOTAL_SUPPLY); + } + + function testDecimals() public { + assertEq(erc20Token.decimals(), DECIMALS); + } + + function testLocalTransfers() public { + assertEq(erc20Token.balanceOf(ALICE), 1000e18); + assertEq(erc20Token.balanceOf(BOB), 0); + + vm.prank(ALICE); + erc20Token.transfer(BOB, 100e18); + assertEq(erc20Token.balanceOf(ALICE), 900e18); + assertEq(erc20Token.balanceOf(BOB), 100e18); + } + + function testRemoteTransfer() public { + remoteToken.enrollRemoteRouter( + ORIGIN, + address(localToken).addressToBytes32() + ); + uint256 balanceBefore = erc20Token.balanceOf(ALICE); + _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMT, 0); + assertEq(erc20Token.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); + } + + function testRemoteTransfer_invalidAmount() public { + vm.expectRevert("ERC20: burn amount exceeds balance"); + _performRemoteTransfer(REQUIRED_VALUE, TRANSFER_AMT * 11); + assertEq(erc20Token.balanceOf(ALICE), 1000e18); + } + + function testRemoteTransfer_withCustomGasConfig() public { + _setCustomGasConfig(); + + uint256 balanceBefore = erc20Token.balanceOf(ALICE); + _performRemoteTransferAndGas( + REQUIRED_VALUE, + TRANSFER_AMT, + GAS_LIMIT * igp.gasPrice() + ); + assertEq(erc20Token.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); + } +} + +contract HypERC20CollateralTest is HypTokenTest { + using TypeCasts for address; + HypERC20Collateral internal erc20Collateral; + + function setUp() public override { + super.setUp(); + + localToken = new HypERC20Collateral( + address(primaryToken), + address(localMailbox) + ); + erc20Collateral = HypERC20Collateral(address(localToken)); + + erc20Collateral.enrollRemoteRouter( + DESTINATION, + address(remoteToken).addressToBytes32() + ); + + primaryToken.transfer(address(localToken), 1000e18); + primaryToken.transfer(ALICE, 1000e18); + + _enrollRemoteTokenRouter(); + } + + function testInitialize_revert_ifAlreadyInitialized() public {} + + function testRemoteTransfer() public { + uint256 balanceBefore = localToken.balanceOf(ALICE); + + vm.prank(ALICE); + primaryToken.approve(address(localToken), TRANSFER_AMT); + _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMT, 0); + assertEq(localToken.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); + } + + function testRemoteTransfer_invalidAllowance() public { + vm.expectRevert("ERC20: insufficient allowance"); + _performRemoteTransfer(REQUIRED_VALUE, TRANSFER_AMT); + assertEq(localToken.balanceOf(ALICE), 1000e18); + } + + function testRemoteTransfer_withCustomGasConfig() public { + _setCustomGasConfig(); + + uint256 balanceBefore = localToken.balanceOf(ALICE); + + vm.prank(ALICE); + primaryToken.approve(address(localToken), TRANSFER_AMT); + _performRemoteTransferAndGas( + REQUIRED_VALUE, + TRANSFER_AMT, + GAS_LIMIT * igp.gasPrice() + ); + assertEq(localToken.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); + } +} + +contract HypNativeTest is HypTokenTest { + using TypeCasts for address; + HypNative internal nativeToken; + + function setUp() public override { + super.setUp(); + + localToken = new HypNative(address(localMailbox)); + nativeToken = HypNative(payable(address(localToken))); + + nativeToken.enrollRemoteRouter( + DESTINATION, + address(remoteToken).addressToBytes32() + ); + + vm.deal(address(localToken), 1000e18); + vm.deal(ALICE, 1000e18); + + _enrollRemoteTokenRouter(); + } + + function testInitialize_revert_ifAlreadyInitialized() public {} + + function testRemoteTransfer() public { + _performRemoteTransferWithEmit( + REQUIRED_VALUE, + TRANSFER_AMT, + TRANSFER_AMT + ); + } + + function testRemoteTransfer_invalidAmount() public { + vm.expectRevert("Native: amount exceeds msg.value"); + _performRemoteTransfer( + REQUIRED_VALUE + TRANSFER_AMT, + TRANSFER_AMT * 10 + ); + assertEq(localToken.balanceOf(ALICE), 1000e18); + } + + function testRemoteTransfer_withCustomGasConfig() public { + _setCustomGasConfig(); + + _performRemoteTransferAndGas( + REQUIRED_VALUE, + TRANSFER_AMT, + TRANSFER_AMT + GAS_LIMIT * igp.gasPrice() + ); + } +} From 3d9a351ad76875c4b142f5074159ba9d63071d37 Mon Sep 17 00:00:00 2001 From: Maxime Date: Fri, 11 Oct 2024 11:49:53 +0300 Subject: [PATCH 2/8] forge install: forge-std v1.9.3 --- lib/forge-std | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/forge-std b/lib/forge-std index 1714bee..8f24d6b 160000 --- a/lib/forge-std +++ b/lib/forge-std @@ -1 +1 @@ -Subproject commit 1714bee72e286e73f76e320d110e0eaf5c4e649d +Subproject commit 8f24d6b04c92975e0795b5868aa0d783251cdeaa From 06eb485db38da6e9bc745a76acbf4407ae4f7691 Mon Sep 17 00:00:00 2001 From: Maxime Date: Tue, 15 Oct 2024 18:13:01 +0300 Subject: [PATCH 3/8] tests: implement unit test for hyp contracts --- foundry.toml | 4 + remappings.txt | 2 +- test/HypLSP7.t.sol | 179 ++++++++++++++++++++++++++++++++------------- test/LSP7Mock.sol | 27 +++++++ 4 files changed, 161 insertions(+), 51 deletions(-) create mode 100644 test/LSP7Mock.sol diff --git a/foundry.toml b/foundry.toml index dacd6da..e8bb305 100644 --- a/foundry.toml +++ b/foundry.toml @@ -15,6 +15,10 @@ src = "src" test = "test" +remappings = [ + "forge-std/=node_modules/forge-std/src/" +] + [profile.ci] fuzz = { runs = 10_000 } verbosity = 4 diff --git a/remappings.txt b/remappings.txt index e59c580..4b7447e 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,2 +1,2 @@ @/=node_modules/@ -forge-std/=node_modules/forge-std/ +forge-std/=node_modules/forge-std diff --git a/test/HypLSP7.t.sol b/test/HypLSP7.t.sol index cddf748..8314029 100644 --- a/test/HypLSP7.t.sol +++ b/test/HypLSP7.t.sol @@ -2,19 +2,20 @@ pragma solidity ^0.8.13; -import "forge-std/Test.sol"; +import "forge-std/src/Test.sol"; -import {TypeCasts} from "@hyperlane-xyz/core/libs/TypeCasts.sol"; -import {TestMailbox} from "@hyperlane-xyz/core/test/TestMailbox.sol"; -import {ERC20Test} from "@hyperlane-xyz/core/test/ERC20Test.sol"; -import {TestPostDispatchHook} from "@hyperlane-xyz/core/test/TestPostDispatchHook.sol"; -import {TestInterchainGasPaymaster} from "@hyperlane-xyz/core/test/TestInterchainGasPaymaster.sol"; -import {GasRouter} from "@hyperlane-xyz/core/client/GasRouter.sol"; +import {TypeCasts} from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol"; +import {TestMailbox} from "@hyperlane-xyz/core/contracts/test/TestMailbox.sol"; + +import {LSP7Mock} from "./LSP7Mock.sol"; +import {TestPostDispatchHook} from "@hyperlane-xyz/core/contracts/test/TestPostDispatchHook.sol"; +import {TestInterchainGasPaymaster} from "@hyperlane-xyz/core/contracts/test/TestInterchainGasPaymaster.sol"; +import {GasRouter} from "@hyperlane-xyz/core/contracts/client/GasRouter.sol"; import {HypLSP7} from "../src/HypLSP7.sol"; -import {HypERC20Collateral} from "@hyperlane-xyz/core/token/HypERC20Collateral.sol"; -import {HypNative} from "@hyperlane-xyz/core/token/HypNative.sol"; -import {TokenRouter} from "@hyperlane-xyz/core/token/libs/TokenRouter.sol"; +import {HypLSP7Collateral} from "../src/HypLSP7Collateral.sol"; +import {HypNative} from "@hyperlane-xyz/core/contracts/token/HypNative.sol"; +import {TokenRouter} from "@hyperlane-xyz/core/contracts/token/libs/TokenRouter.sol"; abstract contract HypTokenTest is Test { using TypeCasts for address; @@ -27,10 +28,11 @@ abstract contract HypTokenTest is Test { uint256 internal constant TRANSFER_AMT = 100e18; string internal constant NAME = "HyperlaneInu"; string internal constant SYMBOL = "HYP"; - address internal constant ALICE = address(0x1); - address internal constant BOB = address(0x2); + address internal ALICE = makeAddr("alice"); + address internal BOB = makeAddr("bob"); + address internal OWNER = makeAddr("owner"); - ERC20Test internal primaryToken; + LSP7Mock internal primaryToken; TokenRouter internal localToken; HypLSP7 internal remoteToken; TestMailbox internal localMailbox; @@ -54,7 +56,7 @@ abstract contract HypTokenTest is Test { localMailbox = new TestMailbox(ORIGIN); remoteMailbox = new TestMailbox(DESTINATION); - primaryToken = new ERC20Test(NAME, SYMBOL, TOTAL_SUPPLY, DECIMALS); + primaryToken = new LSP7Mock(NAME, SYMBOL, address(this), TOTAL_SUPPLY); noopHook = new TestPostDispatchHook(); localMailbox.setDefaultHook(address(noopHook)); @@ -63,16 +65,28 @@ abstract contract HypTokenTest is Test { REQUIRED_VALUE = noopHook.quoteDispatch("", ""); remoteToken = new HypLSP7(DECIMALS, address(remoteMailbox)); - remoteToken.initialize(TOTAL_SUPPLY, NAME, SYMBOL); - remoteToken.enrollRemoteRouter( - ORIGIN, - address(localToken).addressToBytes32() + + // todo: add owner + remoteToken.initialize( + TOTAL_SUPPLY, + NAME, + SYMBOL, + address(noopHook), + address(0), + OWNER ); + + // call as owner + vm.prank(OWNER); + remoteToken.enrollRemoteRouter(ORIGIN, address(localToken).addressToBytes32()); + igp = new TestInterchainGasPaymaster(); + vm.deal(ALICE, 125000); } function _enrollRemoteTokenRouter() internal { + vm.prank(OWNER); remoteToken.enrollRemoteRouter( ORIGIN, address(localToken).addressToBytes32() @@ -84,15 +98,19 @@ abstract contract HypTokenTest is Test { } function _processTransfers(address _recipient, uint256 _amount) internal { + vm.prank(address(remoteMailbox)); remoteToken.handle( ORIGIN, address(localToken).addressToBytes32(), abi.encodePacked(_recipient.addressToBytes32(), _amount) ); + } function _setCustomGasConfig() internal { + + vm.prank(OWNER); localToken.setHook(address(igp)); TokenRouter.GasRouterConfig[] @@ -101,7 +119,10 @@ abstract contract HypTokenTest is Test { domain: DESTINATION, gas: GAS_LIMIT }); + + vm.prank(OWNER); localToken.setDestinationGas(config); + } function _performRemoteTransfer( @@ -109,6 +130,7 @@ abstract contract HypTokenTest is Test { uint256 _amount ) internal { vm.prank(ALICE); + localToken.transferRemote{value: _msgValue}( DESTINATION, BOB.addressToBytes32(), @@ -116,6 +138,7 @@ abstract contract HypTokenTest is Test { ); vm.expectEmit(true, true, false, true); + emit ReceivedTransferRemote(ORIGIN, BOB.addressToBytes32(), _amount); _processTransfers(BOB, _amount); @@ -128,7 +151,9 @@ abstract contract HypTokenTest is Test { uint256 _gasOverhead ) internal { uint256 ethBalance = ALICE.balance; + _performRemoteTransfer(_msgValue + _gasOverhead, _amount); + assertEq(ALICE.balance, ethBalance - REQUIRED_VALUE - _gasOverhead); } @@ -152,103 +177,145 @@ abstract contract HypTokenTest is Test { abi.encodePacked(BOB.addressToBytes32(), TRANSFER_AMT) ); uint256 gasAfter = gasleft(); - console.log("Overhead gas usage: %d", gasBefore - gasAfter); + } } -contract HypERC20Test is HypTokenTest { +contract HypLSP7Test is HypTokenTest { using TypeCasts for address; - HypERC20 internal erc20Token; + HypLSP7 internal lsp7Token; + + address internal owner = makeAddr("owner"); function setUp() public override { super.setUp(); - localToken = new HypERC20(DECIMALS, address(localMailbox)); - erc20Token = HypERC20(address(localToken)); + localToken = new HypLSP7(DECIMALS, address(localMailbox)); + lsp7Token = HypLSP7(payable(address(localToken))); - erc20Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL); - erc20Token.enrollRemoteRouter( + vm.prank(owner); + lsp7Token.initialize( + TOTAL_SUPPLY, + NAME, + SYMBOL, + address(noopHook), + address(0), + owner + ); + + + vm.prank(owner); + lsp7Token.enrollRemoteRouter( DESTINATION, address(remoteToken).addressToBytes32() ); - erc20Token.transfer(ALICE, 1000e18); + + + // from, to, amount, force, data + vm.prank(owner); + + lsp7Token.transfer(owner, ALICE, 1000e18, true, ""); + _enrollRemoteTokenRouter(); } function testInitialize_revert_ifAlreadyInitialized() public { vm.expectRevert("Initializable: contract is already initialized"); - erc20Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL); + lsp7Token.initialize( + TOTAL_SUPPLY, + NAME, + SYMBOL, + address(noopHook), + address(0), + owner + ); } function testTotalSupply() public { - assertEq(erc20Token.totalSupply(), TOTAL_SUPPLY); + assertEq(lsp7Token.totalSupply(), TOTAL_SUPPLY); } function testDecimals() public { - assertEq(erc20Token.decimals(), DECIMALS); + assertEq(lsp7Token.decimals(), DECIMALS); } function testLocalTransfers() public { - assertEq(erc20Token.balanceOf(ALICE), 1000e18); - assertEq(erc20Token.balanceOf(BOB), 0); + assertEq(lsp7Token.balanceOf(ALICE), 1000e18); + assertEq(lsp7Token.balanceOf(BOB), 0); vm.prank(ALICE); - erc20Token.transfer(BOB, 100e18); - assertEq(erc20Token.balanceOf(ALICE), 900e18); - assertEq(erc20Token.balanceOf(BOB), 100e18); + lsp7Token.transfer(ALICE, BOB, 100e18, true, ""); + assertEq(lsp7Token.balanceOf(ALICE), 900e18); + assertEq(lsp7Token.balanceOf(BOB), 100e18); } function testRemoteTransfer() public { + vm.prank(owner); remoteToken.enrollRemoteRouter( ORIGIN, address(localToken).addressToBytes32() ); - uint256 balanceBefore = erc20Token.balanceOf(ALICE); + uint256 balanceBefore = lsp7Token.balanceOf(ALICE); + _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMT, 0); - assertEq(erc20Token.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); + assertEq(lsp7Token.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); } function testRemoteTransfer_invalidAmount() public { - vm.expectRevert("ERC20: burn amount exceeds balance"); + vm.expectRevert(); _performRemoteTransfer(REQUIRED_VALUE, TRANSFER_AMT * 11); - assertEq(erc20Token.balanceOf(ALICE), 1000e18); + assertEq(lsp7Token.balanceOf(ALICE), 1000e18); } function testRemoteTransfer_withCustomGasConfig() public { _setCustomGasConfig(); - uint256 balanceBefore = erc20Token.balanceOf(ALICE); + uint256 balanceBefore = lsp7Token.balanceOf(ALICE); + _performRemoteTransferAndGas( REQUIRED_VALUE, TRANSFER_AMT, GAS_LIMIT * igp.gasPrice() ); - assertEq(erc20Token.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); + + assertEq(lsp7Token.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); } } -contract HypERC20CollateralTest is HypTokenTest { +contract HypLSP7CollateralTest is HypTokenTest { using TypeCasts for address; - HypERC20Collateral internal erc20Collateral; + HypLSP7Collateral internal lsp7Collateral; + + address internal owner = makeAddr("owner"); function setUp() public override { super.setUp(); - localToken = new HypERC20Collateral( + localToken = new HypLSP7Collateral( address(primaryToken), address(localMailbox) ); - erc20Collateral = HypERC20Collateral(address(localToken)); - erc20Collateral.enrollRemoteRouter( + + lsp7Collateral = HypLSP7Collateral(address(localToken)); + + lsp7Collateral.initialize( + address(noopHook), + address(0), + OWNER + ); + + vm.prank(owner); + lsp7Collateral.enrollRemoteRouter( DESTINATION, address(remoteToken).addressToBytes32() ); - primaryToken.transfer(address(localToken), 1000e18); - primaryToken.transfer(ALICE, 1000e18); + primaryToken.transfer(address(this), address(localToken), 1000e18, true, ""); + + primaryToken.transfer(address(this), ALICE, 1000e18, true, ""); _enrollRemoteTokenRouter(); } @@ -259,13 +326,14 @@ contract HypERC20CollateralTest is HypTokenTest { uint256 balanceBefore = localToken.balanceOf(ALICE); vm.prank(ALICE); - primaryToken.approve(address(localToken), TRANSFER_AMT); + primaryToken.authorizeOperator(address(localToken), TRANSFER_AMT, ""); + _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMT, 0); assertEq(localToken.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); } function testRemoteTransfer_invalidAllowance() public { - vm.expectRevert("ERC20: insufficient allowance"); + vm.expectRevert(); _performRemoteTransfer(REQUIRED_VALUE, TRANSFER_AMT); assertEq(localToken.balanceOf(ALICE), 1000e18); } @@ -275,8 +343,10 @@ contract HypERC20CollateralTest is HypTokenTest { uint256 balanceBefore = localToken.balanceOf(ALICE); + vm.prank(ALICE); - primaryToken.approve(address(localToken), TRANSFER_AMT); + // primaryToken.authorizeOperator(address(localToken), TRANSFER_AMT, ""); + primaryToken.authorizeOperator(address(localToken), TRANSFER_AMT, ""); _performRemoteTransferAndGas( REQUIRED_VALUE, TRANSFER_AMT, @@ -290,12 +360,21 @@ contract HypNativeTest is HypTokenTest { using TypeCasts for address; HypNative internal nativeToken; + address internal owner = makeAddr("owner"); + function setUp() public override { super.setUp(); localToken = new HypNative(address(localMailbox)); nativeToken = HypNative(payable(address(localToken))); + nativeToken.initialize( + address(noopHook), + address(0), + OWNER + ); + + vm.prank(owner); nativeToken.enrollRemoteRouter( DESTINATION, address(remoteToken).addressToBytes32() diff --git a/test/LSP7Mock.sol b/test/LSP7Mock.sol new file mode 100644 index 0000000..5925f98 --- /dev/null +++ b/test/LSP7Mock.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT OR Apache-2.0 +pragma solidity ^0.8.13; + + +import {LSP7DigitalAsset} from "@lukso/lsp7-contracts/contracts/LSP7DigitalAsset.sol"; + + + +contract LSP7Mock is LSP7DigitalAsset { + + constructor(string memory name_, string memory symbol_, address initialAccount_, uint256 initialBalance_) + LSP7DigitalAsset(name_, symbol_, initialAccount_, 0, false) { + _mint(initialAccount_, initialBalance_, true, ""); + } + + function mint(uint256 amount) public { + _mint(msg.sender, amount, true, ""); + } + + function mintTo(address account, uint256 amount) public { + _mint(account, amount, true, ""); + } + + function burnFrom(address account, uint256 amount) public { + _burn(account, amount, ""); + } +} From bbaf71974ad4f59ed74e636c17f2398b0dacfd9e Mon Sep 17 00:00:00 2001 From: Maxime Date: Wed, 16 Oct 2024 12:16:35 +0300 Subject: [PATCH 4/8] refactor: remove unused variables --- test/HypLSP7.t.sol | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/test/HypLSP7.t.sol b/test/HypLSP7.t.sol index 8314029..70b68c0 100644 --- a/test/HypLSP7.t.sol +++ b/test/HypLSP7.t.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.13; -import "forge-std/src/Test.sol"; +import {Test} from "forge-std/src/Test.sol"; import {TypeCasts} from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol"; import {TestMailbox} from "@hyperlane-xyz/core/contracts/test/TestMailbox.sol"; @@ -66,7 +66,6 @@ abstract contract HypTokenTest is Test { remoteToken = new HypLSP7(DECIMALS, address(remoteMailbox)); - // todo: add owner remoteToken.initialize( TOTAL_SUPPLY, NAME, @@ -76,7 +75,6 @@ abstract contract HypTokenTest is Test { OWNER ); - // call as owner vm.prank(OWNER); remoteToken.enrollRemoteRouter(ORIGIN, address(localToken).addressToBytes32()); @@ -288,7 +286,6 @@ contract HypLSP7CollateralTest is HypTokenTest { using TypeCasts for address; HypLSP7Collateral internal lsp7Collateral; - address internal owner = makeAddr("owner"); function setUp() public override { super.setUp(); @@ -307,7 +304,7 @@ contract HypLSP7CollateralTest is HypTokenTest { OWNER ); - vm.prank(owner); + vm.prank(OWNER); lsp7Collateral.enrollRemoteRouter( DESTINATION, address(remoteToken).addressToBytes32() @@ -320,8 +317,6 @@ contract HypLSP7CollateralTest is HypTokenTest { _enrollRemoteTokenRouter(); } - function testInitialize_revert_ifAlreadyInitialized() public {} - function testRemoteTransfer() public { uint256 balanceBefore = localToken.balanceOf(ALICE); @@ -345,7 +340,6 @@ contract HypLSP7CollateralTest is HypTokenTest { vm.prank(ALICE); - // primaryToken.authorizeOperator(address(localToken), TRANSFER_AMT, ""); primaryToken.authorizeOperator(address(localToken), TRANSFER_AMT, ""); _performRemoteTransferAndGas( REQUIRED_VALUE, @@ -360,7 +354,6 @@ contract HypNativeTest is HypTokenTest { using TypeCasts for address; HypNative internal nativeToken; - address internal owner = makeAddr("owner"); function setUp() public override { super.setUp(); @@ -374,7 +367,7 @@ contract HypNativeTest is HypTokenTest { OWNER ); - vm.prank(owner); + vm.prank(OWNER); nativeToken.enrollRemoteRouter( DESTINATION, address(remoteToken).addressToBytes32() @@ -386,8 +379,6 @@ contract HypNativeTest is HypTokenTest { _enrollRemoteTokenRouter(); } - function testInitialize_revert_ifAlreadyInitialized() public {} - function testRemoteTransfer() public { _performRemoteTransferWithEmit( REQUIRED_VALUE, From c3a0e06a0084a198a57b92e9a7f8c648a8eb0cab Mon Sep 17 00:00:00 2001 From: Maxime Date: Wed, 16 Oct 2024 12:30:12 +0300 Subject: [PATCH 5/8] fix: lint errors --- .solhint.json | 3 +- README.md | 30 ++++--- docs/README.md | 62 ++++++++------ script/Deploy.s.sol | 7 +- test/HypLSP7.t.sol | 201 ++++++++++---------------------------------- test/LSP7Mock.sol | 16 ++-- 6 files changed, 112 insertions(+), 207 deletions(-) diff --git a/.solhint.json b/.solhint.json index 14f780e..c956bd5 100644 --- a/.solhint.json +++ b/.solhint.json @@ -2,10 +2,9 @@ "extends": "solhint:recommended", "rules": { "code-complexity": ["error", 8], - "compiler-version": ["error", ">=0.8.25"], + "compiler-version": ["error", ">=0.8.0"], "func-name-mixedcase": "off", "func-visibility": ["error", { "ignoreConstructors": true }], - "max-line-length": ["error", 120], "named-parameters-mapping": "warn", "no-console": "off", "not-rely-on-time": "off", diff --git a/README.md b/README.md index 2b03fae..5836713 100644 --- a/README.md +++ b/README.md @@ -9,16 +9,19 @@ [license]: https://opensource.org/licenses/MIT [license-badge]: https://img.shields.io/badge/License-MIT-blue.svg -This repo is the LSP7 version of the [`HypERC20`] and [`HypERC20Collateral`] of `@hyperlane-xyz/core` package. They are used to bridge tokens between the Ethereum and LUKSO chains using the [Hashi Bridge](https://crosschain-alliance.gitbook.io/hashi). +This repo is the LSP7 version of the [`HypERC20`] and [`HypERC20Collateral`] of `@hyperlane-xyz/core` package. They are +used to bridge tokens between the Ethereum and LUKSO chains using the +[Hashi Bridge](https://crosschain-alliance.gitbook.io/hashi). For more details on the **architecture and bridging flow**, see the [**`docs/`**](./docs/README.md) folder. ### Examples of bridged tokens -- ETH -> LUKSO: https://explorer.hyperlane.xyz/message/0x53a383e32fdb68748c8af5c86be3669e58eadc377db2a9f420826cb9474dd55c - -- LUKSO -> ETH: https://explorer.hyperlane.xyz/message/0xf9c86a22e7b5584fc87a9d4ffc39f967a8745cd28b98ed2eaeb220c43996c4ca +- ETH -> LUKSO: + https://explorer.hyperlane.xyz/message/0x53a383e32fdb68748c8af5c86be3669e58eadc377db2a9f420826cb9474dd55c +- LUKSO -> ETH: + https://explorer.hyperlane.xyz/message/0xf9c86a22e7b5584fc87a9d4ffc39f967a8745cd28b98ed2eaeb220c43996c4ca ## Getting Started @@ -45,7 +48,6 @@ This is how to install dependencies: Note that OpenZeppelin Contracts is pre-installed, so you can follow that as an example. - ### Sensible Defaults This template comes with a set of sensible default configurations for you to use. These defaults can be found in the @@ -67,7 +69,6 @@ This is a list of the most frequently needed commands. ### Build & Compile - ```sh # Build the contracts: forge build @@ -100,18 +101,21 @@ bun run test:coverage:report ### GitHub Actions -This repository uses pre-configured GitHub Actions. The contracts are linted and tested on every push and pull requests. You can edit the CI script in [.github/workflows/ci.yml](./.github/workflows/ci.yml). - +This repository uses pre-configured GitHub Actions. The contracts are linted and tested on every push and pull requests. +You can edit the CI script in [.github/workflows/ci.yml](./.github/workflows/ci.yml). ## Foundry Resources -This template builds upon the frameworks and libraries mentioned above, so please consult their respective documentation for details about their specific features. +This template builds upon the frameworks and libraries mentioned above, so please consult their respective documentation +for details about their specific features. For example, if you're interested in exploring Foundry in more detail, you should look at the [Foundry Book](https://book.getfoundry.sh/). In particular, you may be interested in reading the [Writing Tests](https://book.getfoundry.sh/forge/writing-tests.html) tutorial. - -[`HypERC20Collateral`]: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/token/HypERC20Collateral.sol -[`HypERC20`]: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/token/HypERC20.sol -[`Mailbox`]: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/Mailbox.sol +[`HypERC20Collateral`]: + https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/token/HypERC20Collateral.sol +[`HypERC20`]: + https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/token/HypERC20.sol +[`Mailbox`]: + https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/Mailbox.sol diff --git a/docs/README.md b/docs/README.md index 5a55e26..7c50c63 100644 --- a/docs/README.md +++ b/docs/README.md @@ -23,9 +23,11 @@ graph TD end ``` -**scenario 2:** the token was migrated from LUKSO to Ethereum and an HypERC20 token contract was created as a wrapper on the Ethereum side (_e.g: wrapped Chillwhale or wrapped FABS as HypERC20_). +**scenario 2:** the token was migrated from LUKSO to Ethereum and an HypERC20 token contract was created as a wrapper on +the Ethereum side (_e.g: wrapped Chillwhale or wrapped FABS as HypERC20_). -The user burn the wrapped token `HypERC20` on Ethereum, and the tokens are unlocked on the LUKSO side and transferred to the user. +The user burn the wrapped token `HypERC20` on Ethereum, and the tokens are unlocked on the LUKSO side and transferred to +the user. ```mermaid %% Ethereum -> LUKSO - LSP7 token that was initially bridged from LUKSO @@ -45,7 +47,8 @@ graph TD - **scenario 3:** the LSP7 token was originally created and deployed on LUKSO (_e.g: Chillwhale, FABS, etc..._). -The user transfer the LSP7 token to its `HypLSP7Collateral` contract on LUKSO where it is locked. The HypERC20 token on Ethereum is then minted for the user. +The user transfer the LSP7 token to its `HypLSP7Collateral` contract on LUKSO where it is locked. The HypERC20 token on +Ethereum is then minted for the user. ```mermaid graph TD @@ -61,7 +64,8 @@ graph TD end ``` -- **scenario 4:** an ERC20 token was bridged from Ethereum to LUKSO and we want to bridge back to Ethereum (_e.g: wrapped DAI as HypLSP7_). +- **scenario 4:** an ERC20 token was bridged from Ethereum to LUKSO and we want to bridge back to Ethereum (_e.g: + wrapped DAI as HypLSP7_). This HypLSP7 token is burnt on LUKSO, on Ethereum it is unlocked. @@ -79,20 +83,21 @@ graph TD end ``` - ## Detailed Architecture Diagrams > **Notes:** in the architecture diagram below: +> > - The `Yaho` contracts handle the dispatching and batching of messages across chains. -> - The `Yaru` contracts ensures that the messages are properly executed on the destination chain by calling relevant functions like `onMessage`. +> - The `Yaru` contracts ensures that the messages are properly executed on the destination chain by calling relevant +> functions like `onMessage`. ### Ethereum -> LUKSO -> **Note:** This detailed diagram corresponds to the [**scenario 1**](#ethereum---lukso) above. Where an ERC20 token that initially exists on Ethereum (_e.g: DAI, USDC, etc..._) is bridged to LUKSO. +> **Note:** This detailed diagram corresponds to the [**scenario 1**](#ethereum---lukso) above. Where an ERC20 token +> that initially exists on Ethereum (_e.g: DAI, USDC, etc..._) is bridged to LUKSO. ![Ethereum to LUKSO bridge flow](../assets/flow-ethereum-lukso-hashi-bridge.png) - **on Ethereum chain** 1. User transfer ERC20 tokens to [`HypERC20Collateral`]. This locks the tokens in the collateral contract. @@ -102,25 +107,30 @@ graph TD - 3.2. and the Hashi Hook (created by CCIA team). 4. Hashi Hook dispatch the token relaying message from `Yaho` contracts. - - **Off chain** -5. Hashi relayer (managed by CCIA team) listen for events from `Yaho` contracts and request the reporter contracts to relay token relaying message. -6. Hashi executor (managed by CCIA team) listen to event from each Hashi adapter contracts and call `Yaru.executeMessages`. **This step checks whether the Hashi adapters agree on a specify message id** (a threshold number of hash is stored), and set the message Id to verified status. +5. Hashi relayer (managed by CCIA team) listen for events from `Yaho` contracts and request the reporter contracts to + relay token relaying message. +6. Hashi executor (managed by CCIA team) listen to event from each Hashi adapter contracts and call + `Yaru.executeMessages`. **This step checks whether the Hashi adapters agree on a specify message id** (a threshold + number of hash is stored), and set the message Id to verified status. 7. Validator (run by Hyperlane & LUKSO team) will sign the Merkle root when new dispatches happen in Mailbox. 8. Hyperlane relayer (run by Hyperlane team) relays the message by calling Mailbox.process(). **on LUKSO chain** -8. When [`Mailbox.process(...)`](https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/3d116132b87d36af9576d6b116f31a53d680db4a/solidity/contracts/Mailbox.sol#L188-L197) is called, it will: - - 8.1. check with Multisig ISM (includes Hashi ISM), whether the message is signed by validators & verified by Hashi ISM. - - 8.2. If so, it will mint [HypLSP7](./src/HypLSP7.sol) tokens to the receiver. +8. When + [`Mailbox.process(...)`](https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/3d116132b87d36af9576d6b116f31a53d680db4a/solidity/contracts/Mailbox.sol#L188-L197) + is called, it will: +- 8.1. check with Multisig ISM (includes Hashi ISM), whether the message is signed by validators & verified by Hashi + ISM. +- 8.2. If so, it will mint [HypLSP7](./src/HypLSP7.sol) tokens to the receiver. ### LUKSO -> Ethereum -> **Note:** This detailed diagram corresponds to the [**scenario 4**](#lukso---ethereum) above. Where an ERC20 token was bridged from Ethereum to LUKSO and we want to bridge back to Ethereum (_e.g: wrapped DAI as HypLSP7_). +> **Note:** This detailed diagram corresponds to the [**scenario 4**](#lukso---ethereum) above. Where an ERC20 token was +> bridged from Ethereum to LUKSO and we want to bridge back to Ethereum (_e.g: wrapped DAI as HypLSP7_). ![LUKSO to Ethereum bridge flow](../assets/flow-lukso-ethereum-hashi-bridge.png) @@ -135,24 +145,24 @@ graph TD **Off chain** -4. Off chain process remains the same as before, _except there is no Light Client support for Hashi from LUKSO → Ethereum_. +4. Off chain process remains the same as before, _except there is no Light Client support for Hashi from LUKSO → + Ethereum_. **on Ethereum chain** 5. When `Mailbox.process()` is called: - - 5.1. it will check with Multisig ISM (includes Hashi ISM), whether the message is signed by validators & verified by Hashi ISM. + - 5.1. it will check with Multisig ISM (includes Hashi ISM), whether the message is signed by validators & verified + by Hashi ISM. - 5.2. If so, it will unlock ERC20 token to the receiver on the Ethereum chain. - - - - - ## Relevant links & resources - [Cross Chain Alliance - Hashi](https://crosschain-alliance.gitbook.io/hashi) - [Hyperlane smart contracts monorepo](https://github.com/hyperlane-xyz/hyperlane-monorepo) -[`HypERC20Collateral`]: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/token/HypERC20Collateral.sol -[`HypERC20`]: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/token/HypERC20.sol -[`Mailbox`]: https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/Mailbox.sol +[`HypERC20Collateral`]: + https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/token/HypERC20Collateral.sol +[`HypERC20`]: + https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/token/HypERC20.sol +[`Mailbox`]: + https://github.com/hyperlane-xyz/hyperlane-monorepo/blob/%40hyperlane-xyz/core%405.2.0/solidity/contracts/Mailbox.sol diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 22f5f9c..85317c2 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -1,12 +1,11 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity >=0.8.25 <0.9.0; - import { BaseScript } from "./Base.s.sol"; /// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting contract Deploy is BaseScript { - // function run() public broadcast returns (Foo foo) { - // foo = new Foo(); - // } +// function run() public broadcast returns (Foo foo) { +// foo = new Foo(); +// } } diff --git a/test/HypLSP7.t.sol b/test/HypLSP7.t.sol index 70b68c0..8b4daa5 100644 --- a/test/HypLSP7.t.sol +++ b/test/HypLSP7.t.sol @@ -1,24 +1,22 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.13; - -import {Test} from "forge-std/src/Test.sol"; - -import {TypeCasts} from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol"; -import {TestMailbox} from "@hyperlane-xyz/core/contracts/test/TestMailbox.sol"; - -import {LSP7Mock} from "./LSP7Mock.sol"; -import {TestPostDispatchHook} from "@hyperlane-xyz/core/contracts/test/TestPostDispatchHook.sol"; -import {TestInterchainGasPaymaster} from "@hyperlane-xyz/core/contracts/test/TestInterchainGasPaymaster.sol"; -import {GasRouter} from "@hyperlane-xyz/core/contracts/client/GasRouter.sol"; - -import {HypLSP7} from "../src/HypLSP7.sol"; -import {HypLSP7Collateral} from "../src/HypLSP7Collateral.sol"; -import {HypNative} from "@hyperlane-xyz/core/contracts/token/HypNative.sol"; -import {TokenRouter} from "@hyperlane-xyz/core/contracts/token/libs/TokenRouter.sol"; +import { Test } from "forge-std/src/Test.sol"; + +import { TypeCasts } from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol"; +import { TestMailbox } from "@hyperlane-xyz/core/contracts/test/TestMailbox.sol"; +import { LSP7Mock } from "./LSP7Mock.sol"; +import { TestPostDispatchHook } from "@hyperlane-xyz/core/contracts/test/TestPostDispatchHook.sol"; +import { TestInterchainGasPaymaster } from "@hyperlane-xyz/core/contracts/test/TestInterchainGasPaymaster.sol"; +import { GasRouter } from "@hyperlane-xyz/core/contracts/client/GasRouter.sol"; +import { HypLSP7 } from "../src/HypLSP7.sol"; +import { HypLSP7Collateral } from "../src/HypLSP7Collateral.sol"; +import { HypNative } from "@hyperlane-xyz/core/contracts/token/HypNative.sol"; +import { TokenRouter } from "@hyperlane-xyz/core/contracts/token/libs/TokenRouter.sol"; abstract contract HypTokenTest is Test { using TypeCasts for address; + uint32 internal constant ORIGIN = 11; uint32 internal constant DESTINATION = 12; uint8 internal constant DECIMALS = 18; @@ -28,9 +26,9 @@ abstract contract HypTokenTest is Test { uint256 internal constant TRANSFER_AMT = 100e18; string internal constant NAME = "HyperlaneInu"; string internal constant SYMBOL = "HYP"; - address internal ALICE = makeAddr("alice"); - address internal BOB = makeAddr("bob"); - address internal OWNER = makeAddr("owner"); + address internal ALICE = makeAddr("alice"); + address internal BOB = makeAddr("bob"); + address internal OWNER = makeAddr("owner"); LSP7Mock internal primaryToken; TokenRouter internal localToken; @@ -40,17 +38,9 @@ abstract contract HypTokenTest is Test { TestPostDispatchHook internal noopHook; TestInterchainGasPaymaster internal igp; - event SentTransferRemote( - uint32 indexed destination, - bytes32 indexed recipient, - uint256 amount - ); + event SentTransferRemote(uint32 indexed destination, bytes32 indexed recipient, uint256 amount); - event ReceivedTransferRemote( - uint32 indexed origin, - bytes32 indexed recipient, - uint256 amount - ); + event ReceivedTransferRemote(uint32 indexed origin, bytes32 indexed recipient, uint256 amount); function setUp() public virtual { localMailbox = new TestMailbox(ORIGIN); @@ -66,29 +56,19 @@ abstract contract HypTokenTest is Test { remoteToken = new HypLSP7(DECIMALS, address(remoteMailbox)); - remoteToken.initialize( - TOTAL_SUPPLY, - NAME, - SYMBOL, - address(noopHook), - address(0), - OWNER - ); + remoteToken.initialize(TOTAL_SUPPLY, NAME, SYMBOL, address(noopHook), address(0), OWNER); vm.prank(OWNER); remoteToken.enrollRemoteRouter(ORIGIN, address(localToken).addressToBytes32()); igp = new TestInterchainGasPaymaster(); - vm.deal(ALICE, 125000); + vm.deal(ALICE, 125_000); } function _enrollRemoteTokenRouter() internal { vm.prank(OWNER); - remoteToken.enrollRemoteRouter( - ORIGIN, - address(localToken).addressToBytes32() - ); + remoteToken.enrollRemoteRouter(ORIGIN, address(localToken).addressToBytes32()); } function _expectRemoteBalance(address _user, uint256 _balance) internal { @@ -96,44 +76,27 @@ abstract contract HypTokenTest is Test { } function _processTransfers(address _recipient, uint256 _amount) internal { - vm.prank(address(remoteMailbox)); remoteToken.handle( - ORIGIN, - address(localToken).addressToBytes32(), - abi.encodePacked(_recipient.addressToBytes32(), _amount) + ORIGIN, address(localToken).addressToBytes32(), abi.encodePacked(_recipient.addressToBytes32(), _amount) ); - } function _setCustomGasConfig() internal { - vm.prank(OWNER); localToken.setHook(address(igp)); - TokenRouter.GasRouterConfig[] - memory config = new TokenRouter.GasRouterConfig[](1); - config[0] = GasRouter.GasRouterConfig({ - domain: DESTINATION, - gas: GAS_LIMIT - }); + TokenRouter.GasRouterConfig[] memory config = new TokenRouter.GasRouterConfig[](1); + config[0] = GasRouter.GasRouterConfig({ domain: DESTINATION, gas: GAS_LIMIT }); vm.prank(OWNER); localToken.setDestinationGas(config); - } - function _performRemoteTransfer( - uint256 _msgValue, - uint256 _amount - ) internal { + function _performRemoteTransfer(uint256 _msgValue, uint256 _amount) internal { vm.prank(ALICE); - localToken.transferRemote{value: _msgValue}( - DESTINATION, - BOB.addressToBytes32(), - _amount - ); + localToken.transferRemote{ value: _msgValue }(DESTINATION, BOB.addressToBytes32(), _amount); vm.expectEmit(true, true, false, true); @@ -143,11 +106,7 @@ abstract contract HypTokenTest is Test { assertEq(remoteToken.balanceOf(BOB), _amount); } - function _performRemoteTransferAndGas( - uint256 _msgValue, - uint256 _amount, - uint256 _gasOverhead - ) internal { + function _performRemoteTransferAndGas(uint256 _msgValue, uint256 _amount, uint256 _gasOverhead) internal { uint256 ethBalance = ALICE.balance; _performRemoteTransfer(_msgValue + _gasOverhead, _amount); @@ -155,11 +114,7 @@ abstract contract HypTokenTest is Test { assertEq(ALICE.balance, ethBalance - REQUIRED_VALUE - _gasOverhead); } - function _performRemoteTransferWithEmit( - uint256 _msgValue, - uint256 _amount, - uint256 _gasOverhead - ) internal { + function _performRemoteTransferWithEmit(uint256 _msgValue, uint256 _amount, uint256 _gasOverhead) internal { vm.expectEmit(true, true, false, true); emit SentTransferRemote(DESTINATION, BOB.addressToBytes32(), _amount); _performRemoteTransferAndGas(_msgValue, _amount, _gasOverhead); @@ -170,17 +125,15 @@ abstract contract HypTokenTest is Test { uint256 gasBefore = gasleft(); localToken.handle( - DESTINATION, - address(remoteToken).addressToBytes32(), - abi.encodePacked(BOB.addressToBytes32(), TRANSFER_AMT) + DESTINATION, address(remoteToken).addressToBytes32(), abi.encodePacked(BOB.addressToBytes32(), TRANSFER_AMT) ); uint256 gasAfter = gasleft(); - } } contract HypLSP7Test is HypTokenTest { using TypeCasts for address; + HypLSP7 internal lsp7Token; address internal owner = makeAddr("owner"); @@ -191,44 +144,23 @@ contract HypLSP7Test is HypTokenTest { localToken = new HypLSP7(DECIMALS, address(localMailbox)); lsp7Token = HypLSP7(payable(address(localToken))); - vm.prank(owner); - lsp7Token.initialize( - TOTAL_SUPPLY, - NAME, - SYMBOL, - address(noopHook), - address(0), - owner - ); - + lsp7Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL, address(noopHook), address(0), owner); vm.prank(owner); - lsp7Token.enrollRemoteRouter( - DESTINATION, - address(remoteToken).addressToBytes32() - ); - + lsp7Token.enrollRemoteRouter(DESTINATION, address(remoteToken).addressToBytes32()); // from, to, amount, force, data vm.prank(owner); lsp7Token.transfer(owner, ALICE, 1000e18, true, ""); - _enrollRemoteTokenRouter(); } function testInitialize_revert_ifAlreadyInitialized() public { vm.expectRevert("Initializable: contract is already initialized"); - lsp7Token.initialize( - TOTAL_SUPPLY, - NAME, - SYMBOL, - address(noopHook), - address(0), - owner - ); + lsp7Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL, address(noopHook), address(0), owner); } function testTotalSupply() public { @@ -251,10 +183,7 @@ contract HypLSP7Test is HypTokenTest { function testRemoteTransfer() public { vm.prank(owner); - remoteToken.enrollRemoteRouter( - ORIGIN, - address(localToken).addressToBytes32() - ); + remoteToken.enrollRemoteRouter(ORIGIN, address(localToken).addressToBytes32()); uint256 balanceBefore = lsp7Token.balanceOf(ALICE); _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMT, 0); @@ -272,11 +201,7 @@ contract HypLSP7Test is HypTokenTest { uint256 balanceBefore = lsp7Token.balanceOf(ALICE); - _performRemoteTransferAndGas( - REQUIRED_VALUE, - TRANSFER_AMT, - GAS_LIMIT * igp.gasPrice() - ); + _performRemoteTransferAndGas(REQUIRED_VALUE, TRANSFER_AMT, GAS_LIMIT * igp.gasPrice()); assertEq(lsp7Token.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); } @@ -284,31 +209,20 @@ contract HypLSP7Test is HypTokenTest { contract HypLSP7CollateralTest is HypTokenTest { using TypeCasts for address; - HypLSP7Collateral internal lsp7Collateral; + HypLSP7Collateral internal lsp7Collateral; function setUp() public override { super.setUp(); - localToken = new HypLSP7Collateral( - address(primaryToken), - address(localMailbox) - ); - + localToken = new HypLSP7Collateral(address(primaryToken), address(localMailbox)); lsp7Collateral = HypLSP7Collateral(address(localToken)); - lsp7Collateral.initialize( - address(noopHook), - address(0), - OWNER - ); + lsp7Collateral.initialize(address(noopHook), address(0), OWNER); vm.prank(OWNER); - lsp7Collateral.enrollRemoteRouter( - DESTINATION, - address(remoteToken).addressToBytes32() - ); + lsp7Collateral.enrollRemoteRouter(DESTINATION, address(remoteToken).addressToBytes32()); primaryToken.transfer(address(this), address(localToken), 1000e18, true, ""); @@ -338,22 +252,17 @@ contract HypLSP7CollateralTest is HypTokenTest { uint256 balanceBefore = localToken.balanceOf(ALICE); - vm.prank(ALICE); primaryToken.authorizeOperator(address(localToken), TRANSFER_AMT, ""); - _performRemoteTransferAndGas( - REQUIRED_VALUE, - TRANSFER_AMT, - GAS_LIMIT * igp.gasPrice() - ); + _performRemoteTransferAndGas(REQUIRED_VALUE, TRANSFER_AMT, GAS_LIMIT * igp.gasPrice()); assertEq(localToken.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); } } contract HypNativeTest is HypTokenTest { using TypeCasts for address; - HypNative internal nativeToken; + HypNative internal nativeToken; function setUp() public override { super.setUp(); @@ -361,17 +270,10 @@ contract HypNativeTest is HypTokenTest { localToken = new HypNative(address(localMailbox)); nativeToken = HypNative(payable(address(localToken))); - nativeToken.initialize( - address(noopHook), - address(0), - OWNER - ); + nativeToken.initialize(address(noopHook), address(0), OWNER); vm.prank(OWNER); - nativeToken.enrollRemoteRouter( - DESTINATION, - address(remoteToken).addressToBytes32() - ); + nativeToken.enrollRemoteRouter(DESTINATION, address(remoteToken).addressToBytes32()); vm.deal(address(localToken), 1000e18); vm.deal(ALICE, 1000e18); @@ -380,29 +282,18 @@ contract HypNativeTest is HypTokenTest { } function testRemoteTransfer() public { - _performRemoteTransferWithEmit( - REQUIRED_VALUE, - TRANSFER_AMT, - TRANSFER_AMT - ); + _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMT, TRANSFER_AMT); } function testRemoteTransfer_invalidAmount() public { vm.expectRevert("Native: amount exceeds msg.value"); - _performRemoteTransfer( - REQUIRED_VALUE + TRANSFER_AMT, - TRANSFER_AMT * 10 - ); + _performRemoteTransfer(REQUIRED_VALUE + TRANSFER_AMT, TRANSFER_AMT * 10); assertEq(localToken.balanceOf(ALICE), 1000e18); } function testRemoteTransfer_withCustomGasConfig() public { _setCustomGasConfig(); - _performRemoteTransferAndGas( - REQUIRED_VALUE, - TRANSFER_AMT, - TRANSFER_AMT + GAS_LIMIT * igp.gasPrice() - ); + _performRemoteTransferAndGas(REQUIRED_VALUE, TRANSFER_AMT, TRANSFER_AMT + GAS_LIMIT * igp.gasPrice()); } } diff --git a/test/LSP7Mock.sol b/test/LSP7Mock.sol index 5925f98..db0cce2 100644 --- a/test/LSP7Mock.sol +++ b/test/LSP7Mock.sol @@ -1,15 +1,17 @@ // SPDX-License-Identifier: MIT OR Apache-2.0 pragma solidity ^0.8.13; - -import {LSP7DigitalAsset} from "@lukso/lsp7-contracts/contracts/LSP7DigitalAsset.sol"; - - +import { LSP7DigitalAsset } from "@lukso/lsp7-contracts/contracts/LSP7DigitalAsset.sol"; contract LSP7Mock is LSP7DigitalAsset { - - constructor(string memory name_, string memory symbol_, address initialAccount_, uint256 initialBalance_) - LSP7DigitalAsset(name_, symbol_, initialAccount_, 0, false) { + constructor( + string memory name_, + string memory symbol_, + address initialAccount_, + uint256 initialBalance_ + ) + LSP7DigitalAsset(name_, symbol_, initialAccount_, 0, false) + { _mint(initialAccount_, initialBalance_, true, ""); } From 45dd14a1aec3a3e7e5fb7745e3fb6632127bd344 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Fri, 18 Oct 2024 10:31:33 +0200 Subject: [PATCH 6/8] docs: fix grammar --- docs/README.md | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/docs/README.md b/docs/README.md index 7c50c63..acef484 100644 --- a/docs/README.md +++ b/docs/README.md @@ -26,8 +26,8 @@ graph TD **scenario 2:** the token was migrated from LUKSO to Ethereum and an HypERC20 token contract was created as a wrapper on the Ethereum side (_e.g: wrapped Chillwhale or wrapped FABS as HypERC20_). -The user burn the wrapped token `HypERC20` on Ethereum, and the tokens are unlocked on the LUKSO side and transferred to -the user. +The user burns the wrapped token `HypERC20` on Ethereum, and the tokens are unlocked on the LUKSO side and transferred +to the user. ```mermaid %% Ethereum -> LUKSO - LSP7 token that was initially bridged from LUKSO @@ -47,7 +47,7 @@ graph TD - **scenario 3:** the LSP7 token was originally created and deployed on LUKSO (_e.g: Chillwhale, FABS, etc..._). -The user transfer the LSP7 token to its `HypLSP7Collateral` contract on LUKSO where it is locked. The HypERC20 token on +The user transfers the LSP7 token to its `HypLSP7Collateral` contract on LUKSO where it is locked. The HypERC20 token on Ethereum is then minted for the user. ```mermaid @@ -89,7 +89,9 @@ graph TD > > - The `Yaho` contracts handle the dispatching and batching of messages across chains. > - The `Yaru` contracts ensures that the messages are properly executed on the destination chain by calling relevant -> functions like `onMessage`. +> functions like `onMessage`. For more infos, see the +> [**Key Contracts**](https://crosschain-alliance.gitbook.io/hashi/api-and-smart-contracts/key-contracts) section on +> the Hashi Alliance docs. ### Ethereum -> LUKSO @@ -101,7 +103,9 @@ graph TD **on Ethereum chain** 1. User transfer ERC20 tokens to [`HypERC20Collateral`]. This locks the tokens in the collateral contract. -2. `HypERC20Collateral` contract call [`Mailbox`] to pass the message. +2. `HypERC20Collateral` contract calls [`Mailbox`] to pass the message via the `transferRemote(...)` function. + (Internally, the functions `__Router_dispatch(..) -> mailbox.dispatch(...)` are called to dispatch the message to the + mailbox). 3. The `Mailbox` calls: - 3.1. the default Hook (created by Hyperlane), - 3.2. and the Hashi Hook (created by CCIA team). @@ -115,7 +119,7 @@ graph TD `Yaru.executeMessages`. **This step checks whether the Hashi adapters agree on a specify message id** (a threshold number of hash is stored), and set the message Id to verified status. 7. Validator (run by Hyperlane & LUKSO team) will sign the Merkle root when new dispatches happen in Mailbox. -8. Hyperlane relayer (run by Hyperlane team) relays the message by calling Mailbox.process(). +8. Hyperlane relayer (run by Hyperlane team) relays the message by calling `Mailbox.process(...)`. **on LUKSO chain** @@ -138,10 +142,10 @@ graph TD > _Step 1 to 3 needs to be confirmed_ -1. User transfer LSP7 token to HypLSP7 contract and the tokens are burnt. +1. User transfers LSP7 token to HypLSP7 contract and the tokens are burnt. 2. HypLSP7 contract calls `Mailbox` to pass the message. 3. `Mailbox` calls Default Hook (created by Hyperlane) and Hashi Hook (created by CCIA team). -4. Hashi Hook dispatch the token relaying message from Yaho contracts. +4. Hashi Hook dispatches the token relaying message from Yaho contracts. **Off chain** From f1ab0a236e3fdb2eabb3509871adbd0bc47e53bd Mon Sep 17 00:00:00 2001 From: CJ42 Date: Fri, 18 Oct 2024 13:41:01 +0200 Subject: [PATCH 7/8] test: organise code --- script/Deploy.s.sol | 11 ---------- test/HypLSP7.t.sol | 49 ++++++++++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 32 deletions(-) delete mode 100644 script/Deploy.s.sol diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol deleted file mode 100644 index 85317c2..0000000 --- a/script/Deploy.s.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8.25 <0.9.0; - -import { BaseScript } from "./Base.s.sol"; - -/// @dev See the Solidity Scripting tutorial: https://book.getfoundry.sh/tutorials/solidity-scripting -contract Deploy is BaseScript { -// function run() public broadcast returns (Foo foo) { -// foo = new Foo(); -// } -} diff --git a/test/HypLSP7.t.sol b/test/HypLSP7.t.sol index 8b4daa5..796136a 100644 --- a/test/HypLSP7.t.sol +++ b/test/HypLSP7.t.sol @@ -3,17 +3,21 @@ pragma solidity ^0.8.13; import { Test } from "forge-std/src/Test.sol"; +/// Hyperlane testing environnement +/// @dev See https://docs.hyperlane.xyz/docs/guides/developer-tips/unit-testing import { TypeCasts } from "@hyperlane-xyz/core/contracts/libs/TypeCasts.sol"; import { TestMailbox } from "@hyperlane-xyz/core/contracts/test/TestMailbox.sol"; -import { LSP7Mock } from "./LSP7Mock.sol"; import { TestPostDispatchHook } from "@hyperlane-xyz/core/contracts/test/TestPostDispatchHook.sol"; import { TestInterchainGasPaymaster } from "@hyperlane-xyz/core/contracts/test/TestInterchainGasPaymaster.sol"; import { GasRouter } from "@hyperlane-xyz/core/contracts/client/GasRouter.sol"; -import { HypLSP7 } from "../src/HypLSP7.sol"; -import { HypLSP7Collateral } from "../src/HypLSP7Collateral.sol"; import { HypNative } from "@hyperlane-xyz/core/contracts/token/HypNative.sol"; import { TokenRouter } from "@hyperlane-xyz/core/contracts/token/libs/TokenRouter.sol"; +// Mocks + contracts to test +import { LSP7Mock } from "./LSP7Mock.sol"; +import { HypLSP7 } from "../src/HypLSP7.sol"; +import { HypLSP7Collateral } from "../src/HypLSP7Collateral.sol"; + abstract contract HypTokenTest is Test { using TypeCasts for address; @@ -21,14 +25,15 @@ abstract contract HypTokenTest is Test { uint32 internal constant DESTINATION = 12; uint8 internal constant DECIMALS = 18; uint256 internal constant TOTAL_SUPPLY = 1_000_000e18; - uint256 internal REQUIRED_VALUE; // initialized in setUp uint256 internal constant GAS_LIMIT = 10_000; - uint256 internal constant TRANSFER_AMT = 100e18; + uint256 internal constant TRANSFER_AMOUNT = 100e18; string internal constant NAME = "HyperlaneInu"; string internal constant SYMBOL = "HYP"; + address internal ALICE = makeAddr("alice"); address internal BOB = makeAddr("bob"); address internal OWNER = makeAddr("owner"); + uint256 internal REQUIRED_VALUE; // initialized in setUp LSP7Mock internal primaryToken; TokenRouter internal localToken; @@ -125,7 +130,9 @@ abstract contract HypTokenTest is Test { uint256 gasBefore = gasleft(); localToken.handle( - DESTINATION, address(remoteToken).addressToBytes32(), abi.encodePacked(BOB.addressToBytes32(), TRANSFER_AMT) + DESTINATION, + address(remoteToken).addressToBytes32(), + abi.encodePacked(BOB.addressToBytes32(), TRANSFER_AMOUNT) ); uint256 gasAfter = gasleft(); } @@ -186,13 +193,13 @@ contract HypLSP7Test is HypTokenTest { remoteToken.enrollRemoteRouter(ORIGIN, address(localToken).addressToBytes32()); uint256 balanceBefore = lsp7Token.balanceOf(ALICE); - _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMT, 0); - assertEq(lsp7Token.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); + _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMOUNT, 0); + assertEq(lsp7Token.balanceOf(ALICE), balanceBefore - TRANSFER_AMOUNT); } function testRemoteTransfer_invalidAmount() public { vm.expectRevert(); - _performRemoteTransfer(REQUIRED_VALUE, TRANSFER_AMT * 11); + _performRemoteTransfer(REQUIRED_VALUE, TRANSFER_AMOUNT * 11); assertEq(lsp7Token.balanceOf(ALICE), 1000e18); } @@ -201,9 +208,9 @@ contract HypLSP7Test is HypTokenTest { uint256 balanceBefore = lsp7Token.balanceOf(ALICE); - _performRemoteTransferAndGas(REQUIRED_VALUE, TRANSFER_AMT, GAS_LIMIT * igp.gasPrice()); + _performRemoteTransferAndGas(REQUIRED_VALUE, TRANSFER_AMOUNT, GAS_LIMIT * igp.gasPrice()); - assertEq(lsp7Token.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); + assertEq(lsp7Token.balanceOf(ALICE), balanceBefore - TRANSFER_AMOUNT); } } @@ -235,15 +242,15 @@ contract HypLSP7CollateralTest is HypTokenTest { uint256 balanceBefore = localToken.balanceOf(ALICE); vm.prank(ALICE); - primaryToken.authorizeOperator(address(localToken), TRANSFER_AMT, ""); + primaryToken.authorizeOperator(address(localToken), TRANSFER_AMOUNT, ""); - _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMT, 0); - assertEq(localToken.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); + _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMOUNT, 0); + assertEq(localToken.balanceOf(ALICE), balanceBefore - TRANSFER_AMOUNT); } function testRemoteTransfer_invalidAllowance() public { vm.expectRevert(); - _performRemoteTransfer(REQUIRED_VALUE, TRANSFER_AMT); + _performRemoteTransfer(REQUIRED_VALUE, TRANSFER_AMOUNT); assertEq(localToken.balanceOf(ALICE), 1000e18); } @@ -253,9 +260,9 @@ contract HypLSP7CollateralTest is HypTokenTest { uint256 balanceBefore = localToken.balanceOf(ALICE); vm.prank(ALICE); - primaryToken.authorizeOperator(address(localToken), TRANSFER_AMT, ""); - _performRemoteTransferAndGas(REQUIRED_VALUE, TRANSFER_AMT, GAS_LIMIT * igp.gasPrice()); - assertEq(localToken.balanceOf(ALICE), balanceBefore - TRANSFER_AMT); + primaryToken.authorizeOperator(address(localToken), TRANSFER_AMOUNT, ""); + _performRemoteTransferAndGas(REQUIRED_VALUE, TRANSFER_AMOUNT, GAS_LIMIT * igp.gasPrice()); + assertEq(localToken.balanceOf(ALICE), balanceBefore - TRANSFER_AMOUNT); } } @@ -282,18 +289,18 @@ contract HypNativeTest is HypTokenTest { } function testRemoteTransfer() public { - _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMT, TRANSFER_AMT); + _performRemoteTransferWithEmit(REQUIRED_VALUE, TRANSFER_AMOUNT, TRANSFER_AMOUNT); } function testRemoteTransfer_invalidAmount() public { vm.expectRevert("Native: amount exceeds msg.value"); - _performRemoteTransfer(REQUIRED_VALUE + TRANSFER_AMT, TRANSFER_AMT * 10); + _performRemoteTransfer(REQUIRED_VALUE + TRANSFER_AMOUNT, TRANSFER_AMOUNT * 10); assertEq(localToken.balanceOf(ALICE), 1000e18); } function testRemoteTransfer_withCustomGasConfig() public { _setCustomGasConfig(); - _performRemoteTransferAndGas(REQUIRED_VALUE, TRANSFER_AMT, TRANSFER_AMT + GAS_LIMIT * igp.gasPrice()); + _performRemoteTransferAndGas(REQUIRED_VALUE, TRANSFER_AMOUNT, TRANSFER_AMOUNT + GAS_LIMIT * igp.gasPrice()); } } From bb2828c55bae99652e757da19aea956224cc6764 Mon Sep 17 00:00:00 2001 From: CJ42 Date: Mon, 21 Oct 2024 08:13:48 +0200 Subject: [PATCH 8/8] chore: remove duplicate remapping + re-use already defined test variable --- foundry.toml | 92 ++++++++++++++++++++++------------------------ test/HypLSP7.t.sol | 23 +++++------- 2 files changed, 54 insertions(+), 61 deletions(-) diff --git a/foundry.toml b/foundry.toml index e8bb305..6746770 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,59 +1,55 @@ # Full reference https://github.com/foundry-rs/foundry/tree/master/crates/config [profile.default] - auto_detect_solc = false - block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT - bytecode_hash = "none" - evm_version = "shanghai" - fuzz = { runs = 1_000 } - gas_reports = ["*"] - optimizer = true - optimizer_runs = 10_000 - out = "out" - script = "script" - solc = "0.8.25" - src = "src" - test = "test" - -remappings = [ - "forge-std/=node_modules/forge-std/src/" -] +auto_detect_solc = false +block_timestamp = 1_680_220_800 # March 31, 2023 at 00:00 GMT +bytecode_hash = "none" +evm_version = "shanghai" +fuzz = { runs = 1_000 } +gas_reports = ["*"] +optimizer = true +optimizer_runs = 10_000 +out = "out" +script = "script" +solc = "0.8.25" +src = "src" +test = "test" [profile.ci] - fuzz = { runs = 10_000 } - verbosity = 4 +fuzz = { runs = 10_000 } +verbosity = 4 [etherscan] - arbitrum = { key = "${API_KEY_ARBISCAN}" } - avalanche = { key = "${API_KEY_SNOWTRACE}" } - base = { key = "${API_KEY_BASESCAN}" } - bnb_smart_chain = { key = "${API_KEY_BSCSCAN}" } - gnosis_chain = { key = "${API_KEY_GNOSISSCAN}" } - goerli = { key = "${API_KEY_ETHERSCAN}" } - mainnet = { key = "${API_KEY_ETHERSCAN}" } - optimism = { key = "${API_KEY_OPTIMISTIC_ETHERSCAN}" } - polygon = { key = "${API_KEY_POLYGONSCAN}" } - sepolia = { key = "${API_KEY_ETHERSCAN}" } +arbitrum = { key = "${API_KEY_ARBISCAN}" } +avalanche = { key = "${API_KEY_SNOWTRACE}" } +base = { key = "${API_KEY_BASESCAN}" } +bnb_smart_chain = { key = "${API_KEY_BSCSCAN}" } +gnosis_chain = { key = "${API_KEY_GNOSISSCAN}" } +goerli = { key = "${API_KEY_ETHERSCAN}" } +mainnet = { key = "${API_KEY_ETHERSCAN}" } +optimism = { key = "${API_KEY_OPTIMISTIC_ETHERSCAN}" } +polygon = { key = "${API_KEY_POLYGONSCAN}" } +sepolia = { key = "${API_KEY_ETHERSCAN}" } [fmt] - bracket_spacing = true - int_types = "long" - line_length = 120 - multiline_func_header = "all" - number_underscore = "thousands" - quote_style = "double" - tab_width = 4 - wrap_comments = true +bracket_spacing = true +int_types = "long" +line_length = 120 +multiline_func_header = "all" +number_underscore = "thousands" +quote_style = "double" +tab_width = 4 +wrap_comments = true [rpc_endpoints] - arbitrum = "https://arbitrum-mainnet.infura.io/v3/${API_KEY_INFURA}" - avalanche = "https://avalanche-mainnet.infura.io/v3/${API_KEY_INFURA}" - base = "https://mainnet.base.org" - bnb_smart_chain = "https://bsc-dataseed.binance.org" - gnosis_chain = "https://rpc.gnosischain.com" - goerli = "https://goerli.infura.io/v3/${API_KEY_INFURA}" - localhost = "http://localhost:8545" - mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}" - optimism = "https://optimism-mainnet.infura.io/v3/${API_KEY_INFURA}" - polygon = "https://polygon-mainnet.infura.io/v3/${API_KEY_INFURA}" - sepolia = "https://sepolia.infura.io/v3/${API_KEY_INFURA}" +arbitrum = "https://arbitrum-mainnet.infura.io/v3/${API_KEY_INFURA}" +avalanche = "https://avalanche-mainnet.infura.io/v3/${API_KEY_INFURA}" +base = "https://mainnet.base.org" +bnb_smart_chain = "https://bsc-dataseed.binance.org" +gnosis_chain = "https://rpc.gnosischain.com" +goerli = "https://goerli.infura.io/v3/${API_KEY_INFURA}" +localhost = "http://localhost:8545" +mainnet = "https://eth-mainnet.g.alchemy.com/v2/${API_KEY_ALCHEMY}" +optimism = "https://optimism-mainnet.infura.io/v3/${API_KEY_INFURA}" +polygon = "https://polygon-mainnet.infura.io/v3/${API_KEY_INFURA}" +sepolia = "https://sepolia.infura.io/v3/${API_KEY_INFURA}" diff --git a/test/HypLSP7.t.sol b/test/HypLSP7.t.sol index 796136a..6acfdc5 100644 --- a/test/HypLSP7.t.sol +++ b/test/HypLSP7.t.sol @@ -76,7 +76,7 @@ abstract contract HypTokenTest is Test { remoteToken.enrollRemoteRouter(ORIGIN, address(localToken).addressToBytes32()); } - function _expectRemoteBalance(address _user, uint256 _balance) internal { + function _expectRemoteBalance(address _user, uint256 _balance) internal view { assertEq(remoteToken.balanceOf(_user), _balance); } @@ -143,38 +143,35 @@ contract HypLSP7Test is HypTokenTest { HypLSP7 internal lsp7Token; - address internal owner = makeAddr("owner"); - function setUp() public override { super.setUp(); localToken = new HypLSP7(DECIMALS, address(localMailbox)); lsp7Token = HypLSP7(payable(address(localToken))); - vm.prank(owner); - lsp7Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL, address(noopHook), address(0), owner); + vm.prank(OWNER); + lsp7Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL, address(noopHook), address(0), OWNER); - vm.prank(owner); + vm.prank(OWNER); lsp7Token.enrollRemoteRouter(DESTINATION, address(remoteToken).addressToBytes32()); // from, to, amount, force, data - vm.prank(owner); - - lsp7Token.transfer(owner, ALICE, 1000e18, true, ""); + vm.prank(OWNER); + lsp7Token.transfer(OWNER, ALICE, 1000e18, true, ""); _enrollRemoteTokenRouter(); } function testInitialize_revert_ifAlreadyInitialized() public { vm.expectRevert("Initializable: contract is already initialized"); - lsp7Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL, address(noopHook), address(0), owner); + lsp7Token.initialize(TOTAL_SUPPLY, NAME, SYMBOL, address(noopHook), address(0), OWNER); } - function testTotalSupply() public { + function testTotalSupply() public view { assertEq(lsp7Token.totalSupply(), TOTAL_SUPPLY); } - function testDecimals() public { + function testDecimals() public view { assertEq(lsp7Token.decimals(), DECIMALS); } @@ -189,7 +186,7 @@ contract HypLSP7Test is HypTokenTest { } function testRemoteTransfer() public { - vm.prank(owner); + vm.prank(OWNER); remoteToken.enrollRemoteRouter(ORIGIN, address(localToken).addressToBytes32()); uint256 balanceBefore = lsp7Token.balanceOf(ALICE);