// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 2 of 7: GasUsage.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.18;import {Ownable} from"@openzeppelin/contracts/access/Ownable.sol";
import {IGasOracle} from"./interfaces/IGasOracle.sol";
/**
* @dev Contract module which allows children to store typical gas usage of a certain transaction on another chain.
*/abstractcontractGasUsageisOwnable{
IGasOracle internal gasOracle;
mapping(uint chainId =>uint amount) public gasUsage;
constructor(IGasOracle gasOracle_) {
gasOracle = gasOracle_;
}
/**
* @dev Sets the amount of gas used for a transaction on a given chain.
* @param chainId The ID of the chain.
* @param gasAmount The amount of gas used on the chain.
*/functionsetGasUsage(uint chainId, uint gasAmount) externalonlyOwner{
gasUsage[chainId] = gasAmount;
}
/**
* @dev Sets the Gas Oracle contract address.
* @param gasOracle_ The address of the Gas Oracle contract.
*/functionsetGasOracle(IGasOracle gasOracle_) externalonlyOwner{
gasOracle = gasOracle_;
}
/**
* @notice Get the gas cost of a transaction on another chain in the current chain's native token.
* @param chainId The ID of the chain for which to get the gas cost.
* @return The calculated gas cost of the transaction in the current chain's native token
*/functiongetTransactionCost(uint chainId) externalviewreturns (uint) {
unchecked {
return gasOracle.getTransactionGasCostInNativeToken(chainId, gasUsage[chainId]);
}
}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.18;import {Ownable} from"@openzeppelin/contracts/access/Ownable.sol";
import {IGasOracle} from"./interfaces/IGasOracle.sol";
import {IMessenger} from"./interfaces/IMessenger.sol";
import {GasUsage} from"./GasUsage.sol";
import {HashUtils} from"./libraries/HashUtils.sol";
/**
* @dev This contract implements the Allbridge messenger cross-chain communication protocol.
*/contractMessengerisOwnable, GasUsage, IMessenger{
usingHashUtilsforbytes32;
// current chain IDuintpublicimmutable chainId;
// supported destination chain IDsbytes32public otherChainIds;
// the primary account that is responsible for validation that a message has been sent on the source chainaddressprivate primaryValidator;
// the secondary accounts that are responsible for validation that a message has been sent on the source chainmapping(address=>bool) private secondaryValidators;
mapping(bytes32 messageHash =>uint blockNumber) publicoverride sentMessagesBlock;
mapping(bytes32 messageHash =>uint isReceived) publicoverride receivedMessages;
eventMessageSent(bytes32indexed message);
eventMessageReceived(bytes32indexed message);
/**
* @dev Emitted when the contract receives native gas tokens (e.g. Ether on the Ethereum network).
*/eventReceived(address, uint);
/**
* @dev Emitted when the mapping of secondary validators is updated.
*/eventSecondaryValidatorsSet(address[] oldValidators, address[] newValidators);
constructor(uint chainId_,
bytes32 otherChainIds_,
IGasOracle gasOracle_,
address primaryValidator_,
address[] memory validators
) GasUsage(gasOracle_) {
chainId = chainId_;
otherChainIds = otherChainIds_;
primaryValidator = primaryValidator_;
uint length = validators.length;
for (uint index; index < length; ) {
secondaryValidators[validators[index]] =true;
unchecked {
index++;
}
}
}
/**
* @notice Sends a message to another chain.
* @dev Emits a {MessageSent} event, which signals to the off-chain messaging service to invoke the `receiveMessage`
* function on the destination chain to deliver the message.
*
* Requirements:
*
* - the first byte of the message must be the current chain ID.
* - the second byte of the message must be the destination chain ID.
* - the same message cannot be sent second time.
* - messaging fee must be payed. (See `getTransactionCost` of the `GasUsage` contract).
* @param message The message to be sent to the destination chain.
*/functionsendMessage(bytes32 message) externalpayableoverride{
require(uint8(message[0]) == chainId, "Messenger: wrong chainId");
require(otherChainIds[uint8(message[1])] !=0, "Messenger: wrong destination");
bytes32 messageWithSender = message.hashWithSenderAddress(msg.sender);
require(sentMessagesBlock[messageWithSender] ==0, "Messenger: has message");
sentMessagesBlock[messageWithSender] =block.number;
require(msg.value>=this.getTransactionCost(uint8(message[1])), "Messenger: not enough fee");
emit MessageSent(messageWithSender);
}
/**
* @notice Delivers a message to the destination chain.
* @dev Emits an {MessageReceived} event indicating the message has been delivered.
*
* Requirements:
*
* - a valid signature of the primary validator.
* - a valid signature of one of the secondary validators.
* - the second byte of the message must be the current chain ID.
*/functionreceiveMessage(bytes32 message,
uint v1v2,
bytes32 r1,
bytes32 s1,
bytes32 r2,
bytes32 s2
) externaloverride{
bytes32 hashedMessage = message.hashed();
require(ecrecover(hashedMessage, uint8(v1v2 >>8), r1, s1) == primaryValidator, "Messenger: invalid primary");
require(secondaryValidators[ecrecover(hashedMessage, uint8(v1v2), r2, s2)], "Messenger: invalid secondary");
require(uint8(message[1]) == chainId, "Messenger: wrong chainId");
receivedMessages[message] =1;
emit MessageReceived(message);
}
/**
* @dev Allows the admin to withdraw the messaging fee collected in native gas tokens.
*/functionwithdrawGasTokens(uint amount) externalonlyOwner{
payable(msg.sender).transfer(amount);
}
/**
* @dev Allows the admin to set the primary validator address.
*/functionsetPrimaryValidator(address value) externalonlyOwner{
primaryValidator = value;
}
/**
* @dev Allows the admin to set the addresses of secondary validators.
*/functionsetSecondaryValidators(address[] memory oldValidators, address[] memory newValidators) externalonlyOwner{
uint length = oldValidators.length;
uint index;
for (; index < length; ) {
secondaryValidators[oldValidators[index]] =false;
unchecked {
index++;
}
}
length = newValidators.length;
index =0;
for (; index < length; ) {
secondaryValidators[newValidators[index]] =true;
unchecked {
index++;
}
}
emit SecondaryValidatorsSet(oldValidators, newValidators);
}
/**
* @dev Allows the admin to update a list of supported destination chain IDs
* @param value Each byte of the `value` parameter represents whether a chain ID with such index is supported
* as a valid message destination.
*/functionsetOtherChainIds(bytes32 value) externalonlyOwner{
otherChainIds = value;
}
fallback() externalpayable{
revert("Unsupported");
}
receive() externalpayable{
emit Received(msg.sender, msg.value);
}
}
Contract Source Code
File 7 of 7: Ownable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)pragmasolidity ^0.8.0;import"../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableisContext{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}