// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
import "./Ownable.sol";
/**
* @title AccessControl
* @dev This abstract contract implements access control mechanism based on roles.
* Each role can have one or more addresses associated with it, which are granted
* permission to execute functions with the onlyRole modifier.
*/
abstract contract AccessControl is Ownable {
/**
* @dev A mapping of roles to a mapping of addresses to boolean values indicating whether or not they have the role.
*/
mapping(bytes32 => mapping(address => bool)) private _permits;
/**
* @dev Emitted when a role is granted to an address.
*/
event RoleGranted(bytes32 indexed role, address indexed grantee);
/**
* @dev Emitted when a role is revoked from an address.
*/
event RoleRevoked(bytes32 indexed role, address indexed revokee);
/**
* @dev Error message thrown when an address does not have permission to execute a function with onlyRole modifier.
*/
error NoPermit(bytes32 role);
/**
* @dev Constructor that sets the owner of the contract.
*/
constructor(address owner_) Ownable(owner_) {}
/**
* @dev Modifier that restricts access to addresses having roles
* Throws an error if the caller do not have permit
*/
modifier onlyRole(bytes32 role) {
if (!_permits[role][msg.sender]) revert NoPermit(role);
_;
}
/**
* @dev Checks and reverts if an address do not have a specific role.
* @param role_ The role to check.
* @param address_ The address to check.
*/
function _checkRole(bytes32 role_, address address_) internal virtual {
if (!_hasRole(role_, address_)) revert NoPermit(role_);
}
/**
* @dev Grants a role to a given address.
* @param role_ The role to grant.
* @param grantee_ The address to grant the role to.
* Emits a RoleGranted event.
* Can only be called by the owner of the contract.
*/
function grantRole(
bytes32 role_,
address grantee_
) external virtual onlyOwner {
_grantRole(role_, grantee_);
}
/**
* @dev Revokes a role from a given address.
* @param role_ The role to revoke.
* @param revokee_ The address to revoke the role from.
* Emits a RoleRevoked event.
* Can only be called by the owner of the contract.
*/
function revokeRole(
bytes32 role_,
address revokee_
) external virtual onlyOwner {
_revokeRole(role_, revokee_);
}
/**
* @dev Internal function to grant a role to a given address.
* @param role_ The role to grant.
* @param grantee_ The address to grant the role to.
* Emits a RoleGranted event.
*/
function _grantRole(bytes32 role_, address grantee_) internal {
_permits[role_][grantee_] = true;
emit RoleGranted(role_, grantee_);
}
/**
* @dev Internal function to revoke a role from a given address.
* @param role_ The role to revoke.
* @param revokee_ The address to revoke the role from.
* Emits a RoleRevoked event.
*/
function _revokeRole(bytes32 role_, address revokee_) internal {
_permits[role_][revokee_] = false;
emit RoleRevoked(role_, revokee_);
}
/**
* @dev Checks whether an address has a specific role.
* @param role_ The role to check.
* @param address_ The address to check.
* @return A boolean value indicating whether or not the address has the role.
*/
function hasRole(
bytes32 role_,
address address_
) external view returns (bool) {
return _hasRole(role_, address_);
}
function _hasRole(
bytes32 role_,
address address_
) internal view returns (bool) {
return _permits[role_][address_];
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
import "./AccessControl.sol";
/**
* @title AccessControlExtended
* @dev This contract extends the functionality of the AccessControl contract by adding
* the ability to grant and revoke roles based on a combination of role name and a chain slug.
* It also provides batch operations for granting and revoking roles.
*/
contract AccessControlExtended is AccessControl {
/**
* @dev Constructor that sets the owner of the contract.
*/
constructor(address owner_) AccessControl(owner_) {}
/**
* @dev thrown when array lengths are not equal
*/
error UnequalArrayLengths();
/**
* @dev Checks if an address has the role.
* @param roleName_ The name of the role.
* @param chainSlug_ The chain slug associated with the role.
* @param address_ The address to be granted the role.
*/
function _checkRoleWithSlug(
bytes32 roleName_,
uint256 chainSlug_,
address address_
) internal virtual {
bytes32 roleHash = keccak256(abi.encode(roleName_, chainSlug_));
if (!_hasRole(roleHash, address_)) revert NoPermit(roleHash);
}
/**
* @dev Grants a role to an address based on the role name and chain slug.
* @param roleName_ The name of the role.
* @param chainSlug_ The chain slug associated with the role.
* @param grantee_ The address to be granted the role.
*/
function grantRoleWithSlug(
bytes32 roleName_,
uint32 chainSlug_,
address grantee_
) external virtual onlyOwner {
_grantRoleWithSlug(roleName_, chainSlug_, grantee_);
}
/**
* @dev Grants multiple roles to multiple addresses in batch.
* @param roleNames_ The names of the roles to grant.
* @param slugs_ The slugs for chain specific roles. For roles which are not chain-specific, we can use slug = 0
* @param grantees_ The addresses to be granted the roles.
*/
function grantBatchRole(
bytes32[] calldata roleNames_,
uint32[] calldata slugs_,
address[] calldata grantees_
) external virtual onlyOwner {
if (
roleNames_.length != grantees_.length ||
roleNames_.length != slugs_.length
) revert UnequalArrayLengths();
uint256 totalRoles = roleNames_.length;
for (uint256 index = 0; index < totalRoles; ) {
if (slugs_[index] > 0)
_grantRoleWithSlug(
roleNames_[index],
slugs_[index],
grantees_[index]
);
else _grantRole(roleNames_[index], grantees_[index]);
// inputs are controlled by owner
unchecked {
++index;
}
}
}
/**
* @dev Revokes multiple roles from multiple addresses in batch.
* @param roleNames_ The names of the roles to revoke.
* @param slugs_ The slugs for chain specific roles. For roles which are not chain-specific, we can use slug = 0
* @param grantees_ The addresses to be revoked the roles.
*/
function revokeBatchRole(
bytes32[] calldata roleNames_,
uint32[] calldata slugs_,
address[] calldata grantees_
) external virtual onlyOwner {
if (
roleNames_.length != grantees_.length ||
roleNames_.length != slugs_.length
) revert UnequalArrayLengths();
uint256 totalRoles = roleNames_.length;
for (uint256 index = 0; index < totalRoles; ) {
if (slugs_[index] > 0)
_revokeRoleWithSlug(
roleNames_[index],
slugs_[index],
grantees_[index]
);
else _revokeRole(roleNames_[index], grantees_[index]);
// inputs are controlled by owner
unchecked {
++index;
}
}
}
function _grantRoleWithSlug(
bytes32 roleName_,
uint32 chainSlug_,
address grantee_
) internal {
_grantRole(keccak256(abi.encode(roleName_, chainSlug_)), grantee_);
}
/**
* @dev Checks if an address has a role based on the role name and chain slug.
* @param roleName_ The name of the role.
* @param chainSlug_ The chain slug associated with the role.
* @param address_ The address to check for the role.
* @return A boolean indicating whether the address has the specified role.
*/
function hasRoleWithSlug(
bytes32 roleName_,
uint32 chainSlug_,
address address_
) external view returns (bool) {
return _hasRoleWithSlug(roleName_, chainSlug_, address_);
}
function _hasRoleWithSlug(
bytes32 roleName_,
uint32 chainSlug_,
address address_
) internal view returns (bool) {
return _hasRole(keccak256(abi.encode(roleName_, chainSlug_)), address_);
}
/**
* @dev Revokes roles from an address
* @param roleName_ The names of the roles to revoke.
* @param chainSlug_ The chain slug associated with the role.
* @param grantee_ The addresses to be revoked the roles.
*/
function revokeRoleWithSlug(
bytes32 roleName_,
uint32 chainSlug_,
address grantee_
) external virtual onlyOwner {
_revokeRoleWithSlug(roleName_, chainSlug_, grantee_);
}
function _revokeRoleWithSlug(
bytes32 roleName_,
uint32 chainSlug_,
address revokee_
) internal {
_revokeRole(keccak256(abi.encode(roleName_, chainSlug_)), revokee_);
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
// contains role hashes used in socket dl for various different operations
// used to rescue funds
bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE");
// used to withdraw fees
bytes32 constant WITHDRAW_ROLE = keccak256("WITHDRAW_ROLE");
// used to trip switchboards
bytes32 constant TRIP_ROLE = keccak256("TRIP_ROLE");
// used to un trip switchboards
bytes32 constant UN_TRIP_ROLE = keccak256("UN_TRIP_ROLE");
// used by governance
bytes32 constant GOVERNANCE_ROLE = keccak256("GOVERNANCE_ROLE");
//used by executors which executes message at destination
bytes32 constant EXECUTOR_ROLE = keccak256("EXECUTOR_ROLE");
// used by transmitters who seal and propose packets in socket
bytes32 constant TRANSMITTER_ROLE = keccak256("TRANSMITTER_ROLE");
// used by switchboard watchers who work against transmitters
bytes32 constant WATCHER_ROLE = keccak256("WATCHER_ROLE");
// used by fee updaters responsible for updating fees at switchboards, transmit manager and execution manager
bytes32 constant FEES_UPDATER_ROLE = keccak256("FEES_UPDATER_ROLE");
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
/**
* @title ICapacitor
* @dev Interface for a Capacitor contract that stores and manages messages in packets
*/
interface ICapacitor {
/**
* @notice adds the packed message to a packet
* @dev this should be only executable by socket
* @param packedMessage the message packed with payload, fees and config
*/
function addPackedMessage(bytes32 packedMessage) external;
/**
* @notice returns the latest packet details which needs to be sealed
* @return root root hash of the latest packet which is not yet sealed
* @return packetCount latest packet id which is not yet sealed
*/
function getNextPacketToBeSealed()
external
view
returns (bytes32 root, uint64 packetCount);
/**
* @notice returns the root of packet for given id
* @param id the id assigned to packet
* @return root root hash corresponding to given id
*/
function getRootByCount(uint64 id) external view returns (bytes32 root);
/**
* @notice returns the maxPacketLength
* @return maxPacketLength of the capacitor
*/
function getMaxPacketLength()
external
view
returns (uint256 maxPacketLength);
/**
* @notice seals the packet
* @dev indicates the packet is ready to be shipped and no more messages can be added now.
* @dev this should be called by socket only
* @param batchSize_ used with packet batching capacitors
* @return root root hash of the packet
* @return packetCount id of the packed sealed
*/
function sealPacket(
uint256 batchSize_
) external returns (bytes32 root, uint64 packetCount);
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
import "./ICapacitor.sol";
import "./IDecapacitor.sol";
/**
* @title ICapacitorFactory
* @notice Interface for a factory contract that deploys new instances of `ICapacitor` and `IDecapacitor` contracts.
*/
interface ICapacitorFactory {
/**
* @dev Emitted when an invalid capacitor type is requested during deployment.
*/
error InvalidCapacitorType();
/**
* @notice Deploys a new instance of an `ICapacitor` and `IDecapacitor` contract with the specified parameters.
* @param capacitorType The type of the capacitor to be deployed.
* @param siblingChainSlug The identifier of the sibling chain.
* @param maxPacketLength The maximum length of a packet.
* @return Returns the deployed `ICapacitor` and `IDecapacitor` contract instances.
*/
function deploy(
uint256 capacitorType,
uint32 siblingChainSlug,
uint256 maxPacketLength
) external returns (ICapacitor, IDecapacitor);
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
/**
* @title IDecapacitor interface
* @notice Interface for a contract that verifies if a packed message is part of a packet or not
*/
interface IDecapacitor {
/**
* @notice Returns true if packed message is part of root.
* @param root_ root hash of the packet.
* @param packedMessage_ packed message which needs to be verified.
* @param proof_ proof used to determine the inclusion
* @dev This function is kept as view instead of pure, as in future we may have stateful decapacitors
* @return isIncluded boolean indicating whether the message is included in the packet or not.
*/
function verifyMessageInclusion(
bytes32 root_,
bytes32 packedMessage_,
bytes calldata proof_
) external returns (bool isIncluded);
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
/**
* @title Execution Manager Interface
* @dev This interface defines the functions for managing and executing transactions on external chains
* @dev It is also responsible for collecting all the socket fees, which can then be pulled by others
*/
interface IExecutionManager {
/**
* @notice Returns the executor of the packed message and whether the executor is authorized
* @param packedMessage The message packed with payload, fees and config
* @param sig The signature of the message
* @return The address of the executor and a boolean indicating if the executor is authorized
*/
function isExecutor(
bytes32 packedMessage,
bytes memory sig
) external view returns (address, bool);
/**
* @notice Pays the fees for executing a transaction on the external chain
* @dev This function is payable and assumes the socket is going to send correct amount of fees.
* @param minMsgGasLimit_ The minimum gas limit for the transaction
* @param payloadSize_ The payload size in bytes
* @param executionParams_ Extra params for execution
* @param transmissionParams_ Extra params for transmission
* @param siblingChainSlug_ Sibling chain identifier
* @param switchboardFees_ fee charged by switchboard for processing transaction
* @param verificationOverheadFees_ fee charged for verifying transaction
* @param transmitManager_ The transmitManager address
* @param switchboard_ The switchboard address
* @param maxPacketLength_ The maxPacketLength for the capacitor
*/
function payAndCheckFees(
uint256 minMsgGasLimit_,
uint256 payloadSize_,
bytes32 executionParams_,
bytes32 transmissionParams_,
uint32 siblingChainSlug_,
uint128 switchboardFees_,
uint128 verificationOverheadFees_,
address transmitManager_,
address switchboard_,
uint256 maxPacketLength_
) external payable returns (uint128, uint128);
/**
* @notice Returns the minimum fees required for executing a transaction on the external chain
* @param minMsgGasLimit_ minMsgGasLimit_
* @param siblingChainSlug_ The destination slug
* @return The minimum fees required for executing the transaction
*/
function getMinFees(
uint256 minMsgGasLimit_,
uint256 payloadSize_,
bytes32 executionParams_,
uint32 siblingChainSlug_
) external view returns (uint128);
/**
* @notice function for getting the minimum fees required for executing and transmitting a cross-chain transaction
* @dev this function is called at source to calculate the execution cost.
* @param payloadSize_ byte length of payload. Currently only used to check max length, later on will be used for fees calculation.
* @param executionParams_ Can be used for providing extra information. Currently used for msgValue
* @param siblingChainSlug_ Sibling chain identifier
* @return minExecutionFee : Minimum fees required for executing the transaction
*/
function getExecutionTransmissionMinFees(
uint256 minMsgGasLimit_,
uint256 payloadSize_,
bytes32 executionParams_,
bytes32 transmissionParams_,
uint32 siblingChainSlug_,
address transmitManager_
) external view returns (uint128, uint128);
/**
* @notice Updates the execution fees for an executor and message ID
* @param executor The executor address
* @param executionFees The execution fees to update
* @param msgId The ID of the message
*/
function updateExecutionFees(
address executor,
uint128 executionFees,
bytes32 msgId
) external;
/**
* @notice updates the transmission fee
* @param remoteChainSlug_ sibling chain identifier
* @param transmitMinFees_ transmission fees collected
*/
function setTransmissionMinFees(
uint32 remoteChainSlug_,
uint128 transmitMinFees_
) external;
/**
* @notice sets the minimum execution fees required for executing at `siblingChainSlug_`
* @dev this function currently sets the price for a constant msg gas limit and payload size
* @param nonce_ incremental id to prevent signature replay
* @param siblingChainSlug_ sibling chain identifier
* @param executionFees_ total fees where price in destination native token is converted to source native tokens
* @param signature_ signature of fee updater
*/
function setExecutionFees(
uint256 nonce_,
uint32 siblingChainSlug_,
uint128 executionFees_,
bytes calldata signature_
) external;
/**
* @notice sets the min limit for msg value for `siblingChainSlug_`
* @param nonce_ incremental id to prevent signature replay
* @param siblingChainSlug_ sibling chain identifier
* @param msgValueMinThreshold_ min msg value
* @param signature_ signature of fee updater
*/
function setMsgValueMinThreshold(
uint256 nonce_,
uint32 siblingChainSlug_,
uint256 msgValueMinThreshold_,
bytes calldata signature_
) external;
/**
* @notice sets the max limit for msg value for `siblingChainSlug_`
* @param nonce_ incremental id to prevent signature replay
* @param siblingChainSlug_ sibling chain identifier
* @param msgValueMaxThreshold_ max msg value
* @param signature_ signature of fee updater
*/
function setMsgValueMaxThreshold(
uint256 nonce_,
uint32 siblingChainSlug_,
uint256 msgValueMaxThreshold_,
bytes calldata signature_
) external;
/**
* @notice sets the relative token price for `siblingChainSlug_`
* @dev this function is expected to be called frequently to match the original prices
* @param nonce_ incremental id to prevent signature replay
* @param siblingChainSlug_ sibling chain identifier
* @param relativeNativeTokenPrice_ relative price
* @param signature_ signature of fee updater
*/
function setRelativeNativeTokenPrice(
uint256 nonce_,
uint32 siblingChainSlug_,
uint256 relativeNativeTokenPrice_,
bytes calldata signature_
) external;
/**
* @notice called by socket while executing message to validate if the msg value provided is enough
* @param executionParams_ a bytes32 string where first byte gives param type (if value is 0 or not)
* and remaining bytes give the msg value needed
* @param msgValue_ msg.value to be sent with inbound
*/
function verifyParams(
bytes32 executionParams_,
uint256 msgValue_
) external view;
/**
* @notice withdraws switchboard fees from contract
* @param siblingChainSlug_ withdraw fees corresponding to this slug
* @param amount_ withdraw amount
*/
function withdrawSwitchboardFees(
uint32 siblingChainSlug_,
address switchboard_,
uint128 amount_
) external;
/**
* @dev this function gets the transmitManager address from the socket contract. If it is ever upgraded in socket,
* @dev remove the fees from executionManager first, and then upgrade address at socket.
* @notice withdraws transmission fees from contract
* @param siblingChainSlug_ withdraw fees corresponding to this slug
* @param amount_ withdraw amount
*/
function withdrawTransmissionFees(
uint32 siblingChainSlug_,
uint128 amount_
) external;
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
import "./ISocket.sol";
/**
* @title IHasher
* @notice Interface for hasher contract that calculates the packed message
*/
interface IHasher {
/**
* @notice returns the bytes32 hash of the message packed
* @param srcChainSlug src chain slug
* @param srcPlug address of plug at source
* @param dstChainSlug remote chain slug
* @param dstPlug address of plug at remote
* @param messageDetails contains message details, see ISocket for more details
*/
function packMessage(
uint32 srcChainSlug,
address srcPlug,
uint32 dstChainSlug,
address dstPlug,
ISocket.MessageDetails memory messageDetails
) external returns (bytes32);
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
/**
* @title IPlug
* @notice Interface for a plug contract that executes the message received from a source chain.
*/
interface IPlug {
/**
* @dev this should be only executable by socket
* @notice executes the message received from source chain
* @notice It is expected to have original sender checks in the destination plugs using payload
* @param srcChainSlug_ chain slug of source
* @param payload_ the data which is needed by plug at inbound call on remote
*/
function inbound(
uint32 srcChainSlug_,
bytes calldata payload_
) external payable;
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
import "./ITransmitManager.sol";
import "./IExecutionManager.sol";
/**
* @title ISocket
* @notice An interface for a cross-chain communication contract
* @dev This interface provides methods for transmitting and executing messages between chains,
* connecting a plug to a remote chain and setting up switchboards for the message transmission
* This interface also emits events for important operations such as message transmission, execution status,
* and plug connection
*/
interface ISocket {
/**
* @notice A struct containing fees required for message transmission and execution
* @param transmissionFees fees needed for transmission
* @param switchboardFees fees needed by switchboard
* @param executionFee fees needed for execution
*/
struct Fees {
uint128 transmissionFees;
uint128 executionFee;
uint128 switchboardFees;
}
/**
* @title MessageDetails
* @dev This struct defines the details of a message to be executed in a Decapacitor contract.
*/
struct MessageDetails {
// A unique identifier for the message.
bytes32 msgId;
// The fee to be paid for executing the message.
uint256 executionFee;
// The min amount of gas that can be used to execute the message.
uint256 minMsgGasLimit;
// The extra params which might provide msg value and additional info needed for message exec
bytes32 executionParams;
// The payload data to be executed in the message.
bytes payload;
}
/**
* @title ExecutionDetails
* @dev This struct defines the execution details
*/
struct ExecutionDetails {
// packet id
bytes32 packetId;
// proposal count
uint256 proposalCount;
// gas limit needed to execute inbound
uint256 executionGasLimit;
// proof data required by the Decapacitor contract to verify the message's authenticity
bytes decapacitorProof;
// signature of executor
bytes signature;
}
/**
* @notice emits the status of message after inbound call
* @param msgId msg id which is executed
*/
event ExecutionSuccess(bytes32 msgId);
/**
* @notice emits the config set by a plug for a remoteChainSlug
* @param plug address of plug on current chain
* @param siblingChainSlug sibling chain slug
* @param siblingPlug address of plug on sibling chain
* @param inboundSwitchboard inbound switchboard (select from registered options)
* @param outboundSwitchboard outbound switchboard (select from registered options)
* @param capacitor capacitor selected based on outbound switchboard
* @param decapacitor decapacitor selected based on inbound switchboard
*/
event PlugConnected(
address plug,
uint32 siblingChainSlug,
address siblingPlug,
address inboundSwitchboard,
address outboundSwitchboard,
address capacitor,
address decapacitor
);
/**
* @notice registers a message
* @dev Packs the message and includes it in a packet with capacitor
* @param remoteChainSlug_ the remote chain slug
* @param minMsgGasLimit_ the gas limit needed to execute the payload on remote
* @param payload_ the data which is needed by plug at inbound call on remote
*/
function outbound(
uint32 remoteChainSlug_,
uint256 minMsgGasLimit_,
bytes32 executionParams_,
bytes32 transmissionParams_,
bytes calldata payload_
) external payable returns (bytes32 msgId);
/**
* @notice executes a message
* @param executionDetails_ the packet details, proof and signature needed for message execution
* @param messageDetails_ the message details
*/
function execute(
ISocket.ExecutionDetails calldata executionDetails_,
ISocket.MessageDetails calldata messageDetails_
) external payable;
/**
* @notice seals data in capacitor for specific batchSize
* @param batchSize_ size of batch to be sealed
* @param capacitorAddress_ address of capacitor
* @param signature_ signed Data needed for verification
*/
function seal(
uint256 batchSize_,
address capacitorAddress_,
bytes calldata signature_
) external payable;
/**
* @notice proposes a packet
* @param packetId_ packet id
* @param root_ root data
* @param switchboard_ The address of switchboard for which this packet is proposed
* @param signature_ signed Data needed for verification
*/
function proposeForSwitchboard(
bytes32 packetId_,
bytes32 root_,
address switchboard_,
bytes calldata signature_
) external payable;
/**
* @notice sets the config specific to the plug
* @param siblingChainSlug_ the sibling chain slug
* @param siblingPlug_ address of plug present at sibling chain to call inbound
* @param inboundSwitchboard_ the address of switchboard to use for receiving messages
* @param outboundSwitchboard_ the address of switchboard to use for sending messages
*/
function connect(
uint32 siblingChainSlug_,
address siblingPlug_,
address inboundSwitchboard_,
address outboundSwitchboard_
) external;
/**
* @notice deploy capacitor and decapacitor for a switchboard with a specified max packet length, sibling chain slug, and capacitor type.
* @param siblingChainSlug_ The slug of the sibling chain that the switchboard is registered with.
* @param maxPacketLength_ The maximum length of a packet allowed by the switchboard.
* @param capacitorType_ The type of capacitor that the switchboard uses.
* @param siblingSwitchboard_ The switchboard address deployed on `siblingChainSlug_`
*/
function registerSwitchboardForSibling(
uint32 siblingChainSlug_,
uint256 maxPacketLength_,
uint256 capacitorType_,
address siblingSwitchboard_
) external returns (address capacitor, address decapacitor);
/**
* @notice Emits the sibling switchboard for given `siblingChainSlug_`.
* @dev This function is expected to be only called by switchboard.
* @dev the event emitted is tracked by transmitters to decide which switchboard a packet should be proposed on
* @param siblingChainSlug_ The slug of the sibling chain
* @param siblingSwitchboard_ The switchboard address deployed on `siblingChainSlug_`
*/
function useSiblingSwitchboard(
uint32 siblingChainSlug_,
address siblingSwitchboard_
) external;
/**
* @notice Retrieves the packet id roots for a specified packet id.
* @param packetId_ The packet id for which to retrieve the root.
* @param proposalCount_ The proposal id for packetId_ for which to retrieve the root.
* @param switchboard_ The address of switchboard for which this packet is proposed
* @return The packet id roots for the specified packet id.
*/
function packetIdRoots(
bytes32 packetId_,
uint256 proposalCount_,
address switchboard_
) external view returns (bytes32);
/**
* @notice Retrieves the latest proposalCount for a packet id.
* @return The proposal count for the specified packet id.
*/
function proposalCount(bytes32 packetId_) external view returns (uint256);
/**
* @notice Retrieves the minimum fees required for a message with a specified gas limit and destination chain.
* @param minMsgGasLimit_ The gas limit of the message.
* @param remoteChainSlug_ The slug of the destination chain for the message.
* @param plug_ The address of the plug through which the message is sent.
* @return totalFees The minimum fees required for the specified message.
*/
function getMinFees(
uint256 minMsgGasLimit_,
uint256 payloadSize_,
bytes32 executionParams_,
bytes32 transmissionParams_,
uint32 remoteChainSlug_,
address plug_
) external view returns (uint256 totalFees);
/// return instance of transmit manager
function transmitManager__() external view returns (ITransmitManager);
/// return instance of execution manager
function executionManager__() external view returns (IExecutionManager);
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
/**
* @title ISwitchboard
* @dev The interface for a switchboard contract that is responsible for verification of packets between
* different blockchain networks.
*/
interface ISwitchboard {
/**
* @notice Registers itself in Socket for given `siblingChainSlug_`.
* @dev This function is expected to be only called by admin as it handles the capacitor config for given chain
* @param siblingChainSlug_ The slug of the sibling chain to register switchboard with.
* @param maxPacketLength_ The maximum length of a packet allowed by the switchboard.
* @param capacitorType_ The type of capacitor that the switchboard uses.
* @param initialPacketCount_ The packet count at the time of registering switchboard. Packets with packet count below this won't be allowed
* @param siblingSwitchboard_ The switchboard address deployed on `siblingChainSlug_`
*/
function registerSiblingSlug(
uint32 siblingChainSlug_,
uint256 maxPacketLength_,
uint256 capacitorType_,
uint256 initialPacketCount_,
address siblingSwitchboard_
) external;
/**
* @notice Updates the sibling switchboard for given `siblingChainSlug_`.
* @dev This function is expected to be only called by admin
* @param siblingChainSlug_ The slug of the sibling chain to register switchboard with.
* @param siblingSwitchboard_ The switchboard address deployed on `siblingChainSlug_`
*/
function updateSibling(
uint32 siblingChainSlug_,
address siblingSwitchboard_
) external;
/**
* @notice Checks if a packet can be allowed to go through the switchboard.
* @param root the packet root.
* @param packetId The unique identifier for the packet.
* @param proposalCount The unique identifier for a proposal for the packet.
* @param srcChainSlug The unique identifier for the source chain of the packet.
* @param proposeTime The time when the packet was proposed.
* @return A boolean indicating whether the packet is allowed to go through the switchboard or not.
*/
function allowPacket(
bytes32 root,
bytes32 packetId,
uint256 proposalCount,
uint32 srcChainSlug,
uint256 proposeTime
) external view returns (bool);
/**
* @notice Retrieves the minimum fees required for the destination chain to process the packet.
* @param dstChainSlug the unique identifier for the destination chain of the packet.
* @return switchboardFee the switchboard fee required for the destination chain to process the packet.
* @return verificationOverheadFees the verification fee required for the destination chain to process the packet.
*/
function getMinFees(
uint32 dstChainSlug
)
external
view
returns (uint128 switchboardFee, uint128 verificationOverheadFees);
/**
* @notice Receives the fees for processing of packet.
* @param siblingChainSlug_ the chain slug of the sibling chain.
*/
function receiveFees(uint32 siblingChainSlug_) external payable;
/**
* @notice Sets the minimum fees required for the destination chain to process the packet.
* @param nonce_ the nonce of fee Updater to avoid replay.
* @param dstChainSlug_ the unique identifier for the destination chain.
* @param switchboardFees_ the switchboard fee required for the destination chain to process the packet.
* @param verificationOverheadFees_ the verification fee required for the destination chain to process the packet.
* @param signature_ the signature of the request.
* @dev not important to override in all switchboards
*/
function setFees(
uint256 nonce_,
uint32 dstChainSlug_,
uint128 switchboardFees_,
uint128 verificationOverheadFees_,
bytes calldata signature_
) external;
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
/**
* @title ITransmitManager
* @dev The interface for a transmit manager contract
*/
interface ITransmitManager {
/**
* @notice Checks if a given transmitter is authorized to send transactions to the destination chain.
* @param siblingSlug The unique identifier for the sibling chain.
* @param digest The digest of the message being signed.
* @param signature The signature of the message being signed.
* @return The address of the transmitter and a boolean indicating whether the transmitter is authorized or not.
*/
function checkTransmitter(
uint32 siblingSlug,
bytes32 digest,
bytes calldata signature
) external view returns (address, bool);
/**
* @notice sets the transmission fee needed to transmit message to given `siblingSlug_`
* @dev recovered address should add have feeUpdater role for `siblingSlug_`
* @param nonce_ The incremental nonce to prevent signature replay
* @param siblingSlug_ sibling id for which fee updater is registered
* @param transmissionFees_ digest which is signed by transmitter
* @param signature_ signature
*/
function setTransmissionFees(
uint256 nonce_,
uint32 siblingSlug_,
uint128 transmissionFees_,
bytes calldata signature_
) external;
/**
* @notice receives fees from Execution manager
* @dev this function can be used to keep track of fees received for each slug
* @param siblingSlug_ sibling id for which fee updater is registered
*/
function receiveFees(uint32 siblingSlug_) external payable;
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
/**
* @title Ownable
* @dev The Ownable contract provides a simple way to manage ownership of a contract
* and allows for ownership to be transferred to a nominated address.
*/
abstract contract Ownable {
address private _owner;
address private _nominee;
event OwnerNominated(address indexed nominee);
event OwnerClaimed(address indexed claimer);
error OnlyOwner();
error OnlyNominee();
/**
* @dev Sets the contract's owner to the address that is passed to the constructor.
*/
constructor(address owner_) {
_claimOwner(owner_);
}
/**
* @dev Modifier that restricts access to only the contract's owner.
* Throws an error if the caller is not the owner.
*/
modifier onlyOwner() {
if (msg.sender != _owner) revert OnlyOwner();
_;
}
/**
* @dev Returns the current owner of the contract.
*/
function owner() external view returns (address) {
return _owner;
}
/**
* @dev Returns the current nominee for ownership of the contract.
*/
function nominee() external view returns (address) {
return _nominee;
}
/**
* @dev Allows the current owner to nominate a new owner for the contract.
* Throws an error if the caller is not the owner.
* Emits an `OwnerNominated` event with the address of the nominee.
*/
function nominateOwner(address nominee_) external {
if (msg.sender != _owner) revert OnlyOwner();
_nominee = nominee_;
emit OwnerNominated(_nominee);
}
/**
* @dev Allows the nominated owner to claim ownership of the contract.
* Throws an error if the caller is not the nominee.
* Sets the nominated owner as the new owner of the contract.
* Emits an `OwnerClaimed` event with the address of the new owner.
*/
function claimOwner() external {
if (msg.sender != _nominee) revert OnlyNominee();
_claimOwner(msg.sender);
}
/**
* @dev Internal function that sets the owner of the contract to the specified address
* and sets the nominee to address(0).
*/
function _claimOwner(address claimer_) internal {
_owner = claimer_;
_nominee = address(0);
emit OwnerClaimed(claimer_);
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
import "lib/solmate/src/utils/SafeTransferLib.sol";
error ZeroAddress();
/**
* @title RescueFundsLib
* @dev A library that provides a function to rescue funds from a contract.
*/
library RescueFundsLib {
/**
* @dev The address used to identify ETH.
*/
address public constant ETH_ADDRESS =
address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
/**
* @dev thrown when the given token address don't have any code
*/
error InvalidTokenAddress();
/**
* @dev Rescues funds from a contract.
* @param token_ The address of the token contract.
* @param rescueTo_ The address of the user.
* @param amount_ The amount of tokens to be rescued.
*/
function rescueFunds(
address token_,
address rescueTo_,
uint256 amount_
) internal {
if (rescueTo_ == address(0)) revert ZeroAddress();
if (token_ == ETH_ADDRESS) {
SafeTransferLib.safeTransferETH(rescueTo_, amount_);
} else {
if (token_.code.length == 0) revert InvalidTokenAddress();
SafeTransferLib.safeTransfer(ERC20(token_), rescueTo_, amount_);
}
}
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
success := and(
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data.
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
// Counterintuitively, this call must be positioned second to the or() call in the
// surrounding and() call or else returndatasize() will be zero during the computation.
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
import "./SocketDst.sol";
import {SocketSrc} from "./SocketSrc.sol";
/**
* @title Socket
* @notice Core-contract containing all the core-socket utilities.
* @dev This contract inherits from SocketSrc and SocketDst
*/
contract Socket is SocketSrc, SocketDst {
/*
* @notice constructor for creating a new Socket contract instance.
* @param chainSlug_ The unique identifier of the chain this socket is deployed on.
* @param hasher_ The address of the Hasher contract used to pack the message before transmitting them.
* @param capacitorFactory_ The address of the CapacitorFactory contract used to create new Capacitor and DeCapacitor contracts.
* @param owner_ The address of the owner who has the initial admin role.
* @param version_ The version string which is hashed and stored in socket.
*/
constructor(
uint32 chainSlug_,
address hasher_,
address capacitorFactory_,
address owner_,
string memory version_
) AccessControlExtended(owner_) SocketBase(chainSlug_, version_) {
hasher__ = IHasher(hasher_);
capacitorFactory__ = ICapacitorFactory(capacitorFactory_);
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
import "../interfaces/IHasher.sol";
import "../libraries/RescueFundsLib.sol";
import "../utils/AccessControlExtended.sol";
import {RESCUE_ROLE, GOVERNANCE_ROLE} from "../utils/AccessRoles.sol";
import "./SocketConfig.sol";
/**
* @title SocketBase
* @notice A contract that is responsible for common storage for src and dest contracts, governance
* setters and inherits SocketConfig
*/
abstract contract SocketBase is SocketConfig, AccessControlExtended {
// Version string for this socket instance
bytes32 public immutable version;
// ChainSlug for this deployed socket instance
uint32 public immutable chainSlug;
// Counter for messages going outbound from current chain
uint64 public globalMessageCount;
/**
* @dev constructs a new Socket contract instance.
* @param chainSlug_ the chain slug of the contract.
* @param version_ the string to identify current version.
*/
constructor(uint32 chainSlug_, string memory version_) {
chainSlug = chainSlug_;
version = keccak256(bytes(version_));
}
////////////////////////////////////////////////////////
//////////// PERIPHERY CONTRACT CONNECTORS ////////////
////////////////////////////////////////////////////////
// Hasher contract
IHasher public hasher__;
// Transmit Manager contract
ITransmitManager public override transmitManager__;
// Execution Manager contract
IExecutionManager public override executionManager__;
////////////////////////////////////////////////////////
////////////////////// ERRORS //////////////////////////
////////////////////////////////////////////////////////
/**
* @dev Error thrown when non-transmitter tries to seal/propose
*/
error InvalidTransmitter();
////////////////////////////////////////////////////////
////////////////////// EVENTS //////////////////////////
////////////////////////////////////////////////////////
/**
* @notice An event that is emitted when the capacitor factory is updated.
* @param capacitorFactory The address of the new capacitorFactory.
*/
event CapacitorFactorySet(address capacitorFactory);
/**
* @notice An event that is emitted when the hasher is updated.
* @param hasher The address of the new hasher.
*/
event HasherSet(address hasher);
/**
* @notice An event that is emitted when the executionManager is updated.
* @param executionManager The address of the new executionManager.
*/
event ExecutionManagerSet(address executionManager);
/**
* @notice An event that is emitted when a new transmitManager contract is set
* @param transmitManager address of new transmitManager contract
*/
event TransmitManagerSet(address transmitManager);
//////////////////////////////////////////////////
//////////// GOV Permissioned setters ////////////
//////////////////////////////////////////////////
/**
* @dev Set the capacitor factory contract
* @dev Only governance can call this function
* @param capacitorFactory_ The address of the capacitor factory contract
*/
function setCapacitorFactory(
address capacitorFactory_
) external onlyRole(GOVERNANCE_ROLE) {
capacitorFactory__ = ICapacitorFactory(capacitorFactory_);
emit CapacitorFactorySet(capacitorFactory_);
}
/**
* @notice updates hasher__
* @dev Only governance can call this function
* @param hasher_ address of hasher
*/
function setHasher(address hasher_) external onlyRole(GOVERNANCE_ROLE) {
hasher__ = IHasher(hasher_);
emit HasherSet(hasher_);
}
/**
* @notice updates executionManager__
* @dev Only governance can call this function
* @param executionManager_ address of Execution Manager
*/
function setExecutionManager(
address executionManager_
) external onlyRole(GOVERNANCE_ROLE) {
executionManager__ = IExecutionManager(executionManager_);
emit ExecutionManagerSet(executionManager_);
}
/**
* @notice updates transmitManager__
* @param transmitManager_ address of Transmit Manager
* @dev Only governance can call this function
* @dev This function sets the transmitManager address. If it is ever upgraded,
* remove the fees from executionManager first, and then upgrade address at socket.
*/
function setTransmitManager(
address transmitManager_
) external onlyRole(GOVERNANCE_ROLE) {
transmitManager__ = ITransmitManager(transmitManager_);
emit TransmitManagerSet(transmitManager_);
}
//////////////////////////////////////////////
//////////// Rescue role actions ////////////
/////////////////////////////////////////////
/**
* @notice Rescues funds from the contract if they are locked by mistake. This contract does not
* theoretically need this function but it is added for safety.
* @param token_ The address of the token contract.
* @param rescueTo_ The address where rescued tokens need to be sent.
* @param amount_ The amount of tokens to be rescued.
*/
function rescueFunds(
address token_,
address rescueTo_,
uint256 amount_
) external onlyRole(RESCUE_ROLE) {
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
import "../interfaces/ISocket.sol";
import "../interfaces/ICapacitorFactory.sol";
import "../interfaces/ISwitchboard.sol";
/**
* @title SocketConfig
* @notice An abstract contract for configuring socket connections for plugs between different chains,
* manages plug configs and switchboard registrations
* @dev This contract is meant to be inherited by other contracts that require socket configuration functionality
*/
abstract contract SocketConfig is ISocket {
// factory contract that can deploy capacitors and decapacitors
ICapacitorFactory public capacitorFactory__;
/**
* @dev Struct to store the configuration for a plug connection
*/
struct PlugConfig {
// address of the sibling plug on the remote chain
address siblingPlug;
// capacitor instance for the outbound plug connection
ICapacitor capacitor__;
// decapacitor instance for the inbound plug connection
IDecapacitor decapacitor__;
// inbound switchboard instance for the plug connection
ISwitchboard inboundSwitchboard__;
// outbound switchboard instance for the plug connection
ISwitchboard outboundSwitchboard__;
}
// plug => remoteChainSlug => (siblingPlug, capacitor__, decapacitor__, inboundSwitchboard__, outboundSwitchboard__)
mapping(address => mapping(uint32 => PlugConfig)) internal _plugConfigs;
// It is used to maintain record of capacitors in the system registered for a slug and also used in seal for verification
// capacitor address => siblingChainSlug
mapping(address => uint32) public capacitorToSlug;
// switchboard => siblingChainSlug => ICapacitor
mapping(address => mapping(uint32 => ICapacitor)) public capacitors__;
// switchboard => siblingChainSlug => IDecapacitor
mapping(address => mapping(uint32 => IDecapacitor)) public decapacitors__;
// Event triggered when a new switchboard is added
event SwitchboardAdded(
address switchboard,
uint32 siblingChainSlug,
address capacitor,
address decapacitor,
uint256 maxPacketLength,
uint256 capacitorType
);
// Event triggered when a new switchboard is added
event SiblingSwitchboardUpdated(
address switchboard,
uint32 siblingChainSlug,
address siblingSwitchboard
);
// Error triggered when a switchboard already exists
error SwitchboardExists();
// Error triggered when a connection is invalid
error InvalidConnection();
/**
* @notice deploy capacitor and decapacitor for a switchboard with a specified max packet length, sibling chain slug, and capacitor type.
* @param siblingChainSlug_ The slug of the sibling chain that the switchboard is registered with.
* @param maxPacketLength_ The maximum length of a packet allowed by the switchboard.
* @param capacitorType_ The type of capacitor that the switchboard uses.
* @param siblingSwitchboard_ The switchboard address deployed on `siblingChainSlug_`
*/
function registerSwitchboardForSibling(
uint32 siblingChainSlug_,
uint256 maxPacketLength_,
uint256 capacitorType_,
address siblingSwitchboard_
) external override returns (address capacitor, address decapacitor) {
address switchboardAddress = msg.sender;
// only capacitor checked, decapacitor assumed will exist if capacitor does
if (
address(capacitors__[switchboardAddress][siblingChainSlug_]) !=
address(0)
) revert SwitchboardExists();
(
ICapacitor capacitor__,
IDecapacitor decapacitor__
) = capacitorFactory__.deploy(
capacitorType_,
siblingChainSlug_,
maxPacketLength_
);
capacitor = address(capacitor__);
decapacitor = address(decapacitor__);
capacitorToSlug[capacitor] = siblingChainSlug_;
capacitors__[switchboardAddress][siblingChainSlug_] = capacitor__;
decapacitors__[switchboardAddress][siblingChainSlug_] = decapacitor__;
emit SwitchboardAdded(
switchboardAddress,
siblingChainSlug_,
capacitor,
decapacitor,
maxPacketLength_,
capacitorType_
);
emit SiblingSwitchboardUpdated(
switchboardAddress,
siblingChainSlug_,
siblingSwitchboard_
);
}
/**
* @notice Emits the sibling switchboard for given `siblingChainSlug_`.
* @dev This function is expected to be only called by switchboard.
* @dev the event emitted is tracked by transmitters to decide which switchboard a packet should be proposed on
* @param siblingChainSlug_ The slug of the sibling chain
* @param siblingSwitchboard_ The switchboard address deployed on `siblingChainSlug_`
*/
function useSiblingSwitchboard(
uint32 siblingChainSlug_,
address siblingSwitchboard_
) external {
emit SiblingSwitchboardUpdated(
msg.sender,
siblingChainSlug_,
siblingSwitchboard_
);
}
/**
* @notice connects Plug to Socket and sets the config for given `siblingChainSlug_`
* @notice msg.sender is stored as plug address against given configuration
* @param siblingChainSlug_ the sibling chain slug
* @param siblingPlug_ address of plug present at siblingChainSlug_ to call at inbound
* @param inboundSwitchboard_ the address of switchboard to use for verifying messages at inbound
* @param outboundSwitchboard_ the address of switchboard to use for sending messages
*/
function connect(
uint32 siblingChainSlug_,
address siblingPlug_,
address inboundSwitchboard_,
address outboundSwitchboard_
) external override {
// only capacitor checked, decapacitor assumed will exist if capacitor does
// as they both are deployed together always
if (
address(capacitors__[inboundSwitchboard_][siblingChainSlug_]) ==
address(0) ||
address(capacitors__[outboundSwitchboard_][siblingChainSlug_]) ==
address(0)
) revert InvalidConnection();
PlugConfig storage _plugConfig = _plugConfigs[msg.sender][
siblingChainSlug_
];
_plugConfig.siblingPlug = siblingPlug_;
_plugConfig.capacitor__ = capacitors__[outboundSwitchboard_][
siblingChainSlug_
];
_plugConfig.decapacitor__ = decapacitors__[inboundSwitchboard_][
siblingChainSlug_
];
_plugConfig.inboundSwitchboard__ = ISwitchboard(inboundSwitchboard_);
_plugConfig.outboundSwitchboard__ = ISwitchboard(outboundSwitchboard_);
emit PlugConnected(
msg.sender,
siblingChainSlug_,
siblingPlug_,
inboundSwitchboard_,
outboundSwitchboard_,
address(_plugConfig.capacitor__),
address(_plugConfig.decapacitor__)
);
}
/**
* @notice returns the config for given `plugAddress_` and `siblingChainSlug_`
* @param siblingChainSlug_ the sibling chain slug
* @param plugAddress_ address of plug present at current chain
*/
function getPlugConfig(
address plugAddress_,
uint32 siblingChainSlug_
)
external
view
returns (
address siblingPlug,
address inboundSwitchboard__,
address outboundSwitchboard__,
address capacitor__,
address decapacitor__
)
{
PlugConfig memory _plugConfig = _plugConfigs[plugAddress_][
siblingChainSlug_
];
return (
_plugConfig.siblingPlug,
address(_plugConfig.inboundSwitchboard__),
address(_plugConfig.outboundSwitchboard__),
address(_plugConfig.capacitor__),
address(_plugConfig.decapacitor__)
);
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
import "../interfaces/IPlug.sol";
import "./SocketBase.sol";
/**
* @title SocketDst
* @dev SocketDst is an abstract contract that inherits from SocketBase and
* provides functionality for message execution, packet proposal, and verification.
* It manages the mapping of message execution status, packet ID roots, and root proposed
* timestamps. It emits events for packet proposal and root updates.
* It also includes functions for message execution and verification
*/
abstract contract SocketDst is SocketBase {
////////////////////////////////////////////////////////
////////////////////// ERRORS //////////////////////////
////////////////////////////////////////////////////////
/*
* @dev Error emitted when a packet has not been proposed
*/
error PacketNotProposed();
/*
* @dev Error emitted when a packet id is invalid
*/
error InvalidPacketId();
/**
* @dev Error emitted when proof is invalid
*/
error InvalidProof();
/**
* @dev Error emitted when a message has already been executed
*/
error MessageAlreadyExecuted();
/**
* @dev Error emitted when the executor is not valid
*/
error NotExecutor();
/**
* @dev Error emitted when verification fails
*/
error VerificationFailed();
/**
* @dev Error emitted when source slugs deduced from packet id and msg id don't match
*/
error ErrInSourceValidation();
/**
* @dev Error emitted when less gas limit is provided for execution than expected
*/
error LowGasLimit();
////////////////////////////////////////////////////////////
////////////////////// State Vars //////////////////////////
////////////////////////////////////////////////////////////
/**
* @dev keeps track of whether a message has been executed or not using message id
*/
mapping(bytes32 => bool) public messageExecuted;
/**
* @dev capacitorAddr|chainSlug|packetId => proposalCount => switchboard => packetIdRoots
*/
mapping(bytes32 => mapping(uint256 => mapping(address => bytes32)))
public
override packetIdRoots;
/**
* @dev packetId => proposalCount => switchboard => proposalTimestamp
*/
mapping(bytes32 => mapping(uint256 => mapping(address => uint256)))
public rootProposedAt;
/**
* @dev packetId => proposalCount
*/
mapping(bytes32 => uint256) public proposalCount;
////////////////////////////////////////////////////////
////////////////////// EVENTS //////////////////////////
////////////////////////////////////////////////////////
/**
* @notice emits the packet details when proposed at remote
* @param transmitter address of transmitter
* @param packetId packet id
* @param proposalCount proposal id
* @param root packet root
*/
event PacketProposed(
address indexed transmitter,
bytes32 indexed packetId,
uint256 proposalCount,
bytes32 root,
address switchboard
);
////////////////////////////////////////////////////////
////////////////////// OPERATIONS //////////////////////////
////////////////////////////////////////////////////////
/**
* @dev Function to propose a packet
* @notice the signature is validated if it belongs to transmitter or not
* @param packetId_ packet id
* @param root_ packet root
* @param switchboard_ The address of switchboard for which this packet is proposed
* @param signature_ signature
*/
function proposeForSwitchboard(
bytes32 packetId_,
bytes32 root_,
address switchboard_,
bytes calldata signature_
) external payable override {
if (packetId_ == bytes32(0)) revert InvalidPacketId();
(address transmitter, bool isTransmitter) = transmitManager__
.checkTransmitter(
_decodeChainSlug(packetId_),
keccak256(abi.encode(version, chainSlug, packetId_, root_)),
signature_
);
if (!isTransmitter) revert InvalidTransmitter();
packetIdRoots[packetId_][proposalCount[packetId_]][
switchboard_
] = root_;
rootProposedAt[packetId_][proposalCount[packetId_]][
switchboard_
] = block.timestamp;
emit PacketProposed(
transmitter,
packetId_,
proposalCount[packetId_]++,
root_,
switchboard_
);
}
/**
* @notice Executes a message that has been delivered by transmitters and authenticated by switchboards
* @param executionDetails_ all inputs needed from the executor for executing this particular message
* @param messageDetails_ the details needed for message verification
*/
function execute(
ISocket.ExecutionDetails calldata executionDetails_,
ISocket.MessageDetails calldata messageDetails_
) external payable override {
// make sure message is not executed already
if (messageExecuted[messageDetails_.msgId])
revert MessageAlreadyExecuted();
// update state to make sure no reentrancy
messageExecuted[messageDetails_.msgId] = true;
// make sure caller is calling with right gas limits
// we also make sure to give executors the ability to execute with higher gas limits
// than the minimum required
if (
executionDetails_.executionGasLimit < messageDetails_.minMsgGasLimit
) revert LowGasLimit();
if (executionDetails_.packetId == bytes32(0)) revert InvalidPacketId();
// extract chain slug from msgID
uint32 remoteSlug = _decodeChainSlug(messageDetails_.msgId);
// make sure packet and msg are for the same chain
if (_decodeChainSlug(executionDetails_.packetId) != remoteSlug)
revert ErrInSourceValidation();
// extract plug address from msgID
address localPlug = _decodePlug(messageDetails_.msgId);
// fetch required vars from plug config
PlugConfig memory plugConfig;
plugConfig.decapacitor__ = _plugConfigs[localPlug][remoteSlug]
.decapacitor__;
plugConfig.siblingPlug = _plugConfigs[localPlug][remoteSlug]
.siblingPlug;
plugConfig.inboundSwitchboard__ = _plugConfigs[localPlug][remoteSlug]
.inboundSwitchboard__;
// fetch packet root
bytes32 packetRoot = packetIdRoots[executionDetails_.packetId][
executionDetails_.proposalCount
][address(plugConfig.inboundSwitchboard__)];
if (packetRoot == bytes32(0)) revert PacketNotProposed();
// create packed message
bytes32 packedMessage = hasher__.packMessage(
remoteSlug,
plugConfig.siblingPlug,
chainSlug,
localPlug,
messageDetails_
);
// make sure caller is executor
(address executor, bool isValidExecutor) = executionManager__
.isExecutor(packedMessage, executionDetails_.signature);
if (!isValidExecutor) revert NotExecutor();
// finally make sure executor params were respected by the executor
executionManager__.verifyParams(
messageDetails_.executionParams,
msg.value
);
// verify message was part of the packet and
// authenticated by respective switchboard
_verify(
executionDetails_.packetId,
executionDetails_.proposalCount,
remoteSlug,
packedMessage,
packetRoot,
plugConfig,
executionDetails_.decapacitorProof
);
// execute message
_execute(
executor,
localPlug,
remoteSlug,
executionDetails_.executionGasLimit,
messageDetails_
);
}
////////////////////////////////////////////////////////
////////////////// INTERNAL FUNCS //////////////////////
////////////////////////////////////////////////////////
function _verify(
bytes32 packetId_,
uint256 proposalCount_,
uint32 remoteChainSlug_,
bytes32 packedMessage_,
bytes32 packetRoot_,
PlugConfig memory plugConfig_,
bytes memory decapacitorProof_
) internal {
// NOTE: is the the first un-trusted call in the system, another one is Plug.inbound
if (
!ISwitchboard(plugConfig_.inboundSwitchboard__).allowPacket(
packetRoot_,
packetId_,
proposalCount_,
remoteChainSlug_,
rootProposedAt[packetId_][proposalCount_][
address(plugConfig_.inboundSwitchboard__)
]
)
) revert VerificationFailed();
if (
!plugConfig_.decapacitor__.verifyMessageInclusion(
packetRoot_,
packedMessage_,
decapacitorProof_
)
) revert InvalidProof();
}
/**
* This function assumes localPlug_ will have code while executing. As the message
* execution failure is not blocking the system, it is not necessary to check if
* code exists in the given address.
*/
function _execute(
address executor_,
address localPlug_,
uint32 remoteChainSlug_,
uint256 executionGasLimit_,
ISocket.MessageDetails memory messageDetails_
) internal {
// NOTE: external un-trusted call
IPlug(localPlug_).inbound{gas: executionGasLimit_, value: msg.value}(
remoteChainSlug_,
messageDetails_.payload
);
executionManager__.updateExecutionFees(
executor_,
uint128(messageDetails_.executionFee),
messageDetails_.msgId
);
emit ExecutionSuccess(messageDetails_.msgId);
}
/**
* @dev Checks whether the specified packet has been proposed.
* @param packetId_ The ID of the packet to check.
* @param proposalCount_ The proposal ID of the packetId to check.
* @param switchboard_ The address of switchboard for which this packet is proposed
* @return A boolean indicating whether the packet has been proposed or not.
*/
function isPacketProposed(
bytes32 packetId_,
uint256 proposalCount_,
address switchboard_
) external view returns (bool) {
return
packetIdRoots[packetId_][proposalCount_][switchboard_] == bytes32(0)
? false
: true;
}
/**
* @dev Decodes the plug address from a given message id.
* @param id_ The ID of the msg to decode the plug from.
* @return plug_ The address of sibling plug decoded from the message ID.
*/
function _decodePlug(bytes32 id_) internal pure returns (address plug_) {
plug_ = address(uint160(uint256(id_) >> 64));
}
/**
* @dev Decodes the chain ID from a given packet/message ID.
* @param id_ The ID of the packet/msg to decode the chain slug from.
* @return chainSlug_ The chain slug decoded from the packet/message ID.
*/
function _decodeChainSlug(
bytes32 id_
) internal pure returns (uint32 chainSlug_) {
chainSlug_ = uint32(uint256(id_) >> 224);
}
}
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity 0.8.19;
import "./SocketBase.sol";
/**
* @title SocketSrc
* @dev The SocketSrc contract inherits from SocketBase and handles all the operations that
* happen on the source side. Provides the following functions
* 1. Sending messages from the local chain to a remote chain
* 2. Estimating minFees for message transmission, verification and execution
* 3. Sealing packets and making them ready to be transmitted
*/
abstract contract SocketSrc is SocketBase {
////////////////////////////////////////////////////////
////////////////////// ERRORS //////////////////////////
////////////////////////////////////////////////////////
/**
* @dev Error triggerred when invalid capacitor address is provided
*/
error InvalidCapacitorAddress();
/**
* @dev Error triggerred when siblingPlug is not found
*/
error PlugDisconnected();
////////////////////////////////////////////////////////
////////////////////// EVENTS //////////////////////////
////////////////////////////////////////////////////////
/**
* @notice Emits as soon as a capacitor is sealed
* @param transmitter address of transmitter that sealed this packet(recovered from sig)
* @param packetId packed-packet id
* @param root root of the packet
* @param signature signature of transmitter
*/
event Sealed(
address indexed transmitter,
bytes32 indexed packetId,
uint256 batchSize,
bytes32 root,
bytes signature
);
/**
* @notice emits the message details when a new message arrives at outbound
* @param localChainSlug local chain slug
* @param localPlug local plug address
* @param dstChainSlug remote chain slug
* @param dstPlug remote plug address
* @param msgId message id packed with remoteChainSlug and nonce
* @param minMsgGasLimit gas limit needed to execute the inbound at remote
* @param payload the data which will be used by inbound at remote
*/
event MessageOutbound(
uint32 localChainSlug,
address localPlug,
uint32 dstChainSlug,
address dstPlug,
bytes32 msgId,
uint256 minMsgGasLimit,
bytes32 executionParams,
bytes32 transmissionParams,
bytes payload,
Fees fees
);
/**
* @notice To send message to a connected remote chain. Should only be called by a plug.
* @param siblingChainSlug_ the remote chain slug
* @param minMsgGasLimit_ the minimum gas-limit needed to execute the payload on remote
* @param executionParams_ a 32 bytes param to add details for execution, for eg: fees to be paid for execution
* @param transmissionParams_ a 32 bytes param to add extra details for transmission
* @param payload_ bytes to be delivered to the Plug on the siblingChainSlug_
*/
function outbound(
uint32 siblingChainSlug_,
uint256 minMsgGasLimit_,
bytes32 executionParams_,
bytes32 transmissionParams_,
bytes calldata payload_
) external payable override returns (bytes32 msgId) {
PlugConfig memory plugConfig;
// looks up the sibling plug address using the msg.sender as the local plug address
plugConfig.siblingPlug = _plugConfigs[msg.sender][siblingChainSlug_]
.siblingPlug;
// if no sibling plug is found for the given chain slug, revert
if (plugConfig.siblingPlug == address(0)) revert PlugDisconnected();
// fetches auxillary details for the message from the plug config
plugConfig.capacitor__ = _plugConfigs[msg.sender][siblingChainSlug_]
.capacitor__;
plugConfig.outboundSwitchboard__ = _plugConfigs[msg.sender][
siblingChainSlug_
].outboundSwitchboard__;
// creates a unique ID for the message
msgId = _encodeMsgId(plugConfig.siblingPlug);
// validate if caller has send enough fees, if yes, send fees to execution manager
// for parties to claim later
ISocket.Fees memory fees = _validateAndSendFees(
minMsgGasLimit_,
uint256(payload_.length),
executionParams_,
transmissionParams_,
plugConfig.outboundSwitchboard__,
plugConfig.capacitor__.getMaxPacketLength(),
siblingChainSlug_
);
ISocket.MessageDetails memory messageDetails = ISocket.MessageDetails({
msgId: msgId,
minMsgGasLimit: minMsgGasLimit_,
executionParams: executionParams_,
payload: payload_,
executionFee: fees.executionFee
});
// create a compressed data-struct called PackedMessage
// which has the message payload and some configuration details
bytes32 packedMessage = hasher__.packMessage(
chainSlug,
msg.sender,
siblingChainSlug_,
plugConfig.siblingPlug,
messageDetails
);
// finally add packedMessage to the capacitor to generate new root
plugConfig.capacitor__.addPackedMessage(packedMessage);
emit MessageOutbound(
chainSlug,
msg.sender,
siblingChainSlug_,
plugConfig.siblingPlug,
msgId,
minMsgGasLimit_,
executionParams_,
transmissionParams_,
payload_,
fees
);
}
/**
* @notice Validates if enough fee is provided for message execution. If yes, fees is sent and stored in execution manager.
* @param minMsgGasLimit_ the min gas-limit of the message.
* @param payloadSize_ The byte length of payload of the message.
* @param executionParams_ The extraParams required for execution.
* @param transmissionParams_ The extraParams required for transmission.
* @param switchboard_ The address of the switchboard through which the message is sent.
* @param maxPacketLength_ The maxPacketLength for the capacitor used. Used for calculating transmission Fees.
* @param siblingChainSlug_ The slug of the destination chain for the message.
*/
function _validateAndSendFees(
uint256 minMsgGasLimit_,
uint256 payloadSize_,
bytes32 executionParams_,
bytes32 transmissionParams_,
ISwitchboard switchboard_,
uint256 maxPacketLength_,
uint32 siblingChainSlug_
) internal returns (ISocket.Fees memory fees) {
uint128 verificationFeePerMessage;
// switchboard is plug configured and this is an external untrusted call
(
fees.switchboardFees,
verificationFeePerMessage
) = _getSwitchboardMinFees(siblingChainSlug_, switchboard_);
// deposits msg.value to execution manager and checks if enough fees is provided
(fees.executionFee, fees.transmissionFees) = executionManager__
.payAndCheckFees{value: msg.value}(
minMsgGasLimit_,
payloadSize_,
executionParams_,
transmissionParams_,
siblingChainSlug_,
fees.switchboardFees / uint128(maxPacketLength_),
verificationFeePerMessage,
address(transmitManager__),
address(switchboard_),
maxPacketLength_
);
}
/**
* @notice Retrieves the minimum fees required for a message with a specified gas limit and destination chain.
* @param minMsgGasLimit_ The gas limit of the message.
* @param payloadSize_ The byte length of payload of the message.
* @param executionParams_ The extraParams required for execution.
* @param siblingChainSlug_ The slug of the destination chain for the message.
* @param plug_ The address of the plug through which the message is sent.
* @return totalFees The minimum fees required for the specified message.
*/
function getMinFees(
uint256 minMsgGasLimit_,
uint256 payloadSize_,
bytes32 executionParams_,
bytes32 transmissionParams_,
uint32 siblingChainSlug_,
address plug_
) external view override returns (uint256 totalFees) {
ICapacitor capacitor__ = _plugConfigs[plug_][siblingChainSlug_]
.capacitor__;
uint256 maxPacketLength = capacitor__.getMaxPacketLength();
(
uint128 transmissionFees,
uint128 switchboardFees,
uint128 executionFees
) = _getAllMinFees(
minMsgGasLimit_,
payloadSize_,
executionParams_,
transmissionParams_,
siblingChainSlug_,
_plugConfigs[plug_][siblingChainSlug_].outboundSwitchboard__,
maxPacketLength
);
totalFees = transmissionFees + switchboardFees + executionFees;
}
/**
* @notice Retrieves the minimum fees required for switchboard.
* @param siblingChainSlug_ The slug of the destination chain for the message.
* @param switchboard__ The switchboard address for which fees is retrieved.
* @return switchboardFees fees required for message verification
*/
function _getSwitchboardMinFees(
uint32 siblingChainSlug_,
ISwitchboard switchboard__
)
internal
view
returns (uint128 switchboardFees, uint128 verificationOverheadFees)
{
(switchboardFees, verificationOverheadFees) = switchboard__.getMinFees(
siblingChainSlug_
);
}
/**
* @notice Retrieves the minimum fees required for a message with a specified gas limit and destination chain.
* @param minMsgGasLimit_ The gas limit of the message.
* @param payloadSize_ The byte length of payload of the message.
* @param executionParams_ The extraParams required for execution.
* @param siblingChainSlug_ The slug of the destination chain for the message.
* @param switchboard__ The address of the switchboard through which the message is sent.
*/
function _getAllMinFees(
uint256 minMsgGasLimit_,
uint256 payloadSize_,
bytes32 executionParams_,
bytes32 transmissionParams_,
uint32 siblingChainSlug_,
ISwitchboard switchboard__,
uint256 maxPacketLength_
)
internal
view
returns (
uint128 transmissionFees,
uint128 switchboardFees,
uint128 executionFees
)
{
uint128 verificationOverheadFees;
uint128 msgExecutionFee;
(switchboardFees, verificationOverheadFees) = _getSwitchboardMinFees(
siblingChainSlug_,
switchboard__
);
switchboardFees /= uint128(maxPacketLength_);
(msgExecutionFee, transmissionFees) = executionManager__
.getExecutionTransmissionMinFees(
minMsgGasLimit_,
payloadSize_,
executionParams_,
transmissionParams_,
siblingChainSlug_,
address(transmitManager__)
);
transmissionFees /= uint128(maxPacketLength_);
executionFees = msgExecutionFee + verificationOverheadFees;
}
/**
* @notice seals data in capacitor for specific batchSize
* @param batchSize_ size of batch to be sealed
* @param capacitorAddress_ address of capacitor
* @param signature_ signed Data needed for verification
*/
function seal(
uint256 batchSize_,
address capacitorAddress_,
bytes calldata signature_
) external payable override {
uint32 siblingChainSlug = capacitorToSlug[capacitorAddress_];
if (siblingChainSlug == 0) revert InvalidCapacitorAddress();
(bytes32 root, uint64 packetCount) = ICapacitor(capacitorAddress_)
.sealPacket(batchSize_);
bytes32 packetId = _encodePacketId(capacitorAddress_, packetCount);
(address transmitter, bool isTransmitter) = transmitManager__
.checkTransmitter(
siblingChainSlug,
keccak256(
abi.encode(version, siblingChainSlug, packetId, root)
),
signature_
);
if (!isTransmitter) revert InvalidTransmitter();
emit Sealed(transmitter, packetId, batchSize_, root, signature_);
}
// Packs the local plug, local chain slug, remote chain slug and nonce
// globalMessageCount++ will take care of msg id overflow as well
// msgId(256) = localChainSlug(32) | siblingPlug_(160) | nonce(64)
function _encodeMsgId(address siblingPlug_) internal returns (bytes32) {
return
bytes32(
(uint256(chainSlug) << 224) |
(uint256(uint160(siblingPlug_)) << 64) |
globalMessageCount++
);
}
function _encodePacketId(
address capacitorAddress_,
uint64 packetCount_
) internal view returns (bytes32) {
return
bytes32(
(uint256(chainSlug) << 224) |
(uint256(uint160(capacitorAddress_)) << 64) |
packetCount_
);
}
}
{
"compilationTarget": {
"contracts/socket/Socket.sol": "Socket"
},
"evmVersion": "paris",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 999999
},
"remappings": []
}
[{"inputs":[{"internalType":"uint32","name":"chainSlug_","type":"uint32"},{"internalType":"address","name":"hasher_","type":"address"},{"internalType":"address","name":"capacitorFactory_","type":"address"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"string","name":"version_","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ErrInSourceValidation","type":"error"},{"inputs":[],"name":"InvalidCapacitorAddress","type":"error"},{"inputs":[],"name":"InvalidConnection","type":"error"},{"inputs":[],"name":"InvalidPacketId","type":"error"},{"inputs":[],"name":"InvalidProof","type":"error"},{"inputs":[],"name":"InvalidTokenAddress","type":"error"},{"inputs":[],"name":"InvalidTransmitter","type":"error"},{"inputs":[],"name":"LowGasLimit","type":"error"},{"inputs":[],"name":"MessageAlreadyExecuted","type":"error"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"NoPermit","type":"error"},{"inputs":[],"name":"NotExecutor","type":"error"},{"inputs":[],"name":"OnlyNominee","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"PacketNotProposed","type":"error"},{"inputs":[],"name":"PlugDisconnected","type":"error"},{"inputs":[],"name":"SwitchboardExists","type":"error"},{"inputs":[],"name":"UnequalArrayLengths","type":"error"},{"inputs":[],"name":"VerificationFailed","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"capacitorFactory","type":"address"}],"name":"CapacitorFactorySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"executionManager","type":"address"}],"name":"ExecutionManagerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"msgId","type":"bytes32"}],"name":"ExecutionSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"hasher","type":"address"}],"name":"HasherSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"localChainSlug","type":"uint32"},{"indexed":false,"internalType":"address","name":"localPlug","type":"address"},{"indexed":false,"internalType":"uint32","name":"dstChainSlug","type":"uint32"},{"indexed":false,"internalType":"address","name":"dstPlug","type":"address"},{"indexed":false,"internalType":"bytes32","name":"msgId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"minMsgGasLimit","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"executionParams","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"transmissionParams","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"payload","type":"bytes"},{"components":[{"internalType":"uint128","name":"transmissionFees","type":"uint128"},{"internalType":"uint128","name":"executionFee","type":"uint128"},{"internalType":"uint128","name":"switchboardFees","type":"uint128"}],"indexed":false,"internalType":"struct ISocket.Fees","name":"fees","type":"tuple"}],"name":"MessageOutbound","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimer","type":"address"}],"name":"OwnerClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nominee","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"bytes32","name":"packetId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"proposalCount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"root","type":"bytes32"},{"indexed":false,"internalType":"address","name":"switchboard","type":"address"}],"name":"PacketProposed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"plug","type":"address"},{"indexed":false,"internalType":"uint32","name":"siblingChainSlug","type":"uint32"},{"indexed":false,"internalType":"address","name":"siblingPlug","type":"address"},{"indexed":false,"internalType":"address","name":"inboundSwitchboard","type":"address"},{"indexed":false,"internalType":"address","name":"outboundSwitchboard","type":"address"},{"indexed":false,"internalType":"address","name":"capacitor","type":"address"},{"indexed":false,"internalType":"address","name":"decapacitor","type":"address"}],"name":"PlugConnected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"grantee","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"revokee","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"transmitter","type":"address"},{"indexed":true,"internalType":"bytes32","name":"packetId","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"batchSize","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"root","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"signature","type":"bytes"}],"name":"Sealed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"switchboard","type":"address"},{"indexed":false,"internalType":"uint32","name":"siblingChainSlug","type":"uint32"},{"indexed":false,"internalType":"address","name":"siblingSwitchboard","type":"address"}],"name":"SiblingSwitchboardUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"switchboard","type":"address"},{"indexed":false,"internalType":"uint32","name":"siblingChainSlug","type":"uint32"},{"indexed":false,"internalType":"address","name":"capacitor","type":"address"},{"indexed":false,"internalType":"address","name":"decapacitor","type":"address"},{"indexed":false,"internalType":"uint256","name":"maxPacketLength","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"capacitorType","type":"uint256"}],"name":"SwitchboardAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"transmitManager","type":"address"}],"name":"TransmitManagerSet","type":"event"},{"inputs":[],"name":"capacitorFactory__","outputs":[{"internalType":"contract ICapacitorFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"capacitorToSlug","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"capacitors__","outputs":[{"internalType":"contract ICapacitor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainSlug","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"siblingChainSlug_","type":"uint32"},{"internalType":"address","name":"siblingPlug_","type":"address"},{"internalType":"address","name":"inboundSwitchboard_","type":"address"},{"internalType":"address","name":"outboundSwitchboard_","type":"address"}],"name":"connect","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"decapacitors__","outputs":[{"internalType":"contract IDecapacitor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"packetId","type":"bytes32"},{"internalType":"uint256","name":"proposalCount","type":"uint256"},{"internalType":"uint256","name":"executionGasLimit","type":"uint256"},{"internalType":"bytes","name":"decapacitorProof","type":"bytes"},{"internalType":"bytes","name":"signature","type":"bytes"}],"internalType":"struct ISocket.ExecutionDetails","name":"executionDetails_","type":"tuple"},{"components":[{"internalType":"bytes32","name":"msgId","type":"bytes32"},{"internalType":"uint256","name":"executionFee","type":"uint256"},{"internalType":"uint256","name":"minMsgGasLimit","type":"uint256"},{"internalType":"bytes32","name":"executionParams","type":"bytes32"},{"internalType":"bytes","name":"payload","type":"bytes"}],"internalType":"struct ISocket.MessageDetails","name":"messageDetails_","type":"tuple"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"executionManager__","outputs":[{"internalType":"contract IExecutionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"minMsgGasLimit_","type":"uint256"},{"internalType":"uint256","name":"payloadSize_","type":"uint256"},{"internalType":"bytes32","name":"executionParams_","type":"bytes32"},{"internalType":"bytes32","name":"transmissionParams_","type":"bytes32"},{"internalType":"uint32","name":"siblingChainSlug_","type":"uint32"},{"internalType":"address","name":"plug_","type":"address"}],"name":"getMinFees","outputs":[{"internalType":"uint256","name":"totalFees","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"plugAddress_","type":"address"},{"internalType":"uint32","name":"siblingChainSlug_","type":"uint32"}],"name":"getPlugConfig","outputs":[{"internalType":"address","name":"siblingPlug","type":"address"},{"internalType":"address","name":"inboundSwitchboard__","type":"address"},{"internalType":"address","name":"outboundSwitchboard__","type":"address"},{"internalType":"address","name":"capacitor__","type":"address"},{"internalType":"address","name":"decapacitor__","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"globalMessageCount","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"roleNames_","type":"bytes32[]"},{"internalType":"uint32[]","name":"slugs_","type":"uint32[]"},{"internalType":"address[]","name":"grantees_","type":"address[]"}],"name":"grantBatchRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role_","type":"bytes32"},{"internalType":"address","name":"grantee_","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"roleName_","type":"bytes32"},{"internalType":"uint32","name":"chainSlug_","type":"uint32"},{"internalType":"address","name":"grantee_","type":"address"}],"name":"grantRoleWithSlug","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role_","type":"bytes32"},{"internalType":"address","name":"address_","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"roleName_","type":"bytes32"},{"internalType":"uint32","name":"chainSlug_","type":"uint32"},{"internalType":"address","name":"address_","type":"address"}],"name":"hasRoleWithSlug","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hasher__","outputs":[{"internalType":"contract IHasher","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"packetId_","type":"bytes32"},{"internalType":"uint256","name":"proposalCount_","type":"uint256"},{"internalType":"address","name":"switchboard_","type":"address"}],"name":"isPacketProposed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"messageExecuted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nominee_","type":"address"}],"name":"nominateOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominee","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"siblingChainSlug_","type":"uint32"},{"internalType":"uint256","name":"minMsgGasLimit_","type":"uint256"},{"internalType":"bytes32","name":"executionParams_","type":"bytes32"},{"internalType":"bytes32","name":"transmissionParams_","type":"bytes32"},{"internalType":"bytes","name":"payload_","type":"bytes"}],"name":"outbound","outputs":[{"internalType":"bytes32","name":"msgId","type":"bytes32"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"packetIdRoots","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"proposalCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"packetId_","type":"bytes32"},{"internalType":"bytes32","name":"root_","type":"bytes32"},{"internalType":"address","name":"switchboard_","type":"address"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"proposeForSwitchboard","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint32","name":"siblingChainSlug_","type":"uint32"},{"internalType":"uint256","name":"maxPacketLength_","type":"uint256"},{"internalType":"uint256","name":"capacitorType_","type":"uint256"},{"internalType":"address","name":"siblingSwitchboard_","type":"address"}],"name":"registerSwitchboardForSibling","outputs":[{"internalType":"address","name":"capacitor","type":"address"},{"internalType":"address","name":"decapacitor","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"rescueTo_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"rescueFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32[]","name":"roleNames_","type":"bytes32[]"},{"internalType":"uint32[]","name":"slugs_","type":"uint32[]"},{"internalType":"address[]","name":"grantees_","type":"address[]"}],"name":"revokeBatchRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role_","type":"bytes32"},{"internalType":"address","name":"revokee_","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"roleName_","type":"bytes32"},{"internalType":"uint32","name":"chainSlug_","type":"uint32"},{"internalType":"address","name":"grantee_","type":"address"}],"name":"revokeRoleWithSlug","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"rootProposedAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"batchSize_","type":"uint256"},{"internalType":"address","name":"capacitorAddress_","type":"address"},{"internalType":"bytes","name":"signature_","type":"bytes"}],"name":"seal","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"capacitorFactory_","type":"address"}],"name":"setCapacitorFactory","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"executionManager_","type":"address"}],"name":"setExecutionManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"hasher_","type":"address"}],"name":"setHasher","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"transmitManager_","type":"address"}],"name":"setTransmitManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"transmitManager__","outputs":[{"internalType":"contract ITransmitManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"siblingChainSlug_","type":"uint32"},{"internalType":"address","name":"siblingSwitchboard_","type":"address"}],"name":"useSiblingSwitchboard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"}]