Skip to content

Commit 35ef80c

Browse files
authored
evm: Check forks on all entrypoints (#378)
* evm: Check forks on all entrypoints * Add negative tests
1 parent 2a0fd86 commit 35ef80c

File tree

3 files changed

+125
-3
lines changed

3 files changed

+125
-3
lines changed

evm/src/NttManager/NttManager.sol

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,6 @@ contract NttManager is INttManager, RateLimiter, ManagerBase {
196196
bytes32 sourceNttManagerAddress,
197197
TransceiverStructs.NttManagerMessage memory message
198198
) public whenNotPaused {
199-
// verify chain has not forked
200-
checkFork(evmChainId);
201-
202199
(bytes32 digest, bool alreadyExecuted) =
203200
_isMessageExecuted(sourceChainId, sourceNttManagerAddress, message);
204201

@@ -394,6 +391,9 @@ contract NttManager is INttManager, RateLimiter, ManagerBase {
394391
revert NotEnoughCapacity(getCurrentOutboundCapacity(), amount);
395392
}
396393
if (shouldQueue && isAmountRateLimited) {
394+
// verify chain has not forked
395+
checkFork(evmChainId);
396+
397397
// emit an event to notify the user that the transfer is rate limited
398398
emit OutboundTransferRateLimited(
399399
msg.sender, sequence, amount, getCurrentOutboundCapacity()
@@ -444,6 +444,9 @@ contract NttManager is INttManager, RateLimiter, ManagerBase {
444444
address sender,
445445
bytes memory transceiverInstructions
446446
) internal returns (uint64 msgSequence) {
447+
// verify chain has not forked
448+
checkFork(evmChainId);
449+
447450
(
448451
address[] memory enabledTransceivers,
449452
TransceiverStructs.TransceiverInstruction[] memory instructions,
@@ -503,6 +506,9 @@ contract NttManager is INttManager, RateLimiter, ManagerBase {
503506
TrimmedAmount amount,
504507
bool cancelled
505508
) internal {
509+
// verify chain has not forked
510+
checkFork(evmChainId);
511+
506512
// calculate proper amount of tokens to unlock/mint to recipient
507513
// untrim the amount
508514
uint256 untrimmedAmount = amount.untrim(tokenDecimals());

evm/test/IntegrationStandalone.t.sol

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,7 @@ contract TestEndToEndBase is Test, IRateLimiterEvents {
529529
vm.startPrank(userA);
530530
token1.approve(address(nttManagerChain1), sendingAmount);
531531

532+
vm.chainId(chainId1);
532533
vm.recordLogs();
533534

534535
// Send token through standard means (not relayer)

evm/test/NttManager.t.sol

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import "../src/interfaces/IManagerBase.sol";
1111
import "../src/interfaces/IRateLimiterEvents.sol";
1212
import "../src/NttManager/TransceiverRegistry.sol";
1313
import "../src/libraries/PausableUpgradeable.sol";
14+
import "../src/libraries/TransceiverHelpers.sol";
1415
import {Utils} from "./libraries/Utils.sol";
1516

1617
import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
@@ -787,6 +788,120 @@ contract TestNttManager is Test, IRateLimiterEvents {
787788
e2.receiveMessage(encodedEm);
788789
}
789790

791+
function test_transfersOnForkedChains() public {
792+
uint256 evmChainId = block.chainid;
793+
794+
address user_A = address(0x123);
795+
address user_B = address(0x456);
796+
797+
DummyToken token = DummyToken(nttManager.token());
798+
799+
uint8 decimals = token.decimals();
800+
801+
nttManager.setPeer(
802+
TransceiverHelpersLib.SENDING_CHAIN_ID,
803+
toWormholeFormat(address(nttManagerOther)),
804+
9,
805+
type(uint64).max
806+
);
807+
nttManager.setOutboundLimit(0);
808+
809+
token.mintDummy(address(user_A), 5 * 10 ** decimals);
810+
811+
vm.startPrank(user_A);
812+
813+
token.approve(address(nttManager), 3 * 10 ** decimals);
814+
815+
uint64 sequence = nttManager.transfer(
816+
1 * 10 ** decimals,
817+
TransceiverHelpersLib.SENDING_CHAIN_ID,
818+
toWormholeFormat(user_B),
819+
toWormholeFormat(user_A),
820+
true,
821+
new bytes(1)
822+
);
823+
824+
vm.warp(vm.getBlockTimestamp() + 1 days);
825+
826+
vm.chainId(chainId);
827+
828+
// Queued outbound transfers can't be completed
829+
vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId));
830+
nttManager.completeOutboundQueuedTransfer(sequence);
831+
832+
// Queued outbound transfers can't be cancelled
833+
vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId));
834+
nttManager.cancelOutboundQueuedTransfer(sequence);
835+
836+
// Outbound transfers fail when queued
837+
vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId));
838+
nttManager.transfer(
839+
1 * 10 ** decimals,
840+
TransceiverHelpersLib.SENDING_CHAIN_ID,
841+
toWormholeFormat(user_B),
842+
toWormholeFormat(user_A),
843+
true,
844+
new bytes(1)
845+
);
846+
vm.stopPrank();
847+
848+
nttManager.setOutboundLimit(packTrimmedAmount(type(uint64).max, 8).untrim(decimals));
849+
// Outbound transfers fail when not queued
850+
vm.prank(user_A);
851+
vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId));
852+
nttManager.transfer(
853+
1 * 10 ** decimals,
854+
TransceiverHelpersLib.SENDING_CHAIN_ID,
855+
toWormholeFormat(user_B),
856+
toWormholeFormat(user_A),
857+
false,
858+
new bytes(1)
859+
);
860+
861+
// INBOUND
862+
863+
bytes memory tokenTransferMessage = TransceiverStructs.encodeNativeTokenTransfer(
864+
TransceiverStructs.NativeTokenTransfer({
865+
amount: packTrimmedAmount(100, 8),
866+
sourceToken: toWormholeFormat(address(token)),
867+
to: toWormholeFormat(user_B),
868+
toChain: chainId
869+
})
870+
);
871+
872+
bytes memory transceiverMessage;
873+
TransceiverStructs.NttManagerMessage memory nttManagerMessage;
874+
(nttManagerMessage, transceiverMessage) = TransceiverHelpersLib
875+
.buildTransceiverMessageWithNttManagerPayload(
876+
0,
877+
toWormholeFormat(address(0x1)),
878+
toWormholeFormat(address(nttManagerOther)),
879+
toWormholeFormat(address(nttManager)),
880+
tokenTransferMessage
881+
);
882+
883+
// Inbound transfers can't be completed
884+
vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId));
885+
dummyTransceiver.receiveMessage(transceiverMessage);
886+
887+
// Inbound queued transfers can't be completed
888+
nttManager.setInboundLimit(0, TransceiverHelpersLib.SENDING_CHAIN_ID);
889+
890+
vm.chainId(evmChainId);
891+
892+
bytes32 hash = TransceiverStructs.nttManagerMessageDigest(
893+
TransceiverHelpersLib.SENDING_CHAIN_ID, nttManagerMessage
894+
);
895+
dummyTransceiver.receiveMessage(transceiverMessage);
896+
897+
vm.chainId(chainId);
898+
899+
vm.warp(vm.getBlockTimestamp() + 1 days);
900+
901+
vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId));
902+
nttManager.completeInboundQueuedTransfer(hash);
903+
}
904+
790905
// TODO:
791906
// currently there is no way to test the threshold logic and the duplicate
792907
// protection logic without setting up the business logic as well.

0 commit comments

Comments
 (0)