pragmasolidity 0.8.13;import"lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol";
import {ISocket} from"../interfaces/ISocket.sol";
import {IPlug} from"../interfaces/IPlug.sol";
import {RescueFundsLib} from"./RescueFundsLib.sol";
interfaceIHub{
functionreceiveInbound(bytesmemory payload_) external;
}
interfaceIConnector{
functionoutbound(uint256 msgGasLimit_,
bytesmemory payload_
) externalpayable;
functionsiblingChainSlug() externalviewreturns (uint32);
functiongetMinFees(uint256 msgGasLimit_
) externalviewreturns (uint256 totalFees);
}
contractConnectorPlugisIConnector, IPlug, Ownable2Step{
IHub publicimmutable hub__;
ISocket publicimmutable socket__;
uint32publicimmutable siblingChainSlug;
errorNotHub();
errorNotSocket();
eventConnectorPlugDisconnected();
constructor(address hub_, address socket_, uint32 siblingChainSlug_) {
hub__ = IHub(hub_);
socket__ = ISocket(socket_);
siblingChainSlug = siblingChainSlug_;
}
functionoutbound(uint256 msgGasLimit_,
bytesmemory payload_
) externalpayableoverride{
if (msg.sender!=address(hub__)) revert NotHub();
socket__.outbound{value: msg.value}(
siblingChainSlug,
msgGasLimit_,
bytes32(0),
bytes32(0),
payload_
);
}
functioninbound(uint32/* siblingChainSlug_ */, // cannot be connected for any other slug, immutable variablebytescalldata payload_
) externalpayableoverride{
if (msg.sender!=address(socket__)) revert NotSocket();
hub__.receiveInbound(payload_);
}
functiongetMinFees(uint256 msgGasLimit_
) externalviewoverridereturns (uint256 totalFees) {
return
socket__.getMinFees(
msgGasLimit_,
64,
bytes32(0),
bytes32(0),
siblingChainSlug,
address(this)
);
}
functionconnect(address siblingPlug_,
address switchboard_
) externalonlyOwner{
socket__.connect(
siblingChainSlug,
siblingPlug_,
switchboard_,
switchboard_
);
}
functiondisconnect() externalonlyOwner{
(
,
address inboundSwitchboard,
address outboundSwitchboard,
,
) = socket__.getPlugConfig(address(this), siblingChainSlug);
socket__.connect(
siblingChainSlug,
address(0),
inboundSwitchboard,
outboundSwitchboard
);
emit ConnectorPlugDisconnected();
}
/**
* @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_
) externalonlyOwner{
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}
Contract Source Code
File 2 of 11: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 3 of 11: ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=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.abstractcontractERC20{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256 amount);
eventApproval(addressindexed owner, addressindexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
uint8publicimmutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/uint256public totalSupply;
mapping(address=>uint256) public balanceOf;
mapping(address=>mapping(address=>uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/uint256internalimmutable INITIAL_CHAIN_ID;
bytes32internalimmutable INITIAL_DOMAIN_SEPARATOR;
mapping(address=>uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID =block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 amount) publicvirtualreturns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
returntrue;
}
functiontransfer(address to, uint256 amount) publicvirtualreturns (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);
returntrue;
}
functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualreturns (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);
returntrue;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/functionpermit(address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) publicvirtual{
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);
}
functionDOMAIN_SEPARATOR() publicviewvirtualreturns (bytes32) {
returnblock.chainid== INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
functioncomputeDomainSeparator() internalviewvirtualreturns (bytes32) {
returnkeccak256(
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) internalvirtual{
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(addressfrom, uint256 amount) internalvirtual{
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-onlypragmasolidity 0.8.13;/**
* @title IPlug
* @notice Interface for a plug contract that executes the message received from a source chain.
*/interfaceIPlug{
/**
* @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
*/functioninbound(uint32 srcChainSlug_,
bytescalldata payload_
) externalpayable;
}
Contract Source Code
File 6 of 11: ISocket.sol
// SPDX-License-Identifier: GPL-3.0-onlypragmasolidity 0.8.13;/**
* @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
*/interfaceISocket{
/**
* @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
*/structFees {
uint128 transmissionFees;
uint128 executionFee;
uint128 switchboardFees;
}
/**
* @title MessageDetails
* @dev This struct defines the details of a message to be executed in a Decapacitor contract.
*/structMessageDetails {
// A unique identifier for the message.bytes32 msgId;
// The fee to be paid for executing the message.uint256 executionFee;
// The maximum amount of gas that can be used to execute the message.uint256 minMsgGasLimit;
// The extra params which provides msg value and additional info needed for message execbytes32 executionParams;
// The payload data to be executed in the message.bytes payload;
}
/**
* @title ExecutionDetails
* @dev This struct defines the execution details
*/structExecutionDetails {
// packet idbytes32 packetId;
// proposal countuint256 proposalCount;
// gas limit needed to execute inbounduint256 executionGasLimit;
// proof data required by the Decapacitor contract to verify the message's authenticitybytes decapacitorProof;
// signature of executorbytes 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
*/eventMessageOutbound(uint32 localChainSlug,
address localPlug,
uint32 dstChainSlug,
address dstPlug,
bytes32 msgId,
uint256 minMsgGasLimit,
bytes32 executionParams,
bytes32 transmissionParams,
bytes payload,
Fees fees
);
/**
* @notice emits the status of message after inbound call
* @param msgId msg id which is executed
*/eventExecutionSuccess(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
*/eventPlugConnected(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
*/functionoutbound(uint32 remoteChainSlug_,
uint256 minMsgGasLimit_,
bytes32 executionParams_,
bytes32 transmissionParams_,
bytescalldata payload_
) externalpayablereturns (bytes32 msgId);
/**
* @notice executes a message
* @param executionDetails_ the packet details, proof and signature needed for message execution
* @param messageDetails_ the message details
*/functionexecute(
ISocket.ExecutionDetails calldata executionDetails_,
ISocket.MessageDetails calldata messageDetails_
) externalpayable;
/**
* @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
*/functionconnect(uint32 siblingChainSlug_,
address siblingPlug_,
address inboundSwitchboard_,
address outboundSwitchboard_
) external;
/**
* @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.
*/functiongetMinFees(uint256 minMsgGasLimit_,
uint256 payloadSize_,
bytes32 executionParams_,
bytes32 transmissionParams_,
uint32 remoteChainSlug_,
address plug_
) externalviewreturns (uint256 totalFees);
/**
* @notice returns chain slug
* @return chainSlug current chain slug
*/functionchainSlug() externalviewreturns (uint32 chainSlug);
/**
* @notice returns the config for given `plugAddress_` and `siblingChainSlug_`
* @param siblingChainSlug_ the sibling chain slug
* @param plugAddress_ address of plug present at current chain
*/functiongetPlugConfig(address plugAddress_,
uint32 siblingChainSlug_
)
externalviewreturns (address siblingPlug,
address inboundSwitchboard__,
address outboundSwitchboard__,
address capacitor__,
address decapacitor__
);
}
Contract Source Code
File 7 of 11: Ownable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)pragmasolidity ^0.8.0;import"../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableisContext{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
Contract Source Code
File 8 of 11: Ownable2Step.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)pragmasolidity ^0.8.0;import"./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/abstractcontractOwnable2StepisOwnable{
addressprivate _pendingOwner;
eventOwnershipTransferStarted(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/functionpendingOwner() publicviewvirtualreturns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualoverrideonlyOwner{
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtualoverride{
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/functionacceptOwnership() publicvirtual{
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}
Contract Source Code
File 9 of 11: RescueFundsLib.sol
// SPDX-License-Identifier: GPL-3.0-onlypragmasolidity 0.8.13;import"lib/solmate/src/utils/SafeTransferLib.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 10 of 11: 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");
}
}
Contract Source Code
File 11 of 11: Vault.sol
pragmasolidity 0.8.13;import"lib/solmate/src/utils/SafeTransferLib.sol";
import"lib/openzeppelin-contracts/contracts/access/Ownable2Step.sol";
import {Gauge} from"./Gauge.sol";
import {IConnector, IHub} from"./ConnectorPlug.sol";
import {RescueFundsLib} from"./RescueFundsLib.sol";
// @todo: separate our connecter plugscontractVaultisGauge, IHub, Ownable2Step{
usingSafeTransferLibforERC20;
ERC20 publicimmutable token__;
structUpdateLimitParams {
bool isLock;
address connector;
uint256 maxLimit;
uint256 ratePerSecond;
}
// connector => receiver => pendingUnlockmapping(address=>mapping(address=>uint256)) public pendingUnlocks;
// connector => amountmapping(address=>uint256) public connectorPendingUnlocks;
// connector => lockLimitParamsmapping(address=> LimitParams) _lockLimitParams;
// connector => unlockLimitParamsmapping(address=> LimitParams) _unlockLimitParams;
errorConnectorUnavailable();
errorZeroAmount();
eventLimitParamsUpdated(UpdateLimitParams[] updates);
eventTokensDeposited(address connector,
address depositor,
address receiver,
uint256 depositAmount
);
eventPendingTokensTransferred(address connector,
address receiver,
uint256 unlockedAmount,
uint256 pendingAmount
);
eventTokensPending(address connector,
address receiver,
uint256 pendingAmount,
uint256 totalPendingAmount
);
eventTokensUnlocked(address connector,
address receiver,
uint256 unlockedAmount
);
constructor(address token_) {
token__ = ERC20(token_);
}
functionupdateLimitParams(
UpdateLimitParams[] calldata updates_
) externalonlyOwner{
for (uint256 i; i < updates_.length; i++) {
if (updates_[i].isLock) {
_consumePartLimit(0, _lockLimitParams[updates_[i].connector]); // to keep current limit in sync
_lockLimitParams[updates_[i].connector].maxLimit = updates_[i]
.maxLimit;
_lockLimitParams[updates_[i].connector]
.ratePerSecond = updates_[i].ratePerSecond;
} else {
_consumePartLimit(0, _unlockLimitParams[updates_[i].connector]); // to keep current limit in sync
_unlockLimitParams[updates_[i].connector].maxLimit = updates_[i]
.maxLimit;
_unlockLimitParams[updates_[i].connector]
.ratePerSecond = updates_[i].ratePerSecond;
}
}
emit LimitParamsUpdated(updates_);
}
functiondepositToAppChain(address receiver_,
uint256 amount_,
uint256 msgGasLimit_,
address connector_
) externalpayable{
if (amount_ ==0) revert ZeroAmount();
if (_lockLimitParams[connector_].maxLimit ==0)
revert ConnectorUnavailable();
_consumeFullLimit(amount_, _lockLimitParams[connector_]); // reverts on limit hit
token__.safeTransferFrom(msg.sender, address(this), amount_);
IConnector(connector_).outbound{value: msg.value}(
msgGasLimit_,
abi.encode(receiver_, amount_)
);
emit TokensDeposited(connector_, msg.sender, receiver_, amount_);
}
functionunlockPendingFor(address receiver_, address connector_) external{
if (_unlockLimitParams[connector_].maxLimit ==0)
revert ConnectorUnavailable();
uint256 pendingUnlock = pendingUnlocks[connector_][receiver_];
(uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit(
pendingUnlock,
_unlockLimitParams[connector_]
);
pendingUnlocks[connector_][receiver_] = pendingAmount;
connectorPendingUnlocks[connector_] -= consumedAmount;
token__.safeTransfer(receiver_, consumedAmount);
emit PendingTokensTransferred(
connector_,
receiver_,
consumedAmount,
pendingAmount
);
}
// receive inbound assuming connector calledfunctionreceiveInbound(bytesmemory payload_) externaloverride{
if (_unlockLimitParams[msg.sender].maxLimit ==0)
revert ConnectorUnavailable();
(address receiver, uint256 unlockAmount) =abi.decode(
payload_,
(address, uint256)
);
(uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit(
unlockAmount,
_unlockLimitParams[msg.sender]
);
if (pendingAmount >0) {
// add instead of overwrite to handle case where already pending amount is left
pendingUnlocks[msg.sender][receiver] += pendingAmount;
connectorPendingUnlocks[msg.sender] += pendingAmount;
emit TokensPending(
msg.sender,
receiver,
pendingAmount,
pendingUnlocks[msg.sender][receiver]
);
}
token__.safeTransfer(receiver, consumedAmount);
emit TokensUnlocked(msg.sender, receiver, consumedAmount);
}
functiongetMinFees(address connector_,
uint256 msgGasLimit_
) externalviewreturns (uint256 totalFees) {
return IConnector(connector_).getMinFees(msgGasLimit_);
}
functiongetCurrentLockLimit(address connector_
) externalviewreturns (uint256) {
return _getCurrentLimit(_lockLimitParams[connector_]);
}
functiongetCurrentUnlockLimit(address connector_
) externalviewreturns (uint256) {
return _getCurrentLimit(_unlockLimitParams[connector_]);
}
functiongetLockLimitParams(address connector_
) externalviewreturns (LimitParams memory) {
return _lockLimitParams[connector_];
}
functiongetUnlockLimitParams(address connector_
) externalviewreturns (LimitParams memory) {
return _unlockLimitParams[connector_];
}
/**
* @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_
) externalonlyOwner{
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}