// SPDX-License-Identifier: GPL-3.0-onlypragmasolidity 0.8.13;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.
*/abstractcontractAccessControlisOwnable{
/**
* @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.
*/eventRoleGranted(bytes32indexed role, addressindexed grantee);
/**
* @dev Emitted when a role is revoked from an address.
*/eventRoleRevoked(bytes32indexed role, addressindexed revokee);
/**
* @dev Error message thrown when an address does not have permission to execute a function with onlyRole modifier.
*/errorNoPermit(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
*/modifieronlyRole(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_) internalvirtual{
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.
*/functiongrantRole(bytes32 role_,
address grantee_
) externalvirtualonlyOwner{
_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.
*/functionrevokeRole(bytes32 role_,
address revokee_
) externalvirtualonlyOwner{
_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.
*/functionhasRole(bytes32 role_,
address address_
) externalviewreturns (bool) {
return _hasRole(role_, address_);
}
function_hasRole(bytes32 role_,
address address_
) internalviewreturns (bool) {
return _permits[role_][address_];
}
}
Contract Source Code
File 2 of 16: Base.sol
pragmasolidity 0.8.13;import {IMintableERC20} from"../interfaces/IMintableERC20.sol";
import {IConnector} from"../interfaces/IConnector.sol";
import"lib/solmate/src/utils/SafeTransferLib.sol";
import"../interfaces/IHook.sol";
import"../common/Errors.sol";
import"lib/solmate/src/utils/ReentrancyGuard.sol";
import"../interfaces/IBridge.sol";
import"../utils/RescueBase.sol";
import"../common/Constants.sol";
abstractcontractBaseisReentrancyGuard, IBridge, RescueBase{
addresspublicimmutable token;
bytes32public bridgeType;
IHook public hook__;
// message identifier => cachemapping(bytes32=>bytes) public identifierCache;
// connector => cachemapping(address=>bytes) public connectorCache;
mapping(address=>bool) public validConnectors;
eventConnectorStatusUpdated(address connector, bool status);
eventHookUpdated(address newHook);
eventBridgingTokens(address connector,
address sender,
address receiver,
uint256 amount,
bytes32 messageId
);
eventTokensBridged(address connecter,
address receiver,
uint256 amount,
bytes32 messageId
);
constructor(address token_) AccessControl(msg.sender) {
if (token_ != ETH_ADDRESS && token_.code.length==0)
revert InvalidTokenContract();
token = token_;
_grantRole(RESCUE_ROLE, msg.sender);
}
/**
* @notice this function is used to update hook
* @dev it can only be updated by owner
* @dev should be carefully migrated as it can risk user funds
* @param hook_ new hook address
*/functionupdateHook(address hook_,
bool approve_
) externalvirtualonlyOwner{
// remove the approval from the old hookif (token != ETH_ADDRESS) {
if (ERC20(token).allowance(address(this), address(hook__)) >0) {
SafeTransferLib.safeApprove(ERC20(token), address(hook__), 0);
}
if (approve_) {
SafeTransferLib.safeApprove(
ERC20(token),
hook_,
type(uint256).max
);
}
}
hook__ = IHook(hook_);
emit HookUpdated(hook_);
}
functionupdateConnectorStatus(address[] calldata connectors,
bool[] calldata statuses
) externalonlyOwner{
uint256 length = connectors.length;
for (uint256 i; i < length; i++) {
validConnectors[connectors[i]] = statuses[i];
emit ConnectorStatusUpdated(connectors[i], statuses[i]);
}
}
/**
* @notice Executes pre-bridge operations before initiating a token bridge transfer.
* @dev This internal function is called before initiating a token bridge transfer.
* It validates the receiver address and the connector, and if a pre-hook contract is defined,
* it executes the source pre-hook call.
* @param connector_ The address of the connector responsible for the transfer.
* @param transferInfo_ Information about the transfer.
* @return transferInfo Information about the transfer after pre-bridge operations.
* @return postHookData Data returned from the pre-hook call.
* @dev Reverts with `ZeroAddressReceiver` if the receiver address is zero.
* Reverts with `InvalidConnector` if the connector address is not valid.
*/function_beforeBridge(address connector_,
TransferInfo memory transferInfo_
)
internalreturns (TransferInfo memory transferInfo, bytesmemory postHookData)
{
if (transferInfo_.receiver ==address(0)) revert ZeroAddressReceiver();
if (!validConnectors[connector_]) revert InvalidConnector();
if (token == ETH_ADDRESS &&msg.value< transferInfo_.amount)
revert InsufficientMsgValue();
if (address(hook__) !=address(0)) {
(transferInfo, postHookData) = hook__.srcPreHookCall(
SrcPreHookCallParams(connector_, msg.sender, transferInfo_)
);
}
}
/**
* @notice Executes post-bridge operations after completing a token bridge transfer.
* @dev This internal function is called after completing a token bridge transfer.
* It executes the source post-hook call if a hook contract is defined, calculates fees,
* calls the outbound function of the connector, and emits an event for tokens withdrawn.
* @param msgGasLimit_ The gas limit for the outbound call.
* @param connector_ The address of the connector responsible for the transfer.
* @param options_ Additional options for the outbound call.
* @param postSrcHookData_ Data returned from the source post-hook call.
* @param transferInfo_ Information about the transfer.
* @dev Reverts with `MessageIdMisMatched` if the returned message ID does not match the expected message ID.
*/function_afterBridge(uint256 msgGasLimit_,
address connector_,
bytesmemory options_,
bytesmemory postSrcHookData_,
TransferInfo memory transferInfo_
) internal{
TransferInfo memory transferInfo = transferInfo_;
if (address(hook__) !=address(0)) {
transferInfo = hook__.srcPostHookCall(
SrcPostHookCallParams(
connector_,
options_,
postSrcHookData_,
transferInfo_
)
);
}
uint256 fees = token == ETH_ADDRESS
? msg.value- transferInfo.amount
: msg.value;
bytes32 messageId = IConnector(connector_).getMessageId();
bytes32 returnedMessageId = IConnector(connector_).outbound{
value: fees
}(
msgGasLimit_,
abi.encode(
transferInfo.receiver,
transferInfo.amount,
messageId,
transferInfo.data
),
options_
);
if (returnedMessageId != messageId) revert MessageIdMisMatched();
emit BridgingTokens(
connector_,
msg.sender,
transferInfo.receiver,
transferInfo.amount,
messageId
);
}
/**
* @notice Executes pre-mint operations before minting tokens.
* @dev This internal function is called before minting tokens.
* It validates the caller as a valid connector, checks if the receiver is not this contract, the bridge contract,
* or the token contract, and executes the destination pre-hook call if a hook contract is defined.
* @param transferInfo_ Information about the transfer.
* @return postHookData Data returned from the destination pre-hook call.
* @return transferInfo Information about the transfer after pre-mint operations.
* @dev Reverts with `InvalidConnector` if the caller is not a valid connector.
* Reverts with `CannotTransferOrExecuteOnBridgeContracts` if the receiver is this contract, the bridge contract,
* or the token contract.
*/function_beforeMint(uint32,
TransferInfo memory transferInfo_
)
internalreturns (bytesmemory postHookData, TransferInfo memory transferInfo)
{
if (!validConnectors[msg.sender]) revert InvalidConnector();
// no need of source check here, as if invalid caller, will revert with InvalidPoolIdif (
transferInfo_.receiver ==address(this) ||// transferInfo_.receiver == address(bridge__) ||
transferInfo_.receiver == token
) revert CannotTransferOrExecuteOnBridgeContracts();
if (address(hook__) !=address(0)) {
(postHookData, transferInfo) = hook__.dstPreHookCall(
DstPreHookCallParams(
msg.sender,
connectorCache[msg.sender],
transferInfo_
)
);
}
}
/**
* @notice Executes post-mint operations after minting tokens.
* @dev This internal function is called after minting tokens.
* It executes the destination post-hook call if a hook contract is defined and updates cache data.
* @param messageId_ The unique identifier for the mint transaction.
* @param postHookData_ Data returned from the destination pre-hook call.
* @param transferInfo_ Information about the mint transaction.
*/function_afterMint(uint256,
bytes32 messageId_,
bytesmemory postHookData_,
TransferInfo memory transferInfo_
) internal{
if (address(hook__) !=address(0)) {
CacheData memory cacheData = hook__.dstPostHookCall(
DstPostHookCallParams(
msg.sender,
messageId_,
connectorCache[msg.sender],
postHookData_,
transferInfo_
)
);
identifierCache[messageId_] = cacheData.identifierCache;
connectorCache[msg.sender] = cacheData.connectorCache;
}
emit TokensBridged(
msg.sender,
transferInfo_.receiver,
transferInfo_.amount,
messageId_
);
}
/**
* @notice Executes pre-retry operations before retrying a failed transaction.
* @dev This internal function is called before retrying a failed transaction.
* It validates the connector, retrieves cache data for the given message ID,
* and executes the pre-retry hook if defined.
* @param connector_ The address of the connector responsible for the failed transaction.
* @param messageId_ The unique identifier for the failed transaction.
* @return postRetryHookData Data returned from the pre-retry hook call.
* @return transferInfo Information about the transfer.
* @dev Reverts with `InvalidConnector` if the connector is not valid.
* Reverts with `NoPendingData` if there is no pending data for the given message ID.
*/function_beforeRetry(address connector_,
bytes32 messageId_
)
internalreturns (bytesmemory postRetryHookData,
TransferInfo memory transferInfo
)
{
if (!validConnectors[connector_]) revert InvalidConnector();
CacheData memory cacheData = CacheData(
identifierCache[messageId_],
connectorCache[connector_]
);
if (cacheData.identifierCache.length==0) revert NoPendingData();
(postRetryHookData, transferInfo) = hook__.preRetryHook(
PreRetryHookCallParams(connector_, cacheData)
);
}
/**
* @notice Executes post-retry operations after retrying a failed transaction.
* @dev This internal function is called after retrying a failed transaction.
* It retrieves cache data for the given message ID, executes the post-retry hook if defined,
* and updates cache data.
* @param connector_ The address of the connector responsible for the failed transaction.
* @param messageId_ The unique identifier for the failed transaction.
* @param postRetryHookData Data returned from the pre-retry hook call.
*/function_afterRetry(address connector_,
bytes32 messageId_,
bytesmemory postRetryHookData
) internal{
CacheData memory cacheData = CacheData(
identifierCache[messageId_],
connectorCache[connector_]
);
(cacheData) = hook__.postRetryHook(
PostRetryHookCallParams(
connector_,
messageId_,
postRetryHookData,
cacheData
)
);
identifierCache[messageId_] = cacheData.identifierCache;
connectorCache[connector_] = cacheData.connectorCache;
}
/**
* @notice Retrieves the minimum fees required for a transaction from a connector.
* @dev This function returns the minimum fees required for a transaction from the specified connector,
* based on the provided message gas limit and payload size.
* @param connector_ The address of the connector.
* @param msgGasLimit_ The gas limit for the transaction.
* @param payloadSize_ The size of the payload for the transaction.
* @return totalFees The total minimum fees required for the transaction.
*/functiongetMinFees(address connector_,
uint256 msgGasLimit_,
uint256 payloadSize_
) externalviewreturns (uint256 totalFees) {
return IConnector(connector_).getMinFees(msgGasLimit_, payloadSize_);
}
}
pragmasolidity ^0.8.3;import"../common/Structs.sol";
interfaceIHook{
/**
* @notice Executes pre-hook call for source underlyingAsset.
* @dev This function is used to execute a pre-hook call for the source underlyingAsset before initiating a transfer.
* @param params_ Parameters for the pre-hook call.
* @return transferInfo Information about the transfer.
* @return postSrcHookData returned from the pre-hook call.
*/functionsrcPreHookCall(
SrcPreHookCallParams calldata params_
)
externalreturns (
TransferInfo memory transferInfo,
bytesmemory postSrcHookData
);
functionsrcPostHookCall(
SrcPostHookCallParams calldata params_
) externalreturns (TransferInfo memory transferInfo);
/**
* @notice Executes pre-hook call for destination underlyingAsset.
* @dev This function is used to execute a pre-hook call for the destination underlyingAsset before initiating a transfer.
* @param params_ Parameters for the pre-hook call.
*/functiondstPreHookCall(
DstPreHookCallParams calldata params_
)
externalreturns (bytesmemory postHookData, TransferInfo memory transferInfo);
/**
* @notice Executes post-hook call for destination underlyingAsset.
* @dev This function is used to execute a post-hook call for the destination underlyingAsset after completing a transfer.
* @param params_ Parameters for the post-hook call.
* @return cacheData Cached data for the post-hook call.
*/functiondstPostHookCall(
DstPostHookCallParams calldata params_
) externalreturns (CacheData memory cacheData);
/**
* @notice Executes a pre-retry hook for a failed transaction.
* @dev This function is used to execute a pre-retry hook for a failed transaction.
* @param params_ Parameters for the pre-retry hook.
* @return postRetryHookData Data from the post-retry hook.
* @return transferInfo Information about the transfer.
*/functionpreRetryHook(
PreRetryHookCallParams calldata params_
)
externalreturns (bytesmemory postRetryHookData,
TransferInfo memory transferInfo
);
/**
* @notice Executes a post-retry hook for a failed transaction.
* @dev This function is used to execute a post-retry hook for a failed transaction.
* @param params_ Parameters for the post-retry hook.
* @return cacheData Cached data for the post-retry hook.
*/functionpostRetryHook(
PostRetryHookCallParams calldata params_
) externalreturns (CacheData memory cacheData);
}
// SPDX-License-Identifier: GPL-3.0-onlypragmasolidity 0.8.13;/**
* @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.
*/abstractcontractOwnable{
addressprivate _owner;
addressprivate _nominee;
eventOwnerNominated(addressindexed nominee);
eventOwnerClaimed(addressindexed claimer);
errorOnlyOwner();
errorOnlyNominee();
/**
* @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.
*/modifieronlyOwner() {
if (msg.sender!= _owner) revert OnlyOwner();
_;
}
/**
* @dev Returns the current owner of the contract.
*/functionowner() externalviewreturns (address) {
return _owner;
}
/**
* @dev Returns the current nominee for ownership of the contract.
*/functionnominee() externalviewreturns (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.
*/functionnominateOwner(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.
*/functionclaimOwner() 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: MITpragmasolidity 0.8.13;import {RescueFundsLib} from"../libraries/RescueFundsLib.sol";
import {AccessControl} from"./AccessControl.sol";
/**
* @title Base contract for super token and vault
* @notice It contains relevant execution payload storages.
* @dev This contract implements Socket's IPlug to enable message bridging and IMessageBridge
* to support any type of message bridge.
*/abstractcontractRescueBaseisAccessControl{
bytes32constant RESCUE_ROLE =keccak256("RESCUE_ROLE");
/**
* @notice Rescues funds from the contract if they are locked by mistake.
* @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.
*/functionrescueFunds(address token_,
address rescueTo_,
uint256 amount_
) externalonlyRole(RESCUE_ROLE) {
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}
Contract Source Code
File 13 of 16: RescueFundsLib.sol
// SPDX-License-Identifier: GPL-3.0-onlypragmasolidity 0.8.13;import"lib/solmate/src/utils/SafeTransferLib.sol";
import"lib/solmate/src/tokens/ERC20.sol";
errorZeroAddress();
/**
* @title RescueFundsLib
* @dev A library that provides a function to rescue funds from a contract.
*/libraryRescueFundsLib{
/**
* @dev The address used to identify ETH.
*/addresspublicconstant ETH_ADDRESS =address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
/**
* @dev thrown when the given token address don't have any code
*/errorInvalidTokenAddress();
/**
* @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.
*/functionrescueFunds(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_);
}
}
}
Contract Source Code
File 14 of 16: SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=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.librarySafeTransferLib{
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferETH(address to, uint256 amount) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// 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
//////////////////////////////////////////////////////////////*/functionsafeTransferFrom(
ERC20 token,
addressfrom,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// 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");
}
functionsafeTransfer(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// 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");
}
functionsafeApprove(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// 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");
}
}
pragmasolidity 0.8.13;import"./Base.sol";
import"../interfaces/IConnector.sol";
import"lib/solmate/src/tokens/ERC20.sol";
/**
* @title SuperToken
* @notice A contract which enables bridging a token to its sibling chains.
* @dev This contract implements ISuperTokenOrVault to support message bridging through IMessageBridge compliant contracts.
*/contractVaultisBase{
usingSafeTransferLibforERC20;
// /**// * @notice constructor for creating a new SuperTokenVault.// * @param token_ token contract address which is to be bridged.// */constructor(address token_) Base(token_) {
bridgeType = token_ == ETH_ADDRESS ? NATIVE_VAULT : ERC20_VAULT;
}
/**
* @notice Bridges tokens between chains.
* @dev This function allows bridging tokens between different chains.
* @param receiver_ The address to receive the bridged tokens.
* @param amount_ The amount of tokens to bridge.
* @param msgGasLimit_ The gas limit for the execution of the bridging process.
* @param connector_ The address of the connector contract responsible for the bridge.
* @param execPayload_ The payload for executing the bridging process on the connector.
* @param options_ Additional options for the bridging process.
*/functionbridge(address receiver_,
uint256 amount_,
uint256 msgGasLimit_,
address connector_,
bytescalldata execPayload_,
bytescalldata options_
) publicpayablenonReentrant{
(
TransferInfo memory transferInfo,
bytesmemory postHookData
) = _beforeBridge(
connector_,
TransferInfo(receiver_, amount_, execPayload_)
);
_receiveTokens(transferInfo.amount);
_afterBridge(
msgGasLimit_,
connector_,
options_,
postHookData,
transferInfo
);
}
/**
* @notice Bridges tokens between chains using permit.
* @param receiver_ The address to receive the bridged tokens.
* @param amount_ The amount of tokens to bridge.
* @param msgGasLimit_ The gas limit for the execution of the bridging process.
* @param connector_ The address of the connector contract responsible for the bridge.
* @param execPayload_ The payload for executing the bridging process on the connector.
* @param options_ Additional options for the bridging process.
* @param deadline_ The deadline for the permit signature.
* @param v_ The recovery id of the permit signature.
* @param r_ The r value of the permit signature.
* @param s_ The s value of the permit signature.
*/functionbridgeWithPermit(address receiver_,
uint256 amount_,
uint256 msgGasLimit_,
address connector_,
bytescalldata execPayload_,
bytescalldata options_,
uint256 deadline_,
uint8 v_,
bytes32 r_,
bytes32 s_
) externalpayable{
ERC20(token).permit(msg.sender, address(this), amount_, deadline_, v_, r_, s_);
bridge(receiver_, amount_, msgGasLimit_, connector_, execPayload_, options_);
}
/**
* @notice Receives inbound tokens from another chain.
* @dev This function is used to receive tokens from another chain.
* @param siblingChainSlug_ The identifier of the sibling chain.
* @param payload_ The payload containing the inbound tokens.
*/functionreceiveInbound(uint32 siblingChainSlug_,
bytesmemory payload_
) externalpayableoverridenonReentrant{
(
address receiver,
uint256 unlockAmount,
bytes32 messageId,
bytesmemory extraData
) =abi.decode(payload_, (address, uint256, bytes32, bytes));
TransferInfo memory transferInfo = TransferInfo(
receiver,
unlockAmount,
extraData
);
bytesmemory postHookData;
(postHookData, transferInfo) = _beforeMint(
siblingChainSlug_,
transferInfo
);
_transferTokens(transferInfo.receiver, transferInfo.amount);
_afterMint(unlockAmount, messageId, postHookData, transferInfo);
}
/**
* @notice Retry a failed transaction.
* @dev This function allows retrying a failed transaction sent through a connector.
* @param connector_ The address of the connector contract responsible for the failed transaction.
* @param messageId_ The unique identifier of the failed transaction.
*/functionretry(address connector_,
bytes32 messageId_
) externalnonReentrant{
(
bytesmemory postRetryHookData,
TransferInfo memory transferInfo
) = _beforeRetry(connector_, messageId_);
_transferTokens(transferInfo.receiver, transferInfo.amount);
_afterRetry(connector_, messageId_, postRetryHookData);
}
function_transferTokens(address receiver_, uint256 amount_) internal{
if (amount_ ==0) return;
if (address(token) == ETH_ADDRESS) {
SafeTransferLib.safeTransferETH(receiver_, amount_);
} else {
ERC20(token).safeTransfer(receiver_, amount_);
}
}
function_receiveTokens(uint256 amount_) internal{
if (amount_ ==0||address(token) == ETH_ADDRESS) return;
ERC20(token).safeTransferFrom(msg.sender, address(this), amount_);
}
}