22pragma solidity 0.8.28 ;
33
44import {Test} from "forge-std/Test.sol " ;
5- import {console} from "forge-std/console.sol " ;
65
7- import {Bridge} from "../src/Bridge.sol " ;
6+ import {ERC1967Factory } from "solady/utils/ERC1967Factory.sol " ;
7+ import {LibClone} from "solady/utils/LibClone.sol " ;
8+ import {UpgradeableBeacon} from "solady/utils/UpgradeableBeacon.sol " ;
89
10+ import {Bridge} from "../src/Bridge.sol " ;
911import {CrossChainERC20} from "../src/CrossChainERC20.sol " ;
12+ import {CrossChainERC20Factory} from "../src/CrossChainERC20Factory.sol " ;
1013import {Twin} from "../src/Twin.sol " ;
11-
1214import {Call, CallType} from "../src/libraries/CallLib.sol " ;
1315import {MessageStorageLib} from "../src/libraries/MessageStorageLib.sol " ;
1416import {SVMBridgeLib} from "../src/libraries/SVMBridgeLib.sol " ;
1517import {Ix, Pubkey} from "../src/libraries/SVMLib.sol " ;
1618import {SolanaTokenType, TokenLib, Transfer} from "../src/libraries/TokenLib.sol " ;
1719
18- import {LibClone} from "solady/utils/LibClone.sol " ;
19- import {UpgradeableBeacon} from "solady/utils/UpgradeableBeacon.sol " ;
20-
2120contract BridgeTest is Test {
2221 Bridge public bridge;
2322 address public trustedRelayer;
@@ -46,13 +45,29 @@ contract BridgeTest is Test {
4645 user = makeAddr ("user " );
4746 unauthorizedUser = makeAddr ("unauthorizedUser " );
4847
49- // Deploy bridge
50- bridge = new Bridge (REMOTE_BRIDGE, trustedRelayer, initialOwner);
48+ ERC1967Factory f = new ERC1967Factory ();
49+
50+ Bridge bridgeImpl = new Bridge ({remoteBridge: REMOTE_BRIDGE, trustedRelayer: trustedRelayer});
51+ bridge = Bridge (
52+ f.deployAndCall ({
53+ implementation: address (bridgeImpl),
54+ admin: address (this ),
55+ data: abi.encodeCall (Bridge.initialize, (initialOwner))
56+ })
57+ );
58+
59+ address tokenImpl = address (new CrossChainERC20 (address (bridge)));
60+ address erc20Beacon =
61+ address (new UpgradeableBeacon ({initialOwner: address (this ), initialImplementation: tokenImpl}));
62+ CrossChainERC20Factory xChainERC20FactoryImpl = new CrossChainERC20Factory (erc20Beacon);
63+ CrossChainERC20Factory xChainERC20Factory =
64+ CrossChainERC20Factory (f.deploy ({implementation: address (xChainERC20FactoryImpl), admin: address (this )}));
65+ crossChainToken =
66+ CrossChainERC20 (xChainERC20Factory.deploy (Pubkey.unwrap (TEST_REMOTE_TOKEN), "Mock Token " , "MOCK " , 18 ));
5167
5268 // Deploy mock contracts
5369 mockToken = new MockERC20 ("Mock Token " , "MOCK " , 18 );
5470 mockTarget = new MockTarget ();
55- crossChainToken = new CrossChainERC20 (address (bridge));
5671
5772 // Set up balances
5873 vm.deal (address (bridge), 100 ether);
@@ -67,24 +82,23 @@ contract BridgeTest is Test {
6782 //////////////////////////////////////////////////////////////
6883
6984 function test_constructor_setsCorrectValues () public {
70- Bridge testBridge = new Bridge (TEST_SENDER, trustedRelayer, initialOwner );
85+ Bridge testBridge = new Bridge (TEST_SENDER, trustedRelayer);
7186
7287 assertEq (Pubkey.unwrap (testBridge.REMOTE_BRIDGE ()), Pubkey.unwrap (TEST_SENDER));
7388 assertEq (testBridge.TRUSTED_RELAYER (), trustedRelayer);
74- assertTrue (testBridge.TWIN_BEACON () != address (0 ));
7589 assertEq (testBridge.nextIncomingNonce (), 0 );
7690 }
7791
7892 function test_constructor_deploysTwinBeacon () public view {
79- address twinBeacon = bridge.TWIN_BEACON ();
93+ address twinBeacon = bridge.twinBeacon ();
8094 assertTrue (twinBeacon != address (0 ));
8195
8296 UpgradeableBeacon beacon = UpgradeableBeacon (twinBeacon);
8397 assertTrue (beacon.implementation () != address (0 ));
8498 }
8599
86100 function test_constructor_withZeroAddresses () public {
87- Bridge testBridge = new Bridge (Pubkey.wrap (0 ), address (0 ), address ( 0 ) );
101+ Bridge testBridge = new Bridge (Pubkey.wrap (0 ), address (0 ));
88102
89103 assertEq (Pubkey.unwrap (testBridge.REMOTE_BRIDGE ()), 0 );
90104 assertEq (testBridge.TRUSTED_RELAYER (), address (0 ));
@@ -298,12 +312,12 @@ contract BridgeTest is Test {
298312 }
299313
300314 function test_relayMessages_revertsOnAlreadySuccessfulMessage () public {
301- // First, create a message that will fail with trusted relayer due to low gas
315+ // First, create a message that will succeed with trusted relayer
302316 Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage [](1 );
303317 messages[0 ] = Bridge.IncomingMessage ({
304318 nonce: 0 ,
305319 sender: TEST_SENDER,
306- gasLimit: 1000000 , // Low gas to cause failure
320+ gasLimit: 1000000 ,
307321 ty: Bridge.MessageType.Call,
308322 data: abi.encode (
309323 Call ({
@@ -317,11 +331,11 @@ contract BridgeTest is Test {
317331
318332 bytes memory ismData = hex "" ;
319333
320- // First attempt by trusted relayer should fail due to low gas
334+ // First attempt by trusted relayer should succeed
321335 vm.prank (trustedRelayer);
322336 bridge.relayMessages (messages, ismData);
323337
324- // Now try the same message again with non-trusted relayer - should revert with
338+ // Now try the exact same message again with non-trusted relayer - should revert with
325339 // MessageAlreadySuccessfullyRelayed
326340 vm.expectRevert (Bridge.MessageAlreadySuccessfullyRelayed.selector );
327341 vm.prank (unauthorizedUser);
@@ -420,15 +434,9 @@ contract BridgeTest is Test {
420434 }
421435
422436 function test_relayMessage_transferType () public {
423- // Deploy a test cross-chain token that can be initialized
424- TestCrossChainERC20 testToken = new TestCrossChainERC20 (address (bridge));
425-
426- // Initialize it as the bridge
427- vm.prank (address (bridge));
428- testToken.initialize (Pubkey.unwrap (TEST_REMOTE_TOKEN), "Test Token " , "TEST " , 18 );
429-
437+ // Use the crossChainToken already deployed in setUp
430438 Transfer memory transfer = Transfer ({
431- localToken: address (testToken ),
439+ localToken: address (crossChainToken ),
432440 remoteToken: TEST_REMOTE_TOKEN,
433441 to: bytes32 (bytes20 (user)), // Left-align the address in bytes32
434442 remoteAmount: 100e6
@@ -448,19 +456,13 @@ contract BridgeTest is Test {
448456 vm.prank (trustedRelayer);
449457 bridge.relayMessages (messages, ismData);
450458
451- assertEq (testToken .balanceOf (user), 100e6 );
459+ assertEq (crossChainToken .balanceOf (user), 100e6 );
452460 }
453461
454462 function test_relayMessage_transferAndCallType () public {
455- // Deploy a test cross-chain token for this test
456- TestCrossChainERC20 testToken2 = new TestCrossChainERC20 (address (bridge));
457-
458- // Initialize it as the bridge
459- vm.prank (address (bridge));
460- testToken2.initialize (Pubkey.unwrap (TEST_REMOTE_TOKEN), "Test Token 2 " , "TEST2 " , 18 );
461-
463+ // Use the crossChainToken already deployed in setUp
462464 Transfer memory transfer = Transfer ({
463- localToken: address (testToken2 ),
465+ localToken: address (crossChainToken ),
464466 remoteToken: TEST_REMOTE_TOKEN,
465467 to: bytes32 (bytes20 (user)), // Left-align the address in bytes32
466468 remoteAmount: 100e6
@@ -487,7 +489,7 @@ contract BridgeTest is Test {
487489 vm.prank (trustedRelayer);
488490 bridge.relayMessages (messages, ismData);
489491
490- assertEq (testToken2 .balanceOf (user), 100e6 );
492+ assertEq (crossChainToken .balanceOf (user), 100e6 );
491493 assertEq (mockTarget.value (), 456 );
492494 }
493495
@@ -532,7 +534,7 @@ contract BridgeTest is Test {
532534
533535 vm.expectRevert (Bridge.SenderIsNotEntrypoint.selector );
534536 vm.prank (user);
535- bridge.__validateAndRelay (message, true , 0 );
537+ bridge.__validateAndRelay (message, true );
536538 }
537539
538540 function test_relayMessage_revertsOnDirectCall () public {
@@ -718,11 +720,32 @@ contract BridgeTest is Test {
718720 }
719721
720722 function testFuzz_relayMessage_withDifferentNonces (uint64 nonce ) public {
721- vm.assume (nonce < 1000 ); // Limit to reasonable range to avoid overflow
723+ vm.assume (nonce < 100 ); // Limit to a smaller range to avoid excessive gas usage
722724
723- // Set the next nonce to match
724- _setNextNonce (nonce);
725+ // Increment the nonce naturally by sending messages
726+ for (uint64 i = 0 ; i < nonce; i++ ) {
727+ Bridge.IncomingMessage[] memory tempMessages = new Bridge.IncomingMessage [](1 );
728+ tempMessages[0 ] = Bridge.IncomingMessage ({
729+ nonce: i,
730+ sender: TEST_SENDER,
731+ gasLimit: 1000000 ,
732+ ty: Bridge.MessageType.Call,
733+ data: abi.encode (
734+ Call ({
735+ ty: CallType.Call,
736+ to: address (mockTarget),
737+ value: 0 ,
738+ data: abi.encodeWithSelector (MockTarget.setValue.selector , i)
739+ })
740+ )
741+ });
725742
743+ bytes memory tempIsmData = hex "" ;
744+ vm.prank (trustedRelayer);
745+ bridge.relayMessages (tempMessages, tempIsmData);
746+ }
747+
748+ // Now send the actual test message
726749 Bridge.IncomingMessage[] memory messages = new Bridge.IncomingMessage [](1 );
727750 messages[0 ] = Bridge.IncomingMessage ({
728751 nonce: nonce,
@@ -770,11 +793,6 @@ contract BridgeTest is Test {
770793 vm.prank (trustedRelayer);
771794 bridge.relayMessages (messages, ismData);
772795 }
773-
774- function _setNextNonce (uint64 nonce ) internal {
775- // Set the nextIncomingNonce storage slot (slot 3 in Bridge contract)
776- vm.store (address (bridge), bytes32 (uint256 (3 )), bytes32 (uint256 (nonce)));
777- }
778796}
779797
780798//////////////////////////////////////////////////////////////
@@ -843,91 +861,3 @@ contract MockTarget {
843861 revert ("This function always reverts " );
844862 }
845863}
846-
847- contract TestCrossChainERC20 {
848- address private _bridge;
849- string private _name;
850- string private _symbol;
851- uint8 private _decimals;
852- bytes32 private _remoteToken;
853-
854- mapping (address => uint256 ) private _balances;
855- mapping (address => mapping (address => uint256 )) private _allowances;
856- uint256 private _totalSupply;
857-
858- modifier onlyBridge () {
859- require (msg .sender == _bridge, "Not bridge " );
860- _;
861- }
862-
863- constructor (address bridge_ ) {
864- _bridge = bridge_;
865- }
866-
867- function initialize (bytes32 remoteToken_ , string memory name_ , string memory symbol_ , uint8 decimals_ ) external {
868- _remoteToken = remoteToken_;
869- _name = name_;
870- _symbol = symbol_;
871- _decimals = decimals_;
872- }
873-
874- function bridge () external view returns (address ) {
875- return _bridge;
876- }
877-
878- function remoteToken () external view returns (bytes32 ) {
879- return _remoteToken;
880- }
881-
882- function name () external view returns (string memory ) {
883- return _name;
884- }
885-
886- function symbol () external view returns (string memory ) {
887- return _symbol;
888- }
889-
890- function decimals () external view returns (uint8 ) {
891- return _decimals;
892- }
893-
894- function totalSupply () external view returns (uint256 ) {
895- return _totalSupply;
896- }
897-
898- function balanceOf (address account ) external view returns (uint256 ) {
899- return _balances[account];
900- }
901-
902- function allowance (address owner , address spender ) external view returns (uint256 ) {
903- return _allowances[owner][spender];
904- }
905-
906- function mint (address to , uint256 amount ) external onlyBridge {
907- _balances[to] += amount;
908- _totalSupply += amount;
909- }
910-
911- function burn (address from , uint256 amount ) external onlyBridge {
912- _balances[from] -= amount;
913- _totalSupply -= amount;
914- }
915-
916- function transfer (address to , uint256 amount ) external returns (bool ) {
917- _balances[msg .sender ] -= amount;
918- _balances[to] += amount;
919- return true ;
920- }
921-
922- function transferFrom (address from , address to , uint256 amount ) external returns (bool ) {
923- _allowances[from][msg .sender ] -= amount;
924- _balances[from] -= amount;
925- _balances[to] += amount;
926- return true ;
927- }
928-
929- function approve (address spender , uint256 amount ) external returns (bool ) {
930- _allowances[msg .sender ][spender] = amount;
931- return true ;
932- }
933- }
0 commit comments