Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/bridge/Base.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 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";
abstract contract Base is ReentrancyGuard, IBridge, RescueBase {
address public immutable token;
bytes32 public bridgeType;
IHook public hook__;
// message identifier => cache
mapping(bytes32 => bytes) public identifierCache;
// connector => cache
mapping(address => bytes) public connectorCache;
mapping(address => bool) public validConnectors;
event ConnectorStatusUpdated(address connector, bool status);
event HookUpdated(address newHook);
event BridgingTokens(
address connector,
address sender,
address receiver,
uint256 amount,
bytes32 messageId
);
event TokensBridged(
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
*/
function updateHook(
address hook_,
bool approve_
) external virtual onlyOwner {
// remove the approval from the old hook
if (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_);
}
function updateConnectorStatus(
address[] calldata connectors,
bool[] calldata statuses
) external onlyOwner {
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_
)
internal
returns (TransferInfo memory transferInfo, bytes memory 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_)
);
} else {
transferInfo = 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 postHookData_ 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_,
bytes memory options_,
bytes memory postHookData_,
TransferInfo memory transferInfo_
) internal {
TransferInfo memory transferInfo = transferInfo_;
if (address(hook__) != address(0)) {
transferInfo = hook__.srcPostHookCall(
SrcPostHookCallParams(
connector_,
options_,
postHookData_,
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.extraData
),
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_
)
internal
returns (bytes memory postHookData, TransferInfo memory transferInfo)
{
if (!validConnectors[msg.sender]) revert InvalidConnector();
// no need of source check here, as if invalid caller, will revert with InvalidPoolId
if (
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_
)
);
} else {
transferInfo = 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_,
bytes memory 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 postHookData 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_
)
internal
returns (bytes memory postHookData, TransferInfo memory transferInfo)
{
if (!validConnectors[connector_]) revert InvalidConnector();
CacheData memory cacheData = CacheData(
identifierCache[messageId_],
connectorCache[connector_]
);
if (cacheData.identifierCache.length == 0) revert NoPendingData();
(postHookData, 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 postHookData Data returned from the pre-retry hook call.
*/
function _afterRetry(
address connector_,
bytes32 messageId_,
bytes memory postHookData
) internal {
CacheData memory cacheData = CacheData(
identifierCache[messageId_],
connectorCache[connector_]
);
(cacheData) = hook__.postRetryHook(
PostRetryHookCallParams(
connector_,
messageId_,
postHookData,
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.
*/
function getMinFees(
address connector_,
uint256 msgGasLimit_,
uint256 payloadSize_
) external view returns (uint256 totalFees) {
return IConnector(connector_).getMinFees(msgGasLimit_, payloadSize_);
}
}
"
},
"contracts/bridge/Controller.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "./Base.sol";
contract Controller is Base {
uint256 public totalMinted;
constructor(address token_) Base(token_) {
bridgeType = NORMAL_CONTROLLER;
}
/**
* @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 extraData_ The extra data passed to hook functions.
* @param options_ Additional options for the bridging process.
*/
function bridge(
address receiver_,
uint256 amount_,
uint256 msgGasLimit_,
address connector_,
bytes calldata extraData_,
bytes calldata options_
) external payable nonReentrant {
(
TransferInfo memory transferInfo,
bytes memory postHookData
) = _beforeBridge(
connector_,
TransferInfo(receiver_, amount_, extraData_)
);
// to maintain socket dl specific accounting for super token
// re check this logic for mint and mint use cases and if other minter involved
totalMinted -= transferInfo.amount;
_burn(msg.sender, transferInfo.amount);
_afterBridge(
msgGasLimit_,
connector_,
options_,
postHookData,
transferInfo
);
}
/**
* @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.
*/
function receiveInbound(
uint32 siblingChainSlug_,
bytes memory payload_
) external payable override nonReentrant {
(
address receiver,
uint256 lockAmount,
bytes32 messageId,
bytes memory extraData
) = abi.decode(payload_, (address, uint256, bytes32, bytes));
// convert to shares
TransferInfo memory transferInfo = TransferInfo(
receiver,
lockAmount,
extraData
);
bytes memory postHookData;
(postHookData, transferInfo) = _beforeMint(
siblingChainSlug_,
transferInfo
);
_mint(transferInfo.receiver, transferInfo.amount);
totalMinted += transferInfo.amount;
_afterMint(lockAmount, 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.
*/
function retry(
address connector_,
bytes32 messageId_
) external nonReentrant {
(
bytes memory postHookData,
TransferInfo memory transferInfo
) = _beforeRetry(connector_, messageId_);
_mint(transferInfo.receiver, transferInfo.amount);
totalMinted += transferInfo.amount;
_afterRetry(connector_, messageId_, postHookData);
}
function _burn(address user_, uint256 burnAmount_) internal virtual {
IMintableERC20(token).burn(user_, burnAmount_);
}
function _mint(address user_, uint256 mintAmount_) internal virtual {
if (mintAmount_ == 0) return;
IMintableERC20(token).mint(user_, mintAmount_);
}
}
"
},
"contracts/bridge/FiatTokenV2_1/FiatTokenV2_1_Controller.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import {IFiatTokenV2_1_Mintable} from "./IFiatTokenV2_1_Mintable.sol";
import "../Controller.sol";
contract FiatTokenV2_1_Controller is Controller {
using SafeTransferLib for ERC20;
constructor(address token_) Controller(token_) {
bridgeType = FIAT_TOKEN_CONTROLLER;
}
function _burn(address user_, uint256 burnAmount_) internal override {
ERC20(token).safeTransferFrom(user_, address(this), burnAmount_);
IFiatTokenV2_1_Mintable(address(token)).burn(burnAmount_);
}
}
"
},
"contracts/bridge/FiatTokenV2_1/IFiatTokenV2_1_Mintable.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "lib/solmate/src/tokens/ERC20.sol";
// USDC's standard token
abstract contract IFiatTokenV2_1_Mintable is ERC20 {
function mint(address receiver_, uint256 amount_) external virtual;
function burn(uint256 _amount) external virtual;
}
"
},
"contracts/bridge/Vault.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 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.
*/
contract Vault is Base {
using SafeTransferLib for ERC20;
// /**
// * @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 extraData_ The extra data passed to hook functions.
* @param options_ Additional options for the bridging process.
*/
function bridge(
address receiver_,
uint256 amount_,
uint256 msgGasLimit_,
address connector_,
bytes calldata extraData_,
bytes calldata options_
) external payable nonReentrant {
(
TransferInfo memory transferInfo,
bytes memory postHookData
) = _beforeBridge(
connector_,
TransferInfo(receiver_, amount_, extraData_)
);
_receiveTokens(transferInfo.amount);
_afterBridge(
msgGasLimit_,
connector_,
options_,
postHookData,
transferInfo
);
}
/**
* @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.
*/
function receiveInbound(
uint32 siblingChainSlug_,
bytes memory payload_
) external payable override nonReentrant {
(
address receiver,
uint256 unlockAmount,
bytes32 messageId,
bytes memory extraData
) = abi.decode(payload_, (address, uint256, bytes32, bytes));
TransferInfo memory transferInfo = TransferInfo(
receiver,
unlockAmount,
extraData
);
bytes memory 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.
*/
function retry(
address connector_,
bytes32 messageId_
) external nonReentrant {
(
bytes memory postHookData,
TransferInfo memory transferInfo
) = _beforeRetry(connector_, messageId_);
_transferTokens(transferInfo.receiver, transferInfo.amount);
_afterRetry(connector_, messageId_, postHookData);
}
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_);
}
}
"
},
"contracts/common/Constants.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
address constant ETH_ADDRESS = address(
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE
);
bytes32 constant NORMAL_CONTROLLER = keccak256("NORMAL_CONTROLLER");
bytes32 constant FIAT_TOKEN_CONTROLLER = keccak256("FIAT_TOKEN_CONTROLLER");
bytes32 constant LIMIT_HOOK = keccak256("LIMIT_HOOK");
bytes32 constant LIMIT_EXECUTION_HOOK = keccak256("LIMIT_EXECUTION_HOOK");
bytes32 constant LIMIT_EXECUTION_YIELD_HOOK = keccak256(
"LIMIT_EXECUTION_YIELD_HOOK"
);
bytes32 constant LIMIT_EXECUTION_YIELD_TOKEN_HOOK = keccak256(
"LIMIT_EXECUTION_YIELD_TOKEN_HOOK"
);
bytes32 constant ERC20_VAULT = keccak256("ERC20_VAULT");
bytes32 constant NATIVE_VAULT = keccak256("NATIVE_VAULT");
"
},
"contracts/common/Errors.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
error SiblingNotSupported();
error NotAuthorized();
error NotBridge();
error NotSocket();
error ConnectorUnavailable();
error InvalidPoolId();
error CannotTransferOrExecuteOnBridgeContracts();
error NoPendingData();
error MessageIdMisMatched();
error NotMessageBridge();
error InvalidSiblingChainSlug();
error InvalidTokenContract();
error InvalidExchangeRateContract();
error InvalidConnector();
error InvalidConnectorPoolId();
error ZeroAddressReceiver();
error ZeroAddress();
error ZeroAmount();
error DebtRatioTooHigh();
error NotEnoughAssets();
error VaultShutdown();
error InsufficientFunds();
error PermitDeadlineExpired();
error InvalidSigner();
error InsufficientMsgValue();
error InvalidOptionsLength();
"
},
"contracts/common/Structs.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
struct UpdateLimitParams {
bool isMint;
address connector;
uint256 maxLimit;
uint256 ratePerSecond;
}
struct SrcPreHookCallParams {
address connector;
address msgSender;
TransferInfo transferInfo;
}
struct SrcPostHookCallParams {
address connector;
bytes options;
bytes postHookData;
TransferInfo transferInfo;
}
struct DstPreHookCallParams {
address connector;
bytes connectorCache;
TransferInfo transferInfo;
}
struct DstPostHookCallParams {
address connector;
bytes32 messageId;
bytes connectorCache;
bytes postHookData;
TransferInfo transferInfo;
}
struct PreRetryHookCallParams {
address connector;
CacheData cacheData;
}
struct PostRetryHookCallParams {
address connector;
bytes32 messageId;
bytes postHookData;
CacheData cacheData;
}
struct TransferInfo {
address receiver;
uint256 amount;
bytes extraData;
}
struct CacheData {
bytes identifierCache;
bytes connectorCache;
}
struct LimitParams {
uint256 lastUpdateTimestamp;
uint256 ratePerSecond;
uint256 maxLimit;
uint256 lastUpdateLimit;
}
"
},
"contracts/ConnectorPlug.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "./utils/RescueBase.sol";
import {ISocket} from "./interfaces/ISocket.sol";
import {IPlug} from "./interfaces/IPlug.sol";
import {IConnector} from "./interfaces/IConnector.sol";
import {IBridge} from "./interfaces/IBridge.sol";
import "./common/Errors.sol";
contract ConnectorPlug is IConnector, IPlug, RescueBase {
IBridge public immutable bridge__;
ISocket public immutable socket__;
uint32 public immutable siblingChainSlug;
bytes32 public immutable transmissionParams;
uint256 public messageIdPart;
event ConnectorPlugDisconnected();
constructor(
address bridge_,
address socket_,
uint32 siblingChainSlug_,
bytes32 transmissionParams_
) AccessControl(msg.sender) {
bridge__ = IBridge(bridge_);
socket__ = ISocket(socket_);
siblingChainSlug = siblingChainSlug_;
transmissionParams = transmissionParams_;
_grantRole(RESCUE_ROLE, msg.sender);
}
function outbound(
uint256 msgGasLimit_,
bytes memory payload_,
bytes memory options_
) external payable override returns (bytes32 messageId_) {
if (msg.sender != address(bridge__)) revert NotBridge();
if (options_.length == 0) {
return
socket__.outbound{value: msg.value}(
siblingChainSlug,
msgGasLimit_,
bytes32(0),
transmissionParams,
payload_
);
} else {
if (options_.length != 32) revert InvalidOptionsLength();
bytes32 executionParams = abi.decode(options_, (bytes32));
return
socket__.outbound{value: msg.value}(
siblingChainSlug,
msgGasLimit_,
executionParams,
transmissionParams,
payload_
);
}
}
function inbound(
uint32 siblingChainSlug_, // cannot be connected for any other slug, immutable variable
bytes calldata payload_
) external payable override {
if (msg.sender != address(socket__)) revert NotSocket();
bridge__.receiveInbound(siblingChainSlug_, payload_);
}
/**
* @notice this function calculates the fees needed to send the message to Socket.
* @param msgGasLimit_ min gas limit needed at destination chain to execute the message.
*/
function getMinFees(
uint256 msgGasLimit_,
uint256 payloadSize_
) external view returns (uint256 totalFees) {
return
socket__.getMinFees(
msgGasLimit_,
payloadSize_,
bytes32(0),
bytes32(0),
siblingChainSlug,
address(this)
);
}
function connect(
address siblingPlug_,
address switchboard_
) external onlyOwner {
messageIdPart =
(uint256(socket__.chainSlug()) << 224) |
(uint256(uint160(siblingPlug_)) << 64);
socket__.connect(
siblingChainSlug,
siblingPlug_,
switchboard_,
switchboard_
);
}
function disconnect() external onlyOwner {
messageIdPart = 0;
(
,
address inboundSwitchboard,
address outboundSwitchboard,
,
) = socket__.getPlugConfig(address(this), siblingChainSlug);
socket__.connect(
siblingChainSlug,
address(0),
inboundSwitchboard,
outboundSwitchboard
);
emit ConnectorPlugDisconnected();
}
/**
* @notice this function is used to calculate message id before sending outbound().
* @return messageId
*/
function getMessageId() external view returns (bytes32) {
return bytes32(messageIdPart | (socket__.globalMessageCount()));
}
}
"
},
"contracts/hooks/Controller_YieldLimitExecHook.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "lib/openzeppelin-contracts/contracts/utils/math/Math.sol";
import {FixedPointMathLib} from "lib/solmate/src/utils/FixedPointMathLib.sol";
import {IStrategy} from "../interfaces/IStrategy.sol";
import {IMintableERC20} from "../interfaces/IMintableERC20.sol";
import "lib/solmate/src/utils/SafeTransferLib.sol";
import {IConnector} from "../ConnectorPlug.sol";
import "./LimitExecutionHook.sol";
interface IYieldToken {
function updateTotalUnderlyingAssets(uint256 amount_) external;
function calculateMintAmount(uint256 amount_) external returns (uint256);
function convertToShares(
uint256 underlyingAssets
) external view returns (uint256);
function transfer(address to_, uint256 amount_) external returns (bool);
function convertToAssets(uint256 shares) external view returns (uint256);
}
// limits on underlying or visible tokens
contract Controller_YieldLimitExecHook is LimitExecutionHook {
using SafeTransferLib for IMintableERC20;
using FixedPointMathLib for uint256;
uint256 private constant MAX_BPS = 10_000;
IYieldToken public immutable yieldToken__;
// total yield
uint256 public totalUnderlyingAssets;
// if true, no funds can be invested in the strategy
bool public emergencyShutdown;
event ShutdownStateUpdated(bool shutdownState);
modifier notShutdown() {
if (emergencyShutdown) revert VaultShutdown();
_;
}
constructor(
address underlyingAsset_,
address controller_,
address executionHelper_
) LimitExecutionHook(msg.sender, controller_, executionHelper_, true) {
yieldToken__ = IYieldToken(underlyingAsset_);
hookType = LIMIT_EXECUTION_YIELD_TOKEN_HOOK;
_grantRole(LIMIT_UPDATER_ROLE, msg.sender);
}
// assumed transfer info inputs are validated at controller
// transfer info data is untrusted
function srcPreHookCall(
SrcPreHookCallParams calldata params_
)
public
override
notShutdown
returns (TransferInfo memory transferInfo, bytes memory postHookData)
{
super.srcPreHookCall(params_);
uint256 amount = params_.transferInfo.amount;
postHookData = abi.encode(amount);
totalUnderlyingAssets -= amount;
transferInfo = params_.transferInfo;
transferInfo.amount = yieldToken__.convertToShares(amount);
}
function srcPostHookCall(
SrcPostHookCallParams memory srcPostHookCallParams_
)
public
override
isVaultOrController
returns (TransferInfo memory transferInfo)
{
yieldToken__.updateTotalUnderlyingAssets(totalUnderlyingAssets);
transferInfo.receiver = srcPostHookCallParams_.transferInfo.receiver;
transferInfo.extraData = abi.encode(
srcPostHookCallParams_.options,
srcPostHookCallParams_.transferInfo.extraData
);
transferInfo.amount = abi.decode(
srcPostHookCallParams_.postHookData,
(uint256)
);
}
/**
* @notice This function is called before the execution of a destination hook.
* @dev It checks if the sibling chain is supported, consumes a part of the limit, and prepares post-hook data.
*/
function dstPreHookCall(
DstPreHookCallParams calldata params_
)
public
override
notShutdown
isVaultOrController
returns (bytes memory postHookData, TransferInfo memory transferInfo)
{
(uint256 increasedUnderlying, bytes memory payload) = abi.decode(
params_.transferInfo.extraData,
(uint256, bytes)
);
_poolDstHook(params_.connector, increasedUnderlying);
totalUnderlyingAssets += increasedUnderlying;
yieldToken__.updateTotalUnderlyingAssets(totalUnderlyingAssets);
yieldToken__.updateTotalUnderlyingAssets(totalUnderlyingAssets);
if (params_.transferInfo.amount == 0)
return (abi.encode(0, 0, 0, address(0)), transferInfo);
(uint256 consumedUnderlying, uint256 pendingUnderlying) = _limitDstHook(
params_.connector,
params_.transferInfo.amount
);
uint256 sharesToMint = yieldToken__.calculateMintAmount(
params_.transferInfo.amount
);
postHookData = abi.encode(
consumedUnderlying,
pendingUnderlying,
params_.transferInfo.amount,
params_.transferInfo.receiver
);
transferInfo = params_.transferInfo;
if (pendingUnderlying != 0) transferInfo.receiver = address(this);
transferInfo.amount = sharesToMint;
transferInfo.extraData = payload;
}
/**
* @notice Handles post-hook logic after the execution of a destination hook.
* @dev This function processes post-hook data to update the identifier cache and sibling chain cache.
*/
function dstPostHookCall(
DstPostHookCallParams calldata params_
)
public
override
isVaultOrController
notShutdown
returns (CacheData memory cacheData)
{
(
uint256 consumedUnderlying,
uint256 pendingUnderlying,
uint256 depositUnderlying,
address receiver
) = abi.decode(
params_.postHookData,
(uint256, uint256, uint256, address)
);
bytes memory execPayload = params_.transferInfo.extraData;
uint256 connectorPendingShares = _getConnectorPendingAmount(
params_.connectorCache
);
uint256 pendingShares;
if (pendingUnderlying > 0) {
// totalShares * consumedU / totalU
uint256 consumedShares = (params_.transferInfo.amount *
pendingUnderlying) / depositUnderlying;
pendingShares = params_.transferInfo.amount - consumedShares;
cacheData.identifierCache = abi.encode(
params_.transferInfo.receiver,
pendingShares,
params_.connector,
execPayload
);
yieldToken__.transfer(receiver, consumedUnderlying);
emit TokensPending(
params_.connector,
params_.transferInfo.receiver,
consumedShares,
pendingShares,
params_.messageId
);
} else {
if (execPayload.length > 0) {
// execute
bool success = executionHelper__.execute(
params_.transferInfo.receiver,
execPayload,
params_.messageId,
depositUnderlying
);
if (success) {
emit MessageExecuted(
params_.messageId,
params_.transferInfo.receiver
);
cacheData.identifierCache = new bytes(0);
} else
cacheData.identifierCache = abi.encode(
params_.transferInfo.receiver,
0,
params_.connector,
execPayload
);
} else cacheData.identifierCache = new bytes(0);
}
cacheData.connectorCache = abi.encode(
connectorPendingShares + pendingShares
);
}
// /**
// * @notice Handles pre-retry hook logic before execution.
// * @dev This function can be used to mint funds which were in a pending state due to limits.
// * @param siblingChainSlug_ The unique identifier of the sibling chain.
// * @param identifierCache_ Identifier cache containing pending mint information.
// * @param connectorCache_ Sibling chain cache containing pending amount information.
// * @return updatedReceiver The updated receiver of the funds.
// * @return consumedUnderlying The amount consumed from the limit.
// * @return postHookData The post-hook data to be processed after the retry hook execution.
// */
function preRetryHook(
PreRetryHookCallParams calldata params_
)
public
override
isVaultOrController
notShutdown
returns (bytes memory postHookData, TransferInfo memory transferInfo)
{
(
address receiver,
uint256 totalPendingShares,
address connector,
) = abi.decode(
params_.cacheData.identifierCache,
(address, uint256, address, bytes)
);
if (connector != params_.connector) revert InvalidConnector();
(uint256 consumedShares, uint256 pendingShares) = _limitDstHook(
params_.connector,
totalPendingShares
);
postHookData = abi.encode(receiver, consumedShares, pendingShares);
uint256 consumedUnderlying = yieldToken__.convertToAssets(
consumedShares
);
yieldToken__.transfer(receiver, consumedUnderlying);
transferInfo = TransferInfo(transferInfo.receiver, 0, bytes(""));
}
// /**
// * @notice Handles post-retry hook logic after execution.
// * @dev This function updates the identifier cache and sibling chain cache based on the post-hook data.
// * @param siblingChainSlug_ The unique identifier of the sibling chain.
// * @param identifierCache_ Identifier cache containing pending mint information.
// * @param connectorCache_ Sibling chain cache containing pending amount information.
// * @param postHookData_ The post-hook data containing updated receiver and consumed/pending amounts.
// * @return newIdentifierCache The updated identifier cache.
// * @return newConnectorCache The updated sibling chain cache.
// */
function postRetryHook(
PostRetryHookCallParams calldata params_
) public override returns (CacheData memory cacheData) {
return super.postRetryHook(params_);
}
function updateEmergencyShutdownState(
bool shutdownState_
) external onlyOwner {
emergencyShutdown = shutdownState_;
emit ShutdownStateUpdated(shutdownState_);
}
}
"
},
"contracts/hooks/HookBase.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "lib/solmate/src/utils/ReentrancyGuard.sol";
import "../common/Errors.sol";
import "../common/Constants.sol";
import "../interfaces/IHook.sol";
import "../utils/RescueBase.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.
*/
abstract contract HookBase is ReentrancyGuard, IHook, RescueBase {
address public immutable vaultOrController;
bytes32 public hookType;
/**
* @notice Constructor for creating a new SuperToken.
*/
constructor(
address owner_,
address vaultOrController_
) AccessControl(owner_) {
vaultOrController = vaultOrController_;
_grantRole(RESCUE_ROLE, owner_);
}
modifier isVaultOrController() {
if (msg.sender != vaultOrController) revert NotAuthorized();
_;
}
}
"
},
"contracts/hooks/LimitExecutionHook.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "./plugins/LimitPlugin.sol";
import "./plugins/ExecutionHelper.sol";
import "./plugins/ConnectorPoolPlugin.sol";
import "../interfaces/IController.sol";
contract LimitExecutionHook is LimitPlugin, ConnectorPoolPlugin {
bool public useControllerPools;
ExecutionHelper executionHelper__;
event MessageExecuted(bytes32 indexed messageId, address indexed receiver);
/**
* @notice Constructor for creating a new SuperToken.
* @param owner_ Owner of this contract.
*/
constructor(
address owner_,
address controller_,
address executionHelper_,
bool useControllerPools_
) HookBase(owner_, controller_) {
useControllerPools = useControllerPools_;
executionHelper__ = ExecutionHelper(executionHelper_);
hookType = LIMIT_EXECUTION_HOOK;
_grantRole(LIMIT_UPDATER_ROLE, owner_);
}
function setExecutionHelper(address executionHelper_) external onlyOwner {
executionHelper__ = ExecutionHelper(executionHelper_);
}
function srcPreHookCall(
SrcPreHookCallParams calldata params_
)
public
virtual
isVaultOrController
returns (TransferInfo memory, bytes memory)
{
if (useControllerPools)
_poolSrcHook(params_.connector, params_.transferInfo.amount);
_limitSrcHook(params_.connector, params_.transferInfo.amount);
return (params_.transferInfo, bytes(""));
}
function srcPostHookCall(
SrcPostHookCallParams memory params_
) public virtual isVaultOrController returns (TransferInfo memory) {
return params_.transferInfo;
}
function dstPreHookCall(
DstPreHookCallParams calldata params_
)
public
virtual
isVaultOrController
returns (bytes memory postHookData, TransferInfo memory transferInfo)
{
if (useControllerPools)
_poolDstHook(params_.connector, params_.transferInfo.amount);
(uint256 consumedAmount, uint256 pendingAmount) = _limitDstHook(
params_.connector,
params_.transferInfo.amount
);
postHookData = abi.encode(
consumedAmount,
pendingAmount,
params_.transferInfo.amount
);
transferInfo = params_.transferInfo;
transferInfo.amount = consumedAmount;
}
function dstPostHookCall(
DstPostHookCallParams calldata params_
) public virtual isVaultOrController returns (CacheData memory cacheData) {
bytes memory execPayload = params_.transferInfo.extraData;
(
uint256 consumedAmount,
uint256 pendingAmount,
uint256 bridgeAmount
) = abi.decode(params_.postHookData, (uint256, uint256, uint256));
uint256 connectorPendingAmount = _getConnectorPendingAmount(
params_.connectorCache
);
cacheData.connectorCache = abi.encode(
connectorPendingAmount + pendingAmount
);
cacheData.identifierCache = abi.encode(
params_.transferInfo.receiver,
pendingAmount,
bridgeAmount,
params_.connector,
execPayload
);
if (pendingAmount > 0) {
emit TokensPending(
params_.connector,
params_.transferInfo.receiver,
consumedAmount,
pendingAmount,
params_.messageId
);
} else {
if (execPayload.length > 0) {
// execute
bool success = executionHelper__.execute(
params_.transferInfo.receiver,
execPayload,
params_.messageId,
bridgeAmount
);
if (success) {
emit MessageExecuted(
params_.messageId,
params_.transferInfo.receiver
);
cacheData.identifierCache = new bytes(0);
}
} else cacheData.identifierCache = new bytes(0);
}
}
function preRetryHook(
PreRetryHookCallParams calldata params_
)
public
virtual
isVaultOrController
returns (bytes memory postHookData, TransferInfo memory transferInfo)
{
(address receiver, uint256 pendingMint, , address connector, ) = abi
.decode(
params_.cacheData.identifierCache,
(address, uint256, uint256, address, bytes)
);
if (connector != params_.connector) revert InvalidConnector();
(uint256 consumedAmount, uint256 pendingAmount) = _limitDstHook(
params_.connector,
pendingMint
);
postHookData = abi.encode(receiver, consumedAmount, pendingAmount);
transferInfo = TransferInfo(receiver, consumedAmount, bytes(""));
}
function postRetryHook(
PostRetryHookCallParams calldata params_
) public virtual isVaultOrController returns (CacheData memory cacheData) {
(
,
,
uint256 bridgeAmount,
address connector,
bytes memory execPayload
) = abi.decode(
params_.cacheData.identifierCache,
(address, uint256, uint256, address, bytes)
);
(address receiver, uint256 consumedAmount, uint256 pendingAmount) = abi
.decode(params_.postHookData, (address, uint256, uint256));
uint256 connectorPendingAmount = _getConnectorPendingAmount(
params_.cacheData.connectorCache
);
cacheData.connectorCache = abi.encode(
connectorPendingAmount - consumedAmount
);
cacheData.identifierCache = abi.encode(
receiver,
pendingAmount,
bridgeAmount,
connector,
execPayload
);
emit PendingTokensBridged(
params_.connector,
receiver,
consumedAmount,
pendingAmount,
params_.messageId
);
if (pendingAmount == 0) {
// receiver is not an input from user, can receiver check
// no connector check required here, as already done in preRetryHook call in same tx
// execute
bool success = executionHelper__.execute(
receiver,
execPayload,
params_.messageId,
bridgeAmount
);
if (success) {
emit MessageExecuted(params_.messageId, receiver);
cacheData.identifierCache = new bytes(0);
}
}
}
function getConnectorPendingAmount(
address connector_
) external returns (uint256) {
bytes memory cache = IController(vaultOrController).connectorCache(
connector_
);
return _getConnectorPendingAmount(cache);
}
function _getIdentifierPendingAmount(
bytes memory identifierCache_
) internal pure returns (uint256) {
if (identifierCache_.length > 0) {
(, uint256 pendingAmount, , , ) = abi.decode(
identifierCache_,
(address, uint256, uint256, address, bytes)
);
return pendingAmount;
} else return 0;
}
function getIdentifierPendingAmount(
bytes32 messageId_
) external returns (uint256) {
bytes memory cache = IController(vaultOrController).identifierCache(
messageId_
);
return _getIdentifierPendingAmount(cache);
}
}
"
},
"contracts/hooks/LimitHook.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "./plugins/LimitPlugin.sol";
import "../interfaces/IController.sol";
import "./plugins/ConnectorPoolPlugin.sol";
contract LimitHook is LimitPlugin, ConnectorPoolPlugin {
bool public immutable useControllerPools;
/**
* @notice Constructor for creating a new SuperToken.
* @param owner_ Owner of this contract.
*/
constructor(
address owner_,
address controller_,
bool useControllerPools_
) HookBase(owner_, controller_) {
useControllerPools = useControllerPools_;
hookType = LIMIT_HOOK;
_grantRole(LIMIT_UPDATER_ROLE, owner_);
}
function srcPreHookCall(
SrcPreHookCallParams memory params_
)
external
isVaultOrController
returns (TransferInfo memory transferInfo, bytes memory postHookData)
{
if (useControllerPools)
_poolSrcHook(params_.connector, params_.transferInfo.amount);
_limitSrcHook(params_.connector, params_.transferInfo.amount);
transferInfo = params_.transferInfo;
postHookData = hex"";
}
function srcPostHookCall(
SrcPostHookCallParams memory params_
) external view isVaultOrController returns (TransferInfo memory) {
return params_.transferInfo;
}
function dstPreHookCall(
DstPreHookCallParams memory params_
)
external
isVaultOrController
returns (bytes memory postHookData, TransferInfo memory transferInfo)
{
if (useControllerPools)
_poolDstHook(params_.connector, params_.transferInfo.amount);
(uint256 consumedAmount, uint256 pendingAmount) = _limitDstHook(
params_.connector,
params_.transferInfo.amount
);
postHookData = abi.encode(consumedAmount, pendingAmount);
transferInfo = params_.transferInfo;
transferInfo.amount = consumedAmount;
}
function dstPostHookCall(
DstPostHookCallParams memory params_
) external isVaultOrController returns (CacheData memory cacheData) {
(uint256 consumedAmount, uint256 pendingAmount) = abi.decode(
params_.postHookData,
(uint256, uint256)
);
uint256 connectorPendingAmount = _getConnectorPendingAmount(
params_.connectorCache
);
if (pendingAmount > 0) {
cacheData = CacheData(
abi.encode(
params_.transferInfo.receiver,
pendingAmount,
params_.connector
),
abi.encode(connectorPendingAmount + pendingAmount)
);
emit TokensPending(
params_.connector,
params_.transferInfo.receiver,
consumedAmount,
pendingAmount,
params_.messageId
);
} else {
cacheData = CacheData(
bytes(""),
abi.encode(connectorPendingAmount + pendingAmount)
);
}
}
function preRetryHook(
PreRetryHookCallParams memory params_
)
external
nonReentrant
isVaultOrController
returns (bytes memory postHookData, TransferInfo memory transferInfo)
{
(address receiver, uint256 pendingMint, address connector) = abi.decode(
params_.cacheData.identifierCache,
(address, uint256, address)
);
if (connector != params_.connector) revert InvalidConnector();
(uint256 consumedAmount, uint256 pendingAmount) = _limitDstHook(
params_.connector,
pendingMint
);
postHookData = abi.encode(receiver, consumedAmount, pendingAmount);
transferInfo = TransferInfo(receiver, consumedAmount, bytes(""));
}
function postRetryHook(
PostRetryHookCallParams calldata params_
)
external
isVaultOrController
nonReentrant
returns (CacheData memory cacheData)
{
(address receiver, uint256 consumedAmount, uint256 pendingAmount) = abi
.decode(params_.postHookData, (address, uint256, uint256));
// code reaches here after minting/unlocking the pending amount
emit PendingTokensBridged(
params_.connector,
receiver,
consumedAmount,
pendingAmount,
params_.messageId
);
uint256 connectorPendingAmount = _getConnectorPendingAmount(
params_.cacheData.connectorCache
);
cacheData.connectorCache = abi.encode(
connectorPendingAmount - consumedAmount
);
cacheData.identifierCache = abi.encode(
receiver,
pendingAmount,
params_.connector
);
if (pendingAmount == 0) {
cacheData.identifierCache = new bytes(0);
}
}
function getConnectorPendingAmount(
address connector_
) external returns (uint256) {
bytes memory cache = IController(vaultOrController).connectorCache(
connector_
);
return _getConnectorPendingAmount(cache);
}
function _getIdentifierPendingAmount(
bytes memory identifierCache_
) internal pure returns (uint256) {
if (identifierCache_.length > 0) {
(, uint256 pendingAmount, ) = abi.decode(
identifierCache_,
(address, uint256, address)
);
return pendingAmount;
} else return 0;
}
function getIdentifierPendingAmount(
bytes32 messageId_
) external returns (uint256) {
bytes memory cache = IController(vaultOrController).identifierCache(
messageId_
);
return _getIdentifierPendingAmount(cache);
}
}
"
},
"contracts/hooks/plugins/ConnectorPoolPlugin.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "../HookBase.sol";
abstract contract ConnectorPoolPlugin is HookBase {
// connectorPoolId => totalLockedAmount
mapping(uint256 => uint256) public poolLockedAmounts;
// connector => connectorPoolId
mapping(address => uint256) public connectorPoolIds;
event ConnectorPoolIdUpdated(address connector, uint256 poolId);
event PoolLockedAmountUpdated(uint256 poolId, uint256 amount);
function updateConnectorPoolId(
address[] calldata connectors,
uint256[] calldata poolIds_
) external onlyOwner {
uint256 length = connectors.length;
for (uint256 i; i < length; i++) {
if (poolIds_[i] == 0) revert InvalidPoolId();
connectorPoolIds[connectors[i]] = poolIds_[i];
emit ConnectorPoolIdUpdated(connectors[i], poolIds_[i]);
}
}
function updatePoolLockedAmounts(
uint256[] calldata poolIds_,
uint256[] calldata amounts_
) external onlyOwner {
uint256 length = poolIds_.length;
for (uint256 i; i < length; i++) {
if (poolIds_[i] == 0) revert InvalidPoolId();
poolLockedAmounts[poolIds_[i]] = amounts_[i];
emit PoolLockedAmountUpdated(poolIds_[i], amounts_[i]);
}
}
function _poolSrcHook(address connector_, uint256 amount_) internal {
uint256 connectorPoolId = connectorPoolIds[connector_];
if (connectorPoolId == 0) revert InvalidPoolId();
if (amount_ > poolLockedAmounts[connectorPoolId])
revert InsufficientFunds();
poolLockedAmounts[connectorPoolId] -= amount_;
}
function _poolDstHook(
address connector_,
uint256 amount_
) internal returns (uint256 oldLockedAmount) {
uint256 connectorPoolId = connectorPoolIds[connector_];
if (connectorPoolId == 0) revert InvalidPoolId();
oldLockedAmount = poolLockedAmounts[connectorPoolId];
poolLockedAmounts[connectorPoolId] += amount_;
}
}
"
},
"contracts/hooks/plugins/ExecutionHelper.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "../../libraries/ExcessivelySafeCall.sol";
import "../../utils/RescueBase.sol";
import "../../common/Errors.sol";
/**
* @title ExecutionHelper
* @notice It is an untrusted contract used for payload execution by Super token and Vault.
*/
contract ExecutionHelper is RescueBase {
using ExcessivelySafeCall for address;
uint16 private constant MAX_COPY_BYTES = 0;
address public hook;
bytes32 public messageId;
uint256 public bridgeAmount;
event ExecutionFailed(bytes reason);
constructor(address owner_) AccessControl(owner_) {
_grantRole(RESCUE_ROLE, owner_);
}
modifier onlyHook() {
require(msg.sender == hook, "ExecutionHelper: only hook");
_;
}
function setHook(address hook_) external onlyOwner {
hook = hook_;
}
/**
* @notice this function is used to execute a payload at target_
* @dev receiver address cannot be this contract address.
* @param target_ address of target.
* @param payload_ payload to be executed at target.
*/
function execute(
address target_,
bytes memory payload_,
bytes32 messageId_,
uint256 bridgeAmount_
) external onlyHook returns (bool success) {
if (target_ == address(this)) return false;
messageId = messageId_;
bridgeAmount = bridgeAmount_;
bytes memory returnData;
(success, returnData) = target_.excessivelySafeCall(
gasleft(),
MAX_COPY_BYTES,
payload_
);
if (!success) emit ExecutionFailed(_getRevertMsg(returnData));
messageId = bytes32(0);
bridgeAmount = 0;
}
function _getRevertMsg(
bytes memory _returnData
) internal pure returns (bytes memory) {
// If the _res length is less than 68, then the transaction failed silently (without a revert message)
if (_returnData.length < 68) return bytes("");
return _returnData;
}
}
"
},
"contracts/hooks/plugins/LimitPlugin.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "../HookBase.sol";
import {Gauge} from "../../utils/Gauge.sol";
abstract contract LimitPlugin is Gauge, HookBase {
bytes32 constant LIMIT_UPDATER_ROLE = keccak256("LIMIT_UPDATER_ROLE");
// connector => receivingLimitParams
mapping(address => LimitParams) _receivingLimitParams;
// connector => sendingLimitParams
mapping(address => LimitParams) _sendingLimitParams;
////////////////////////////////////////////////////////
////////////////////// EVENTS //////////////////////////
////////////////////////////////////////////////////////
// Emitted when limit parameters are updated
event LimitParamsUpdated(UpdateLimitParams[] updates);
// Emitted when pending tokens are minted to the receiver
event PendingTokensBridged(
address connector,
address receiver,
uint256 consumedAmount,
uint256 pendingAmount,
bytes32 messageId
);
// Emitted when the transfer reaches the limit, and the token mint is added to the pending queue
event TokensPending(
address connector,
address receiver,
uint256 consumedAmount,
uint256 pendingAmount,
bytes32 messageId
);
/**
* @notice This function is used to set bridge limits.
* @dev It can only be updated by the owner.
* @param updates An array of structs containing update parameters.
*/
function updateLimitParams(
UpdateLimitParams[] calldata updates
) external onlyRole(LIMIT_UPDATER_ROLE) {
for (uint256 i = 0; i < updates.length; i++) {
if (updates[i].isMint) {
_consumePartLimit(
0,
_receivingLimitParams[updates[i].connector]
); // To keep the current limit in sync
_receivingLimitParams[updates[i].connector].maxLimit = updates[
i
].maxLimit;
_receivingLimitParams[updates[i].connector]
.ratePerSecond = updates[i].ratePerSecond;
} else {
_consumePartLimit(0, _sendingLimitParams[updates[i].connector]); // To keep the current limit in sync
_sendingLimitParams[updates[i].connector].maxLimit = updates[i]
.maxLimit;
_sendingLimitParams[updates[i].connector]
.ratePerSecond = updates[i].ratePerSecond;
}
}
emit LimitParamsUpdated(updates);
}
function getCurrentReceivingLimit(
address connector_
) external view returns (uint256) {
return _getCurrentLimit(_receivingLimitParams[connector_]);
}
function getCurrentSendingLimit(
address connector_
) external view returns (uint256) {
return _getCurrentLimit(_sendingLimitParams[connector_]);
}
function getReceivingLimitParams(
address connector_
) external view returns (LimitParams memory) {
return _receivingLimitParams[connector_];
}
function getSendingLimitParams(
address connector_
) external view returns (LimitParams memory) {
return _sendingLimitParams[connector_];
}
function _limitSrcHook(address connector_, uint256 amount_) internal {
if (_sendingLimitParams[connector_].maxLimit == 0)
revert SiblingNotSupported();
_consumeFullLimit(amount_, _sendingLimitParams[connector_]); // Reverts on limit hit
}
function _limitDstHook(
address connector_,
uint256 amount_
) internal returns (uint256 consumedAmount, uint256 pendingAmount) {
if (_receivingLimitParams[connector_].maxLimit == 0)
revert SiblingNotSupported();
(consumedAmount, pendingAmount) = _consumePartLimit(
amount_,
_receivingLimitParams[connector_]
);
}
function _getConnectorPendingAmount(
bytes memory connectorCache_
) internal pure returns (uint256) {
if (connectorCache_.length > 0) {
return abi.decode(connectorCache_, (uint256));
} else return 0;
}
}
"
},
"contracts/hooks/Vault_YieldLimitExecHook.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;
import "lib/openzeppelin-contracts/contracts/utils/math/Math.sol
Submitted on: 2025-11-05 10:44:58
Comments
Log in to comment.
No comments yet.