-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: complete bridge adapter invariants
- Loading branch information
1 parent
8080134
commit df5f6e8
Showing
4 changed files
with
182 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
pragma solidity >=0.8.9; | ||
|
||
/// library imports | ||
import {Vm, Test} from "forge-std/Test.sol"; | ||
|
||
/// local imports | ||
import "test/Setup.t.sol"; | ||
|
||
/// handler import | ||
import {BridgeAdapterHandler} from "test/invariant-tests/handlers/BridgeAdapter.Handler.sol"; | ||
|
||
/// @notice invariants for bridge adapters receiving messages | ||
contract BridgeAdapterInvariant is Setup { | ||
BridgeAdapterHandler public handler; | ||
|
||
/// @notice initializes the setup | ||
function setUp() public override { | ||
/// @dev calls setup to spin up test contracts | ||
super.setUp(); | ||
|
||
/// @dev selects fork and deploy the handlers | ||
vm.selectFork(fork[BSC_CHAIN_ID]); | ||
handler = new BridgeAdapterHandler( | ||
contractAddress[BSC_CHAIN_ID]["WORMHOLE_RECEIVER_ADAPTER"], | ||
contractAddress[BSC_CHAIN_ID]["AXELAR_RECEIVER_ADAPTER"], | ||
contractAddress[BSC_CHAIN_ID]["MMA_RECEIVER"] | ||
); | ||
|
||
/// @dev bind the handler as target for invariant | ||
targetContract(address(handler)); | ||
} | ||
|
||
function invariant_test_bridge_adapter_receivers() public { | ||
if (handler.success() && handler.lastBridge() == 1) { | ||
assertTrue( | ||
WormholeReceiverAdapter(contractAddress[BSC_CHAIN_ID]["WORMHOLE_RECEIVER_ADAPTER"]).isMessageExecuted( | ||
handler.lastMessageId() | ||
) | ||
); | ||
assertTrue( | ||
WormholeReceiverAdapter(contractAddress[BSC_CHAIN_ID]["WORMHOLE_RECEIVER_ADAPTER"]).deliveryHashStatus( | ||
handler.lastMessageHash() | ||
) | ||
); | ||
} | ||
|
||
if (handler.success() && handler.lastBridge() == 2) { | ||
assertTrue( | ||
AxelarReceiverAdapter(contractAddress[BSC_CHAIN_ID]["AXELAR_RECEIVER_ADAPTER"]).isMessageExecuted( | ||
handler.lastMessageId() | ||
) | ||
); | ||
assertTrue( | ||
AxelarReceiverAdapter(contractAddress[BSC_CHAIN_ID]["AXELAR_RECEIVER_ADAPTER"]).commandIdStatus( | ||
handler.lastMessageHash() | ||
) | ||
); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
test/invariant-tests/handlers/BridgeAdapter.Handler.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// SPDX-License-Identifier: GPL-3.0-only | ||
pragma solidity >=0.8.9; | ||
|
||
/// library imports | ||
import "forge-std/Test.sol"; | ||
|
||
/// local imports | ||
import {WormholeReceiverAdapter} from "src/adapters/wormhole/WormholeReceiverAdapter.sol"; | ||
import {AxelarReceiverAdapter} from "src/adapters/axelar/AxelarReceiverAdapter.sol"; | ||
import {AdapterPayload} from "src/libraries/Types.sol"; | ||
import "src/libraries/TypeCasts.sol"; | ||
import "src/libraries/Message.sol"; | ||
import "src/adapters/axelar/libraries/StringAddressConversion.sol"; | ||
|
||
/// @notice helper interface of axelar gateway to simulaate approve contract call | ||
interface IAxelarGateway { | ||
function approveContractCall(bytes calldata params, bytes32 commandId) external; | ||
|
||
function callContract(string calldata destinationChain, string calldata contractAddress, bytes calldata payload) | ||
external; | ||
} | ||
|
||
/// @notice handler for invariant testing receiver bridge adapters | ||
contract BridgeAdapterHandler is Test { | ||
/// @notice local state | ||
WormholeReceiverAdapter public wormhole; | ||
AxelarReceiverAdapter public axelar; | ||
|
||
address public multiBridgeReceiver; | ||
bytes32 public lastMessageId; | ||
bytes32 public lastMessageHash; | ||
|
||
uint256 public lastBridge; | ||
|
||
bool public success; | ||
|
||
/// @notice modifier to prank caller | ||
modifier prank(address _prankster) { | ||
vm.startPrank(_prankster); | ||
_; | ||
vm.stopPrank(); | ||
} | ||
|
||
/// @notice initial setup contracts | ||
constructor(address _wormhole, address _axelar, address _multiBridgeReceiver) { | ||
wormhole = WormholeReceiverAdapter(_wormhole); | ||
axelar = AxelarReceiverAdapter(_axelar); | ||
multiBridgeReceiver = _multiBridgeReceiver; | ||
} | ||
|
||
/// @notice helper for wormhole receivers | ||
function receiveWormholeMessages( | ||
AdapterPayload memory _payload, | ||
MessageLibrary.Message memory _data, | ||
bytes[] memory _vaas, | ||
bytes32 _deliveryHash | ||
) external prank(wormhole.relayer()) { | ||
success = false; | ||
vm.assume(_payload.msgId != bytes32(0)); | ||
|
||
lastBridge = 1; | ||
_payload.receiverAdapter = address(wormhole); | ||
_payload.finalDestination = multiBridgeReceiver; | ||
_data.dstChainId = 56; | ||
_data.srcChainId = 1; | ||
_payload.data = abi.encode(_data); | ||
|
||
lastMessageId = _payload.msgId; | ||
lastMessageHash = _deliveryHash; | ||
success = true; | ||
|
||
wormhole.receiveWormholeMessages( | ||
abi.encode(_payload), | ||
_vaas, | ||
TypeCasts.addressToBytes32(wormhole.senderAdapter()), | ||
wormhole.senderChainId(), | ||
_deliveryHash | ||
); | ||
} | ||
|
||
/// @notice helper for axelar receivers | ||
function receiveAxelarMessages( | ||
AdapterPayload memory _payload, | ||
MessageLibrary.Message memory _data, | ||
bytes32 _commandId | ||
) external prank(address(axelar.gateway())) { | ||
lastBridge = 2; | ||
_payload.receiverAdapter = address(axelar); | ||
_payload.finalDestination = multiBridgeReceiver; | ||
|
||
_data.dstChainId = 56; | ||
_data.srcChainId = 1; | ||
_payload.data = abi.encode(_data); | ||
|
||
/// @notice simulating the payload existence on axelar's gateway | ||
/// @dev helps us bypass NOT_APPROVED_BY_GATEWAY error on axelar adapter | ||
IAxelarGateway(address(axelar.gateway())).approveContractCall( | ||
abi.encode( | ||
"ethereum", | ||
StringAddressConversion.toString(address(axelar.senderAdapter())), | ||
address(axelar), | ||
keccak256(abi.encode(_payload)), | ||
bytes32(0), | ||
0 | ||
), | ||
_commandId | ||
); | ||
|
||
lastMessageId = _payload.msgId; | ||
lastMessageHash = _commandId; | ||
success = true; | ||
|
||
axelar.execute( | ||
_commandId, | ||
axelar.senderChainId(), | ||
StringAddressConversion.toString(axelar.senderAdapter()), | ||
abi.encode(_payload) | ||
); | ||
} | ||
} |