Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(contracts): minor token related changes #4580

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions solidity/contracts/client/GasRouter.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.6.11;

/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/

// ============ Internal Imports ============
import {Router} from "./Router.sol";
import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol";

abstract contract GasRouter is Router {
event GasSet(uint32 domain, uint256 gas);
aroralanuk marked this conversation as resolved.
Show resolved Hide resolved

// ============ Mutable Storage ============
mapping(uint32 => uint256) public destinationGas;

Expand Down Expand Up @@ -56,6 +71,7 @@ abstract contract GasRouter is Router {

function _setDestinationGas(uint32 domain, uint256 gas) internal {
destinationGas[domain] = gas;
emit GasSet(domain, gas);
}

function _GasRouter_dispatch(
Expand Down
4 changes: 4 additions & 0 deletions solidity/contracts/libs/TypeCasts.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ library TypeCasts {

// alignment preserving cast
function bytes32ToAddress(bytes32 _buf) internal pure returns (address) {
require(
uint256(_buf) <= uint256(type(uint160).max),
"TypeCasts: bytes32ToAddress overflow"
);
return address(uint160(uint256(_buf)));
}
}
16 changes: 16 additions & 0 deletions solidity/contracts/token/HypERC20Collateral.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0;

/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/

// ============ Internal Imports ============
import {TokenRouter} from "./libs/TokenRouter.sol";
import {TokenMessage} from "./libs/TokenMessage.sol";
import {MailboxClient} from "../client/MailboxClient.sol";

// ============ External Imports ============
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

Expand All @@ -22,6 +37,7 @@ contract HypERC20Collateral is TokenRouter {
* @param erc20 Address of the token to keep as collateral
*/
constructor(address erc20, address _mailbox) TokenRouter(_mailbox) {
require(Address.isContract(erc20), "HypERC20Collateral: invalid token");
wrappedToken = IERC20(erc20);
}

Expand Down
13 changes: 13 additions & 0 deletions solidity/contracts/token/extensions/HypERC4626.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,19 @@
// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/

// ============ Internal Imports ============
import {IXERC20} from "../interfaces/IXERC20.sol";
import {HypERC20} from "../HypERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
Expand Down
16 changes: 8 additions & 8 deletions solidity/contracts/token/extensions/HypERC4626Collateral.sol
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,7 @@ contract HypERC4626Collateral is HypERC20Collateral {
// Can't override _transferFromSender only because we need to pass shares in the token message
_transferFromSender(_amount);
uint256 _shares = _depositIntoVault(_amount);
uint256 _exchangeRate = PRECISION.mulDiv(
vault.totalAssets(),
vault.totalSupply(),
Math.Rounding.Down
);
uint256 _exchangeRate = vault.convertToAssets(PRECISION);
bytes memory _tokenMetadata = abi.encode(_exchangeRate);

bytes memory _tokenMessage = TokenMessage.format(
Expand Down Expand Up @@ -97,15 +93,19 @@ contract HypERC4626Collateral is HypERC20Collateral {
* @dev Update the exchange rate on the synthetic token by accounting for additional yield accrued to the underlying vault
* @param _destinationDomain domain of the vault
*/
function rebase(uint32 _destinationDomain) public payable {
function rebase(
uint32 _destinationDomain,
bytes calldata _hookMetadata,
address _hook
) public payable {
// force a rebase with an empty transfer to 0x1
_transferRemote(
_destinationDomain,
NULL_RECIPIENT,
0,
msg.value,
bytes(""),
address(0)
_hookMetadata,
_hook
);
}
}
2 changes: 2 additions & 0 deletions solidity/test/GasRouter.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ contract GasRouterTest is Test {
}

function testSetDestinationGas(uint256 gas) public {
vm.expectEmit(true, true, true, true);
emit GasRouter.GasSet(originDomain, gas);
setDestinationGas(remoteRouter, originDomain, gas);
assertEq(remoteRouter.destinationGas(originDomain), gas);

Expand Down
5 changes: 5 additions & 0 deletions solidity/test/token/HypERC20.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,11 @@ contract HypERC20CollateralTest is HypTokenTest {
_enrollRemoteTokenRouter();
}

function test_constructor_revert_ifInvalidToken() public {
vm.expectRevert("HypERC20Collateral: invalid token");
new HypERC20Collateral(address(0), address(localMailbox));
}

function testInitialize_revert_ifAlreadyInitialized() public {}

function testRemoteTransfer() public {
Expand Down
68 changes: 53 additions & 15 deletions solidity/test/token/HypERC4626Test.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
import {HypERC20} from "../../contracts/token/HypERC20.sol";
import {HypERC4626Collateral} from "../../contracts/token/extensions/HypERC4626Collateral.sol";
import {HypERC4626} from "../../contracts/token/extensions/HypERC4626.sol";
import {StandardHookMetadata} from "../../contracts/hooks/libs/StandardHookMetadata.sol";
import "../../contracts/test/ERC4626/ERC4626Test.sol";
import {ProtocolFee} from "../../contracts/hooks/ProtocolFee.sol";

contract HypERC4626CollateralTest is HypTokenTest {
using TypeCasts for address;
Expand Down Expand Up @@ -121,14 +123,38 @@

_accrueYield();

localRebasingToken.rebase(DESTINATION);
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
remoteMailbox.processNextInboundMessage();
assertEq(
assertApproxEqRelDecimal(
remoteToken.balanceOf(BOB),
transferAmount + _discountedYield()
transferAmount + _discountedYield(),
1e14,
0
);
}

function testRemoteTransfer_rebaseWithCustomHook() public {

Check failure

Code scanning / Olympix Integrated Security

Modifying state after making an external call may allow for reentrancy attacks. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/reentrancy Critical test

Modifying state after making an external call may allow for reentrancy attacks. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/reentrancy

Check notice

Code scanning / Olympix Integrated Security

Reentrant functions which emit events after making an external call may lead to out-of-order events. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/reentrancy-events Low test

Reentrant functions which emit events after making an external call may lead to out-of-order events. For more information, visit: http://detectors.olympixdevsectools.com/article/web3-vulnerability/reentrancy-events
_performRemoteTransferWithoutExpectation(0, transferAmount);
assertEq(remoteToken.balanceOf(BOB), transferAmount);

_accrueYield();

uint256 FEE = 1e18;
ProtocolFee customHook = new ProtocolFee(
FEE,
FEE,
address(this),
address(this)
);

localRebasingToken.rebase{value: FEE}(
DESTINATION,
StandardHookMetadata.overrideMsgValue(FEE),
address(customHook)
);
assertEq(address(customHook).balance, FEE);
}

function testRebaseWithTransfer() public {
_performRemoteTransferWithoutExpectation(0, transferAmount);
assertEq(remoteToken.balanceOf(BOB), transferAmount);
Expand Down Expand Up @@ -217,7 +243,7 @@

_accrueYield();

localRebasingToken.rebase(DESTINATION);
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
remoteMailbox.processNextInboundMessage();

// Use balance here since it might be off by <1bp
Expand Down Expand Up @@ -256,7 +282,7 @@
_accrueYield();
_accrueYield(); // earning 2x yield to be split

localRebasingToken.rebase(DESTINATION);
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
vm.prank(CAROL);

remoteToken.transferRemote(
Expand Down Expand Up @@ -293,7 +319,7 @@
// decrease collateral in vault by 10%
uint256 drawdown = 5e18;
primaryToken.burnFrom(address(vault), drawdown);
localRebasingToken.rebase(DESTINATION);
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
remoteMailbox.processNextInboundMessage();

// Use balance here since it might be off by <1bp
Expand All @@ -319,7 +345,7 @@

_accrueYield();

localRebasingToken.rebase(DESTINATION);
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
remoteMailbox.processNextInboundMessage();

vm.prank(BOB);
Expand All @@ -330,13 +356,23 @@
);
peerMailbox.processNextInboundMessage();

assertEq(remoteRebasingToken.exchangeRate(), 1045e7); // 5 * 0.9 = 4.5% yield
assertApproxEqRelDecimal(
remoteRebasingToken.exchangeRate(),
1045e7,
1e14,
0
); // 5 * 0.9 = 4.5% yield
assertEq(peerRebasingToken.exchangeRate(), 1e10); // assertingthat transfers by the synthetic variant don't impact the exchang rate

localRebasingToken.rebase(PEER_DESTINATION);
localRebasingToken.rebase(PEER_DESTINATION, bytes(""), address(0));
peerMailbox.processNextInboundMessage();

assertEq(peerRebasingToken.exchangeRate(), 1045e7); // asserting that the exchange rate is set finally by the collateral variant
assertApproxEqRelDecimal(
peerRebasingToken.exchangeRate(),
1045e7,
1e14,
0
); // asserting that the exchange rate is set finally by the collateral variant
}

function test_cyclicTransfers() public {
Expand All @@ -346,7 +382,7 @@

_accrueYield();

localRebasingToken.rebase(DESTINATION); // yield is added
localRebasingToken.rebase(DESTINATION, bytes(""), address(0)); // yield is added
remoteMailbox.processNextInboundMessage();

// BOB: remote -> peer(BOB) (yield is leftover)
Expand All @@ -358,7 +394,7 @@
);
peerMailbox.processNextInboundMessage();

localRebasingToken.rebase(PEER_DESTINATION);
localRebasingToken.rebase(PEER_DESTINATION, bytes(""), address(0));
peerMailbox.processNextInboundMessage();

// BOB: peer -> local(CAROL)
Expand Down Expand Up @@ -398,11 +434,13 @@

_accrueYield();

localRebasingToken.rebase(DESTINATION);
localRebasingToken.rebase(DESTINATION, bytes(""), address(0));
remoteMailbox.processNextInboundMessage();
assertEq(
assertApproxEqRelDecimal(
remoteToken.balanceOf(BOB),
transferAmount + _discountedYield()
transferAmount + _discountedYield(),
1e14,
0
);

vm.prank(address(localMailbox));
Expand Down
Loading