You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
238 lines
7.9 KiB
238 lines
7.9 KiB
2 years ago
|
// SPDX-License-Identifier: Apache-2.0
|
||
|
pragma solidity ^0.8.13;
|
||
|
|
||
|
import "forge-std/Test.sol";
|
||
2 years ago
|
import {LiquidityLayerRouter} from "../contracts/middleware/liquidity-layer/LiquidityLayerRouter.sol";
|
||
|
import {CircleBridgeAdapter} from "../contracts/middleware/liquidity-layer/adapters/CircleBridgeAdapter.sol";
|
||
2 years ago
|
import {MockToken} from "../contracts/mock/MockToken.sol";
|
||
|
import {TestTokenRecipient} from "../contracts/test/TestTokenRecipient.sol";
|
||
2 years ago
|
import {MockCircleMessageTransmitter} from "../contracts/mock/MockCircleMessageTransmitter.sol";
|
||
|
import {MockCircleBridge} from "../contracts/mock/MockCircleBridge.sol";
|
||
2 years ago
|
import {MockHyperlaneEnvironment} from "../contracts/mock/MockHyperlaneEnvironment.sol";
|
||
2 years ago
|
|
||
|
import {TypeCasts} from "../contracts/libs/TypeCasts.sol";
|
||
|
|
||
2 years ago
|
contract LiquidityLayerRouterTest is Test {
|
||
2 years ago
|
MockHyperlaneEnvironment testEnvironment;
|
||
|
|
||
2 years ago
|
LiquidityLayerRouter originLiquidityLayerRouter;
|
||
|
LiquidityLayerRouter destinationLiquidityLayerRouter;
|
||
2 years ago
|
|
||
2 years ago
|
MockCircleMessageTransmitter messageTransmitter;
|
||
|
MockCircleBridge circleBridge;
|
||
|
CircleBridgeAdapter originBridgeAdapter;
|
||
|
CircleBridgeAdapter destinationBridgeAdapter;
|
||
|
|
||
2 years ago
|
string bridge = "FooBridge";
|
||
|
|
||
|
uint32 originDomain = 123;
|
||
|
uint32 destinationDomain = 321;
|
||
|
|
||
|
TestTokenRecipient recipient;
|
||
|
MockToken token;
|
||
|
bytes messageBody = hex"beefdead";
|
||
|
uint256 amount = 420000;
|
||
|
|
||
2 years ago
|
event LiquidityLayerAdapterSet(string indexed bridge, address adapter);
|
||
2 years ago
|
|
||
|
function setUp() public {
|
||
|
token = new MockToken();
|
||
2 years ago
|
|
||
|
circleBridge = new MockCircleBridge(token);
|
||
|
messageTransmitter = new MockCircleMessageTransmitter(token);
|
||
|
originBridgeAdapter = new CircleBridgeAdapter();
|
||
|
destinationBridgeAdapter = new CircleBridgeAdapter();
|
||
|
|
||
2 years ago
|
recipient = new TestTokenRecipient();
|
||
|
|
||
2 years ago
|
originLiquidityLayerRouter = new LiquidityLayerRouter();
|
||
|
destinationLiquidityLayerRouter = new LiquidityLayerRouter();
|
||
2 years ago
|
|
||
|
testEnvironment = new MockHyperlaneEnvironment(
|
||
|
originDomain,
|
||
|
destinationDomain
|
||
|
);
|
||
|
|
||
|
// TODO: set IGP?
|
||
2 years ago
|
originLiquidityLayerRouter.initialize(
|
||
2 years ago
|
address(this),
|
||
|
address(testEnvironment.connectionManager(originDomain)),
|
||
|
address(0)
|
||
|
);
|
||
2 years ago
|
destinationLiquidityLayerRouter.initialize(
|
||
2 years ago
|
address(this),
|
||
|
address(testEnvironment.connectionManager(destinationDomain)),
|
||
|
address(0)
|
||
|
);
|
||
|
|
||
2 years ago
|
originLiquidityLayerRouter.enrollRemoteRouter(
|
||
2 years ago
|
destinationDomain,
|
||
2 years ago
|
TypeCasts.addressToBytes32(address(destinationLiquidityLayerRouter))
|
||
2 years ago
|
);
|
||
2 years ago
|
destinationLiquidityLayerRouter.enrollRemoteRouter(
|
||
2 years ago
|
originDomain,
|
||
2 years ago
|
TypeCasts.addressToBytes32(address(originLiquidityLayerRouter))
|
||
2 years ago
|
);
|
||
|
|
||
2 years ago
|
originBridgeAdapter.initialize(
|
||
|
address(this),
|
||
|
address(circleBridge),
|
||
|
address(messageTransmitter),
|
||
2 years ago
|
address(originLiquidityLayerRouter)
|
||
2 years ago
|
);
|
||
|
|
||
|
destinationBridgeAdapter.initialize(
|
||
|
address(this),
|
||
|
address(circleBridge),
|
||
|
address(messageTransmitter),
|
||
2 years ago
|
address(destinationLiquidityLayerRouter)
|
||
2 years ago
|
);
|
||
|
|
||
|
originBridgeAdapter.addToken(address(token), "USDC");
|
||
|
destinationBridgeAdapter.addToken(address(token), "USDC");
|
||
|
|
||
|
originBridgeAdapter.enrollRemoteRouter(
|
||
|
destinationDomain,
|
||
|
TypeCasts.addressToBytes32(address(destinationBridgeAdapter))
|
||
|
);
|
||
|
destinationBridgeAdapter.enrollRemoteRouter(
|
||
|
destinationDomain,
|
||
|
TypeCasts.addressToBytes32(address(originBridgeAdapter))
|
||
|
);
|
||
|
|
||
2 years ago
|
originLiquidityLayerRouter.setLiquidityLayerAdapter(
|
||
2 years ago
|
bridge,
|
||
2 years ago
|
address(originBridgeAdapter)
|
||
2 years ago
|
);
|
||
|
|
||
2 years ago
|
destinationLiquidityLayerRouter.setLiquidityLayerAdapter(
|
||
2 years ago
|
bridge,
|
||
2 years ago
|
address(destinationBridgeAdapter)
|
||
2 years ago
|
);
|
||
|
|
||
|
token.mint(address(this), amount);
|
||
|
}
|
||
|
|
||
2 years ago
|
function testSetLiquidityLayerAdapter() public {
|
||
|
// Expect the LiquidityLayerAdapterSet event.
|
||
2 years ago
|
// Expect topic0 & data to match
|
||
|
vm.expectEmit(true, false, false, true);
|
||
2 years ago
|
emit LiquidityLayerAdapterSet(bridge, address(originBridgeAdapter));
|
||
2 years ago
|
|
||
|
// Set the token bridge adapter
|
||
2 years ago
|
originLiquidityLayerRouter.setLiquidityLayerAdapter(
|
||
2 years ago
|
bridge,
|
||
2 years ago
|
address(originBridgeAdapter)
|
||
2 years ago
|
);
|
||
|
|
||
|
// Expect the bridge adapter to have been set
|
||
|
assertEq(
|
||
2 years ago
|
originLiquidityLayerRouter.liquidityLayerAdapters(bridge),
|
||
2 years ago
|
address(originBridgeAdapter)
|
||
2 years ago
|
);
|
||
|
}
|
||
|
|
||
|
// ==== dispatchWithTokens ====
|
||
|
|
||
|
function testDispatchWithTokensRevertsWithUnkownBridgeAdapter() public {
|
||
|
vm.expectRevert("No adapter found for bridge");
|
||
2 years ago
|
originLiquidityLayerRouter.dispatchWithTokens(
|
||
2 years ago
|
destinationDomain,
|
||
|
TypeCasts.addressToBytes32(address(recipient)),
|
||
|
messageBody,
|
||
|
address(token),
|
||
|
amount,
|
||
|
"BazBridge" // some unknown bridge name
|
||
|
);
|
||
|
}
|
||
|
|
||
|
function testDispatchWithTokensRevertsWithFailedTransferIn() public {
|
||
|
vm.expectRevert("ERC20: insufficient allowance");
|
||
2 years ago
|
originLiquidityLayerRouter.dispatchWithTokens(
|
||
2 years ago
|
destinationDomain,
|
||
|
TypeCasts.addressToBytes32(address(recipient)),
|
||
|
messageBody,
|
||
|
address(token),
|
||
|
amount,
|
||
|
bridge
|
||
|
);
|
||
|
}
|
||
|
|
||
|
function testDispatchWithTokenTransfersMovesTokens() public {
|
||
2 years ago
|
token.approve(address(originLiquidityLayerRouter), amount);
|
||
|
originLiquidityLayerRouter.dispatchWithTokens(
|
||
2 years ago
|
destinationDomain,
|
||
|
TypeCasts.addressToBytes32(address(recipient)),
|
||
|
messageBody,
|
||
|
address(token),
|
||
|
amount,
|
||
|
bridge
|
||
|
);
|
||
|
}
|
||
|
|
||
|
function testDispatchWithTokensCallsAdapter() public {
|
||
|
vm.expectCall(
|
||
2 years ago
|
address(originBridgeAdapter),
|
||
2 years ago
|
abi.encodeWithSelector(
|
||
2 years ago
|
originBridgeAdapter.sendTokens.selector,
|
||
2 years ago
|
destinationDomain,
|
||
|
TypeCasts.addressToBytes32(address(recipient)),
|
||
|
address(token),
|
||
|
amount
|
||
|
)
|
||
|
);
|
||
2 years ago
|
token.approve(address(originLiquidityLayerRouter), amount);
|
||
|
originLiquidityLayerRouter.dispatchWithTokens(
|
||
2 years ago
|
destinationDomain,
|
||
|
TypeCasts.addressToBytes32(address(recipient)),
|
||
|
messageBody,
|
||
|
address(token),
|
||
|
amount,
|
||
|
bridge
|
||
|
);
|
||
|
}
|
||
|
|
||
|
function testProcessingRevertsIfBridgeAdapterReverts() public {
|
||
2 years ago
|
token.approve(address(originLiquidityLayerRouter), amount);
|
||
|
originLiquidityLayerRouter.dispatchWithTokens(
|
||
2 years ago
|
destinationDomain,
|
||
|
TypeCasts.addressToBytes32(address(recipient)),
|
||
|
messageBody,
|
||
|
address(token),
|
||
|
amount,
|
||
|
bridge
|
||
|
);
|
||
|
|
||
2 years ago
|
vm.expectRevert("Circle message not processed yet");
|
||
2 years ago
|
testEnvironment.processNextPendingMessage();
|
||
|
}
|
||
|
|
||
|
function testDispatchWithTokensTransfersOnDestination() public {
|
||
2 years ago
|
token.approve(address(originLiquidityLayerRouter), amount);
|
||
|
originLiquidityLayerRouter.dispatchWithTokens(
|
||
2 years ago
|
destinationDomain,
|
||
|
TypeCasts.addressToBytes32(address(recipient)),
|
||
|
messageBody,
|
||
|
address(token),
|
||
|
amount,
|
||
|
bridge
|
||
|
);
|
||
|
|
||
2 years ago
|
bytes32 nonceId = messageTransmitter.hashSourceAndNonce(
|
||
|
destinationBridgeAdapter.hyperlaneDomainToCircleDomain(
|
||
|
originDomain
|
||
|
),
|
||
|
circleBridge.nextNonce()
|
||
|
);
|
||
|
|
||
|
messageTransmitter.process(
|
||
|
nonceId,
|
||
|
address(destinationBridgeAdapter),
|
||
|
amount
|
||
|
);
|
||
2 years ago
|
testEnvironment.processNextPendingMessage();
|
||
|
assertEq(recipient.lastData(), messageBody);
|
||
|
assertEq(token.balanceOf(address(recipient)), amount);
|
||
|
}
|
||
|
}
|