Skip to content

Commit

Permalink
test transfer
Browse files Browse the repository at this point in the history
  • Loading branch information
aroralanuk committed Nov 5, 2024
1 parent b5762d5 commit 6616b98
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 23 deletions.
9 changes: 6 additions & 3 deletions solidity/contracts/hooks/OPL2ToL1Hook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,10 @@ contract OPL2ToL1Hook is AbstractMessageIdAuthHook {
bytes calldata metadata,
bytes calldata message
) internal view override returns (uint256) {
bytes memory metadataWithGasLimit = metadata.overrideGasLimit(78_000);
return
metadata.msgValue(0) + childHook.quoteDispatch(metadata, message);
metadata.msgValue(0) +
childHook.quoteDispatch(metadataWithGasLimit, message);
}

// ============ Internal functions ============
Expand All @@ -83,9 +85,10 @@ contract OPL2ToL1Hook is AbstractMessageIdAuthHook {
(message.id(), metadata.msgValue(0))
);

bytes memory metadataWithGasLimit = metadata.overrideGasLimit(78_000);
childHook.postDispatch{
value: childHook.quoteDispatch(metadata, message)
}(metadata, message);
value: childHook.quoteDispatch(metadataWithGasLimit, message)
}(metadataWithGasLimit, message);
l2Messenger.sendMessage{value: metadata.msgValue(0)}(
TypeCasts.bytes32ToAddress(ism),
payload,
Expand Down
13 changes: 13 additions & 0 deletions solidity/contracts/hooks/libs/StandardHookMetadata.sol
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,17 @@ library StandardHookMetadata {
getCustomMetadata(_metadata)
);
}

function overrideGasLimit(
bytes calldata _metadata,
uint256 _gasLimit
) internal view returns (bytes memory) {
return
formatMetadata(
msgValue(_metadata, 0),
_gasLimit,
refundAddress(_metadata, msg.sender),
getCustomMetadata(_metadata)
);
}
}
23 changes: 15 additions & 8 deletions solidity/contracts/token/HypValue.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity >=0.8.0;
import {TokenRouter} from "./libs/TokenRouter.sol";
import {TokenMessage} from "./libs/TokenMessage.sol";
import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";

// Note: this assumes 1:1 exchange rate between source and destination chain
contract HypValue is TokenRouter {
Expand All @@ -23,14 +24,15 @@ contract HypValue is TokenRouter {
);
}

// use _hook with caution
function transferRemote(
uint32 _destination,
bytes32 _recipient,
uint256 _amount,
bytes calldata _hookMetadata,
address /*_hook*/
address _hook
) public payable virtual override returns (bytes32 messageId) {
_checkSufficientValue(_destination, _amount);
uint256 quote = _checkSufficientValue(_destination, _amount);

bytes memory hookMetadata = StandardHookMetadata.overrideMsgValue(
_hookMetadata,
Expand All @@ -42,9 +44,9 @@ contract HypValue is TokenRouter {
_destination,
_recipient,
_amount,
_amount,
_amount + quote,
hookMetadata,
address(hook)
_hook
);
}

Expand All @@ -53,7 +55,7 @@ contract HypValue is TokenRouter {
bytes32 _recipient,
uint256 _amount
) external payable virtual override returns (bytes32 messageId) {
_checkSufficientValue(_destination, _amount);
uint256 quote = _checkSufficientValue(_destination, _amount);
bytes memory hookMetadata = StandardHookMetadata.formatMetadata(
_amount,
destinationGas[_destination],
Expand All @@ -66,7 +68,7 @@ contract HypValue is TokenRouter {
_destination,
_recipient,
_amount,
_amount,
_amount + quote,
hookMetadata,
address(hook)
);
Expand All @@ -82,7 +84,9 @@ contract HypValue is TokenRouter {
address _recipient,
uint256 _amount,
bytes calldata // no metadata
) internal virtual override {}
) internal virtual override {
Address.sendValue(payable(_recipient), _amount);
}

function balanceOf(
address /* _account */
Expand All @@ -93,10 +97,13 @@ contract HypValue is TokenRouter {
function _checkSufficientValue(
uint32 _destination,
uint256 _amount
) internal view {
) internal view returns (uint256) {
uint256 quote = this.quoteGasPayment(_destination);
if (msg.value < _amount + quote) {
revert InsufficientValue(_amount + quote, msg.value);
}
return quote;
}

receive() external payable {}
}
99 changes: 87 additions & 12 deletions solidity/test/token/HypValue.t.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity >=0.8.0;

import "forge-std/console.sol";

import {TypeCasts} from "../../contracts/libs/TypeCasts.sol";
import {HypTokenTest} from "./HypERC20.t.sol";
import {HypERC20} from "../../contracts/token/HypERC20.sol";
import {TokenRouter} from "../../contracts/token/libs/TokenRouter.sol";
import {HypValue} from "../../contracts/token/HypValue.sol";
import {OPL2ToL1Hook} from "../../contracts/hooks/OPL2ToL1Hook.sol";
import {OPL2ToL1Ism} from "../../contracts/isms/hook/OPL2ToL1Ism.sol";
import {IOptimismPortal} from "../../contracts/interfaces/optimism/IOptimismPortal.sol";
import {ICrossDomainMessenger} from "../../contracts/interfaces/optimism/ICrossDomainMessenger.sol";
import {AbstractMessageIdAuthorizedIsm} from "../../contracts/isms/hook/AbstractMessageIdAuthorizedIsm.sol";
import {MockOptimismMessenger, MockOptimismPortal} from "../../contracts/mock/MockOptimism.sol";
import {TestInterchainGasPaymaster} from "../../contracts/test/TestInterchainGasPaymaster.sol";

Expand All @@ -15,7 +22,8 @@ contract HypValueTest is HypTokenTest {
address internal constant L2_MESSENGER_ADDRESS =
0x4200000000000000000000000000000000000007;

HypValue internal valueRouter;
HypValue internal localValueRouter;
HypValue internal remoteValueRouter;
OPL2ToL1Hook internal valueHook;
OPL2ToL1Ism internal ism;
TestInterchainGasPaymaster internal mockOverheadIgp;
Expand All @@ -29,8 +37,11 @@ contract HypValueTest is HypTokenTest {
address(new MockOptimismMessenger()).code
);

localToken = new HypValue(address(localMailbox));
valueRouter = HypValue(payable(address(localToken)));
localValueRouter = new HypValue(address(localMailbox));
remoteValueRouter = new HypValue(address(remoteMailbox));

localToken = TokenRouter(payable(address(localValueRouter)));
remoteToken = HypERC20(payable(address(remoteValueRouter)));

l1Messenger = new MockOptimismMessenger();
portal = new MockOptimismPortal();
Expand All @@ -46,13 +57,22 @@ contract HypValueTest is HypTokenTest {
address(mockOverheadIgp)
);

valueRouter.initialize(address(valueHook), address(ism), address(this));
localValueRouter.initialize(
address(valueHook),
address(ism),
address(this)
);
remoteValueRouter.initialize(
address(valueHook),
address(ism),
address(this)
);

valueRouter.enrollRemoteRouter(
localValueRouter.enrollRemoteRouter(
DESTINATION,
address(remoteToken).addressToBytes32()
);
remoteToken.enrollRemoteRouter(
remoteValueRouter.enrollRemoteRouter(
ORIGIN,
address(localToken).addressToBytes32()
);
Expand All @@ -61,18 +81,73 @@ contract HypValueTest is HypTokenTest {
}

function testRemoteTransfer() public {
// uint256 balanceBefore = localToken.balanceOf(ALICE);

uint256 quote = valueRouter.quoteGasPayment(DESTINATION);
uint256 quote = localValueRouter.quoteGasPayment(DESTINATION);
console.log("quote", quote);
uint256 msgValue = TRANSFER_AMT + quote;

// vm.prank(ALICE);
_performRemoteTransferWithEmit(msgValue, TRANSFER_AMT, quote);
// assertEq(localToken.balanceOf(ALICE), balanceBefore - TRANSFER_AMT);
vm.expectEmit(true, true, false, true);
emit TokenRouter.SentTransferRemote(
DESTINATION,
BOB.addressToBytes32(),
TRANSFER_AMT
);

vm.prank(ALICE);
bytes32 messageId = localToken.transferRemote{value: msgValue}(
DESTINATION,
BOB.addressToBytes32(),
TRANSFER_AMT
);

_externalBridgeDestinationCall(messageId, msgValue);

vm.expectEmit(true, true, false, true);
emit ReceivedTransferRemote(
ORIGIN,
BOB.addressToBytes32(),
TRANSFER_AMT
);
remoteMailbox.processNextInboundMessage();

assertEq(BOB.balance, TRANSFER_AMT);
assertEq(address(mockOverheadIgp).balance, quote);
}

function testTransfer_withHookSpecified(
uint256 fee,
bytes calldata metadata
) public override {}

function _externalBridgeDestinationCall(
bytes32 _messageId,
uint256 _msgValue
) internal {
bytes memory encodedHookData = abi.encodeCall(
AbstractMessageIdAuthorizedIsm.preVerifyMessage,
(_messageId, _msgValue)
);

bytes memory messengerCalldata = abi.encodeCall(
ICrossDomainMessenger.relayMessage,
(
0,
address(valueHook),
address(ism),
_msgValue,
uint256(100_000),
encodedHookData
)
);
vm.deal(address(portal), _msgValue);
IOptimismPortal.WithdrawalTransaction
memory withdrawal = IOptimismPortal.WithdrawalTransaction({
nonce: 0,
sender: L2_MESSENGER_ADDRESS,
target: address(l1Messenger),
value: _msgValue,
gasLimit: 100_000,
data: messengerCalldata
});
portal.finalizeWithdrawalTransaction(withdrawal);
}
}

0 comments on commit 6616b98

Please sign in to comment.