编译器
0.8.13+commit.abaa5c0e
文件 1 的 23:AccessControl.sol
pragma solidity 0.8.13;
import "./Ownable.sol";
abstract contract AccessControl is Ownable {
mapping(bytes32 => mapping(address => bool)) private _permits;
event RoleGranted(bytes32 indexed role, address indexed grantee);
event RoleRevoked(bytes32 indexed role, address indexed revokee);
error NoPermit(bytes32 role);
constructor(address owner_) Ownable(owner_) {}
modifier onlyRole(bytes32 role) {
if (!_permits[role][msg.sender]) revert NoPermit(role);
_;
}
function _checkRole(bytes32 role_, address address_) internal virtual {
if (!_hasRole(role_, address_)) revert NoPermit(role_);
}
function grantRole(
bytes32 role_,
address grantee_
) external virtual onlyOwner {
_grantRole(role_, grantee_);
}
function revokeRole(
bytes32 role_,
address revokee_
) external virtual onlyOwner {
_revokeRole(role_, revokee_);
}
function _grantRole(bytes32 role_, address grantee_) internal {
_permits[role_][grantee_] = true;
emit RoleGranted(role_, grantee_);
}
function _revokeRole(bytes32 role_, address revokee_) internal {
_permits[role_][revokee_] = false;
emit RoleRevoked(role_, revokee_);
}
function hasRole(
bytes32 role_,
address address_
) external view returns (bool) {
return _hasRole(role_, address_);
}
function _hasRole(
bytes32 role_,
address address_
) internal view returns (bool) {
return _permits[role_][address_];
}
}
文件 2 的 23:ConnectorPlug.sol
pragma solidity 0.8.13;
import "../common/Ownable.sol";
import {ISocket} from "../interfaces/ISocket.sol";
import {IPlug} from "../interfaces/IPlug.sol";
import {RescueFundsLib} from "../libraries/RescueFundsLib.sol";
interface IHub {
function receiveInbound(bytes memory payload_) external;
}
interface IConnector {
function outbound(
uint256 msgGasLimit_,
bytes memory payload_
) external payable;
function siblingChainSlug() external view returns (uint32);
function getMinFees(
uint256 msgGasLimit_
) external view returns (uint256 totalFees);
}
contract ConnectorPlug is IConnector, IPlug, Ownable {
IHub public immutable hub__;
ISocket public immutable socket__;
uint32 public immutable siblingChainSlug;
error NotHub();
error NotSocket();
event ConnectorPlugDisconnected();
constructor(
address hub_,
address socket_,
uint32 siblingChainSlug_
) Ownable(msg.sender) {
hub__ = IHub(hub_);
socket__ = ISocket(socket_);
siblingChainSlug = siblingChainSlug_;
}
function outbound(
uint256 msgGasLimit_,
bytes memory payload_
) external payable override {
if (msg.sender != address(hub__)) revert NotHub();
socket__.outbound{value: msg.value}(
siblingChainSlug,
msgGasLimit_,
bytes32(0),
bytes32(0),
payload_
);
}
function inbound(
uint32 ,
bytes calldata payload_
) external payable override {
if (msg.sender != address(socket__)) revert NotSocket();
hub__.receiveInbound(payload_);
}
function getMinFees(
uint256 msgGasLimit_
) external view override returns (uint256 totalFees) {
return
socket__.getMinFees(
msgGasLimit_,
64,
bytes32(0),
bytes32(0),
siblingChainSlug,
address(this)
);
}
function connect(
address siblingPlug_,
address switchboard_
) external onlyOwner {
socket__.connect(
siblingChainSlug,
siblingPlug_,
switchboard_,
switchboard_
);
}
function disconnect() external onlyOwner {
(
,
address inboundSwitchboard,
address outboundSwitchboard,
,
) = socket__.getPlugConfig(address(this), siblingChainSlug);
socket__.connect(
siblingChainSlug,
address(0),
inboundSwitchboard,
outboundSwitchboard
);
emit ConnectorPlugDisconnected();
}
function rescueFunds(
address token_,
address rescueTo_,
uint256 amount_
) external onlyOwner {
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}
文件 3 的 23:Controller.sol
pragma solidity 0.8.13;
import {IExchangeRate} from "./ExchangeRate.sol";
import {Ownable} from "../common/Ownable.sol";
import {Gauge} from "../common/Gauge.sol";
import {IConnector, IHub} from "./ConnectorPlug.sol";
import {IMintableERC20} from "./IMintableERC20.sol";
import {RescueFundsLib} from "../libraries/RescueFundsLib.sol";
contract Controller is IHub, Gauge, Ownable(msg.sender) {
IMintableERC20 public immutable token__;
IExchangeRate public exchangeRate__;
struct UpdateLimitParams {
bool isMint;
address connector;
uint256 maxLimit;
uint256 ratePerSecond;
}
mapping(uint256 => uint256) public poolLockedAmounts;
mapping(address => uint256) public connectorPoolIds;
mapping(address => LimitParams) _mintLimitParams;
mapping(address => LimitParams) _burnLimitParams;
mapping(address => mapping(address => uint256)) public pendingMints;
mapping(address => uint256) public connectorPendingMints;
uint256 public totalMinted;
error ConnectorUnavailable();
error InvalidPoolId();
error ZeroAmount();
event ExchangeRateUpdated(address exchangeRate);
event ConnectorPoolIdUpdated(address connector, uint256 poolId);
event LimitParamsUpdated(UpdateLimitParams[] updates);
event TokensWithdrawn(
address connector,
address withdrawer,
address receiver,
uint256 burnAmount
);
event PendingTokensMinted(
address connector,
address receiver,
uint256 mintAmount,
uint256 pendingAmount
);
event TokensPending(
address connecter,
address receiver,
uint256 pendingAmount,
uint256 totalPendingAmount
);
event TokensMinted(address connecter, address receiver, uint256 mintAmount);
constructor(address token_, address exchangeRate_) {
token__ = IMintableERC20(token_);
exchangeRate__ = IExchangeRate(exchangeRate_);
}
function updateExchangeRate(address exchangeRate_) external onlyOwner {
exchangeRate__ = IExchangeRate(exchangeRate_);
emit ExchangeRateUpdated(exchangeRate_);
}
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 updateLimitParams(
UpdateLimitParams[] calldata updates_
) external onlyOwner {
for (uint256 i; i < updates_.length; i++) {
if (updates_[i].isMint) {
_consumePartLimit(0, _mintLimitParams[updates_[i].connector]);
_mintLimitParams[updates_[i].connector].maxLimit = updates_[i]
.maxLimit;
_mintLimitParams[updates_[i].connector]
.ratePerSecond = updates_[i].ratePerSecond;
} else {
_consumePartLimit(0, _burnLimitParams[updates_[i].connector]);
_burnLimitParams[updates_[i].connector].maxLimit = updates_[i]
.maxLimit;
_burnLimitParams[updates_[i].connector]
.ratePerSecond = updates_[i].ratePerSecond;
}
}
emit LimitParamsUpdated(updates_);
}
function withdrawFromAppChain(
address receiver_,
uint256 burnAmount_,
uint256 msgGasLimit_,
address connector_
) external payable {
if (burnAmount_ == 0) revert ZeroAmount();
if (_burnLimitParams[connector_].maxLimit == 0)
revert ConnectorUnavailable();
_consumeFullLimit(burnAmount_, _burnLimitParams[connector_]);
totalMinted -= burnAmount_;
_burn(msg.sender, burnAmount_);
uint256 connectorPoolId = connectorPoolIds[connector_];
if (connectorPoolId == 0) revert InvalidPoolId();
uint256 unlockAmount = exchangeRate__.getUnlockAmount(
burnAmount_,
poolLockedAmounts[connectorPoolId]
);
poolLockedAmounts[connectorPoolId] -= unlockAmount;
IConnector(connector_).outbound{value: msg.value}(
msgGasLimit_,
abi.encode(receiver_, unlockAmount)
);
emit TokensWithdrawn(connector_, msg.sender, receiver_, burnAmount_);
}
function _burn(address user_, uint256 burnAmount_) internal virtual {
token__.burn(user_, burnAmount_);
}
function mintPendingFor(address receiver_, address connector_) external {
if (_mintLimitParams[connector_].maxLimit == 0)
revert ConnectorUnavailable();
uint256 pendingMint = pendingMints[connector_][receiver_];
(uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit(
pendingMint,
_mintLimitParams[connector_]
);
pendingMints[connector_][receiver_] = pendingAmount;
connectorPendingMints[connector_] -= consumedAmount;
totalMinted += consumedAmount;
token__.mint(receiver_, consumedAmount);
emit PendingTokensMinted(
connector_,
receiver_,
consumedAmount,
pendingAmount
);
}
function receiveInbound(bytes memory payload_) external override {
if (_mintLimitParams[msg.sender].maxLimit == 0)
revert ConnectorUnavailable();
(address receiver, uint256 lockAmount) = abi.decode(
payload_,
(address, uint256)
);
uint256 connectorPoolId = connectorPoolIds[msg.sender];
if (connectorPoolId == 0) revert InvalidPoolId();
poolLockedAmounts[connectorPoolId] += lockAmount;
uint256 mintAmount = exchangeRate__.getMintAmount(
lockAmount,
poolLockedAmounts[connectorPoolId]
);
(uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit(
mintAmount,
_mintLimitParams[msg.sender]
);
if (pendingAmount > 0) {
pendingMints[msg.sender][receiver] += pendingAmount;
connectorPendingMints[msg.sender] += pendingAmount;
emit TokensPending(
msg.sender,
receiver,
pendingAmount,
pendingMints[msg.sender][receiver]
);
}
totalMinted += consumedAmount;
token__.mint(receiver, consumedAmount);
emit TokensMinted(msg.sender, receiver, consumedAmount);
}
function getMinFees(
address connector_,
uint256 msgGasLimit_
) external view returns (uint256 totalFees) {
return IConnector(connector_).getMinFees(msgGasLimit_);
}
function getCurrentMintLimit(
address connector_
) external view returns (uint256) {
return _getCurrentLimit(_mintLimitParams[connector_]);
}
function getCurrentBurnLimit(
address connector_
) external view returns (uint256) {
return _getCurrentLimit(_burnLimitParams[connector_]);
}
function getMintLimitParams(
address connector_
) external view returns (LimitParams memory) {
return _mintLimitParams[connector_];
}
function getBurnLimitParams(
address connector_
) external view returns (LimitParams memory) {
return _burnLimitParams[connector_];
}
function rescueFunds(
address token_,
address rescueTo_,
uint256 amount_
) external onlyOwner {
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}
文件 4 的 23:ERC20.sol
pragma solidity >=0.8.0;
abstract contract ERC20 {
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender];
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
文件 5 的 23:ExcessivelySafeCall.sol
pragma solidity 0.8.13;
library ExcessivelySafeCall {
uint constant LOW_28_MASK =
0x00000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffff;
function excessivelySafeCall(
address _target,
uint _gas,
uint16 _maxCopy,
bytes memory _calldata
) internal returns (bool, bytes memory) {
uint _toCopy;
bool _success;
bytes memory _returnData = new bytes(_maxCopy);
assembly {
_success := call(
_gas,
_target,
0,
add(_calldata, 0x20),
mload(_calldata),
0,
0
)
_toCopy := returndatasize()
if gt(_toCopy, _maxCopy) {
_toCopy := _maxCopy
}
mstore(_returnData, _toCopy)
returndatacopy(add(_returnData, 0x20), 0, _toCopy)
}
return (_success, _returnData);
}
function excessivelySafeStaticCall(
address _target,
uint _gas,
uint16 _maxCopy,
bytes memory _calldata
) internal view returns (bool, bytes memory) {
uint _toCopy;
bool _success;
bytes memory _returnData = new bytes(_maxCopy);
assembly {
_success := staticcall(
_gas,
_target,
add(_calldata, 0x20),
mload(_calldata),
0,
0
)
_toCopy := returndatasize()
if gt(_toCopy, _maxCopy) {
_toCopy := _maxCopy
}
mstore(_returnData, _toCopy)
returndatacopy(add(_returnData, 0x20), 0, _toCopy)
}
return (_success, _returnData);
}
function swapSelector(
bytes4 _newSelector,
bytes memory _buf
) internal pure {
require(_buf.length >= 4);
uint _mask = LOW_28_MASK;
assembly {
let _word := mload(add(_buf, 0x20))
_word := and(_word, _mask)
_word := or(_newSelector, _word)
mstore(add(_buf, 0x20), _word)
}
}
}
文件 6 的 23:ExchangeRate.sol
pragma solidity 0.8.13;
import "../common/Ownable.sol";
import {RescueFundsLib} from "../libraries/RescueFundsLib.sol";
interface IExchangeRate {
function getMintAmount(
uint256 lockAmount,
uint256 totalLockedAmount
) external returns (uint256 mintAmount);
function getUnlockAmount(
uint256 burnAmount,
uint256 totalLockedAmount
) external returns (uint256 unlockAmount);
}
contract ExchangeRate is IExchangeRate, Ownable(msg.sender) {
function getMintAmount(
uint256 lockAmount,
uint256
) external pure returns (uint256 mintAmount) {
return lockAmount;
}
function getUnlockAmount(
uint256 burnAmount,
uint256
) external pure returns (uint256 unlockAmount) {
return burnAmount;
}
function rescueFunds(
address token_,
address rescueTo_,
uint256 amount_
) external onlyOwner {
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}
文件 7 的 23:Execute.sol
pragma solidity 0.8.13;
import "lib/solmate/src/utils/ReentrancyGuard.sol";
import "../libraries/ExcessivelySafeCall.sol";
contract Execute is ReentrancyGuard {
using ExcessivelySafeCall for address;
struct PendingExecutionDetails {
address receiver;
uint32 siblingChainSlug;
bytes payload;
bool isAmountPending;
}
uint16 private constant MAX_COPY_BYTES = 150;
mapping(bytes32 => PendingExecutionDetails) public pendingExecutions;
error InvalidExecutionRetry();
error PendingAmount();
error CannotExecuteOnBridgeContracts();
function retryPayloadExecution(bytes32 msgId_) external nonReentrant {
PendingExecutionDetails storage details = pendingExecutions[msgId_];
if (details.isAmountPending) revert PendingAmount();
if (details.receiver == address(0)) revert InvalidExecutionRetry();
bool success = _execute(details.receiver, details.payload);
if (success) _clearPayload(msgId_);
}
function _execute(
address target_,
bytes memory payload_
) internal returns (bool success) {
(success, ) = target_.excessivelySafeCall(
gasleft(),
MAX_COPY_BYTES,
payload_
);
}
function _cachePayload(
bytes32 msgId_,
uint32 siblingChainSlug_,
bool isAmountPending_,
address receiver_,
bytes memory payload_
) internal {
pendingExecutions[msgId_].receiver = receiver_;
pendingExecutions[msgId_].siblingChainSlug = siblingChainSlug_;
pendingExecutions[msgId_].payload = payload_;
pendingExecutions[msgId_].isAmountPending = isAmountPending_;
}
function _clearPayload(bytes32 msgId_) internal {
pendingExecutions[msgId_].receiver = address(0);
pendingExecutions[msgId_].siblingChainSlug = 0;
pendingExecutions[msgId_].payload = bytes("");
pendingExecutions[msgId_].isAmountPending = false;
}
}
文件 8 的 23:FiatTokenV2_1_Controller.sol
pragma solidity 0.8.13;
import "lib/solmate/src/utils/SafeTransferLib.sol";
import {Controller} from "../Controller.sol";
import {IMintableERC20} from "../IMintableERC20.sol";
import {IFiatTokenV2_1_Mintable} from "./IFiatTokenV2_1_Mintable.sol";
contract FiatTokenV2_1_Controller is Controller {
using SafeTransferLib for IMintableERC20;
constructor(
address token_,
address exchangeRate_
) Controller(token_, exchangeRate_) {}
function _burn(address user_, uint256 burnAmount_) internal override {
token__.safeTransferFrom(user_, address(this), burnAmount_);
IFiatTokenV2_1_Mintable(address(token__)).burn(burnAmount_);
}
}
文件 9 的 23:Gauge.sol
pragma solidity 0.8.13;
abstract contract Gauge {
struct LimitParams {
uint256 lastUpdateTimestamp;
uint256 ratePerSecond;
uint256 maxLimit;
uint256 lastUpdateLimit;
}
error AmountOutsideLimit();
function _getCurrentLimit(
LimitParams storage _params
) internal view returns (uint256 _limit) {
uint256 timeElapsed = block.timestamp - _params.lastUpdateTimestamp;
uint256 limitIncrease = timeElapsed * _params.ratePerSecond;
if (limitIncrease + _params.lastUpdateLimit > _params.maxLimit) {
_limit = _params.maxLimit;
} else {
_limit = limitIncrease + _params.lastUpdateLimit;
}
}
function _consumePartLimit(
uint256 amount_,
LimitParams storage _params
) internal returns (uint256 consumedAmount, uint256 pendingAmount) {
uint256 currentLimit = _getCurrentLimit(_params);
_params.lastUpdateTimestamp = block.timestamp;
if (currentLimit >= amount_) {
_params.lastUpdateLimit = currentLimit - amount_;
consumedAmount = amount_;
pendingAmount = 0;
} else {
_params.lastUpdateLimit = 0;
consumedAmount = currentLimit;
pendingAmount = amount_ - currentLimit;
}
}
function _consumeFullLimit(
uint256 amount_,
LimitParams storage _params
) internal {
uint256 currentLimit = _getCurrentLimit(_params);
if (currentLimit >= amount_) {
_params.lastUpdateTimestamp = block.timestamp;
_params.lastUpdateLimit = currentLimit - amount_;
} else {
revert AmountOutsideLimit();
}
}
}
文件 10 的 23:IFiatTokenV2_1_Mintable.sol
pragma solidity 0.8.13;
import "lib/solmate/src/tokens/ERC20.sol";
abstract contract IFiatTokenV2_1_Mintable is ERC20 {
function mint(address receiver_, uint256 amount_) external virtual;
function burn(uint256 _amount) external virtual;
}
文件 11 的 23:IMessageBridge.sol
pragma solidity 0.8.13;
interface IMessageBridge {
function outbound(
uint32 siblingChainSlug_,
uint256 msgGasLimit_,
bytes memory payload_,
bytes memory options_
) external payable returns (bytes32 messageId_);
function getMessageId(
uint32 siblingChainSlug_
) external view returns (bytes32);
}
文件 12 的 23:IMintableERC20.sol
pragma solidity 0.8.13;
import "lib/solmate/src/tokens/ERC20.sol";
abstract contract IMintableERC20 is ERC20 {
function mint(address receiver_, uint256 amount_) external virtual;
function burn(address burner_, uint256 amount_) external virtual;
}
文件 13 的 23:IPlug.sol
pragma solidity 0.8.13;
interface IPlug {
function inbound(
uint32 srcChainSlug_,
bytes calldata payload_
) external payable;
}
文件 14 的 23:ISocket.sol
pragma solidity 0.8.13;
interface ISocket {
struct Fees {
uint128 transmissionFees;
uint128 executionFee;
uint128 switchboardFees;
}
struct MessageDetails {
bytes32 msgId;
uint256 executionFee;
uint256 minMsgGasLimit;
bytes32 executionParams;
bytes payload;
}
struct ExecutionDetails {
bytes32 packetId;
uint256 proposalCount;
uint256 executionGasLimit;
bytes decapacitorProof;
bytes signature;
}
event MessageOutbound(
uint32 localChainSlug,
address localPlug,
uint32 dstChainSlug,
address dstPlug,
bytes32 msgId,
uint256 minMsgGasLimit,
bytes32 executionParams,
bytes32 transmissionParams,
bytes payload,
Fees fees
);
event ExecutionSuccess(bytes32 msgId);
event PlugConnected(
address plug,
uint32 siblingChainSlug,
address siblingPlug,
address inboundSwitchboard,
address outboundSwitchboard,
address capacitor,
address decapacitor
);
function outbound(
uint32 remoteChainSlug_,
uint256 minMsgGasLimit_,
bytes32 executionParams_,
bytes32 transmissionParams_,
bytes memory payload_
) external payable returns (bytes32 msgId);
function execute(
ISocket.ExecutionDetails calldata executionDetails_,
ISocket.MessageDetails calldata messageDetails_
) external payable;
function connect(
uint32 siblingChainSlug_,
address siblingPlug_,
address inboundSwitchboard_,
address outboundSwitchboard_
) external;
function getMinFees(
uint256 minMsgGasLimit_,
uint256 payloadSize_,
bytes32 executionParams_,
bytes32 transmissionParams_,
uint32 remoteChainSlug_,
address plug_
) external view returns (uint256 totalFees);
function chainSlug() external view returns (uint32 chainSlug);
function globalMessageCount() external view returns (uint64);
function getPlugConfig(
address plugAddress_,
uint32 siblingChainSlug_
)
external
view
returns (
address siblingPlug,
address inboundSwitchboard__,
address outboundSwitchboard__,
address capacitor__,
address decapacitor__
);
}
文件 15 的 23:ISuperTokenOrVault.sol
pragma solidity 0.8.13;
interface ISuperTokenOrVault {
function inbound(
uint32 siblingChainSlug_,
bytes memory payload_
) external payable;
}
文件 16 的 23:Ownable.sol
pragma solidity 0.8.13;
abstract contract Ownable {
address private _owner;
address private _nominee;
event OwnerNominated(address indexed nominee);
event OwnerClaimed(address indexed claimer);
error OnlyOwner();
error OnlyNominee();
constructor(address owner_) {
_claimOwner(owner_);
}
modifier onlyOwner() {
if (msg.sender != _owner) revert OnlyOwner();
_;
}
function owner() external view returns (address) {
return _owner;
}
function nominee() external view returns (address) {
return _nominee;
}
function nominateOwner(address nominee_) external {
if (msg.sender != _owner) revert OnlyOwner();
_nominee = nominee_;
emit OwnerNominated(_nominee);
}
function claimOwner() external {
if (msg.sender != _nominee) revert OnlyNominee();
_claimOwner(msg.sender);
}
function _claimOwner(address claimer_) internal {
_owner = claimer_;
_nominee = address(0);
emit OwnerClaimed(claimer_);
}
}
文件 17 的 23:ReentrancyGuard.sol
pragma solidity >=0.8.0;
abstract contract ReentrancyGuard {
uint256 private locked = 1;
modifier nonReentrant() virtual {
require(locked == 1, "REENTRANCY");
locked = 2;
_;
locked = 1;
}
}
文件 18 的 23:RescueFundsLib.sol
pragma solidity 0.8.13;
import "lib/solmate/src/utils/SafeTransferLib.sol";
error ZeroAddress();
library RescueFundsLib {
address public constant ETH_ADDRESS =
address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
error InvalidTokenAddress();
function rescueFunds(
address token_,
address rescueTo_,
uint256 amount_
) internal {
if (rescueTo_ == address(0)) revert ZeroAddress();
if (token_ == ETH_ADDRESS) {
SafeTransferLib.safeTransferETH(rescueTo_, amount_);
} else {
if (token_.code.length == 0) revert InvalidTokenAddress();
SafeTransferLib.safeTransfer(ERC20(token_), rescueTo_, amount_);
}
}
}
文件 19 的 23:SafeTransferLib.sol
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
library SafeTransferLib {
function safeTransferETH(address to, uint256 amount) internal {
bool success;
assembly {
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 68), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
assembly {
let freeMemoryPointer := mload(0x40)
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff))
mstore(add(freeMemoryPointer, 36), amount)
success := and(
or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
文件 20 的 23:SocketPlug.sol
pragma solidity 0.8.13;
import {ISocket} from "../../interfaces/ISocket.sol";
import {IPlug} from "../../interfaces/IPlug.sol";
import {AccessControl} from "../../common/AccessControl.sol";
import {RescueFundsLib} from "../../libraries/RescueFundsLib.sol";
import {IMessageBridge} from "./../IMessageBridge.sol";
import {ISuperTokenOrVault} from "./../ISuperTokenOrVault.sol";
contract SocketPlug is IPlug, AccessControl, IMessageBridge {
bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE");
ISocket public immutable socket__;
ISuperTokenOrVault public tokenOrVault__;
uint32 public immutable chainSlug;
mapping(uint32 => address) public siblingPlugs;
event SocketPlugDisconnected(uint32 siblingChainSlug);
event SuperTokenOrVaultSet();
error NotSuperTokenOrVault();
error NotSocket();
error TokenOrVaultAlreadySet();
constructor(
address socket_,
address owner_,
uint32 chainSlug_
) AccessControl(owner_) {
socket__ = ISocket(socket_);
chainSlug = chainSlug_;
}
function outbound(
uint32 siblingChainSlug_,
uint256 msgGasLimit_,
bytes memory payload_,
bytes memory
) external payable returns (bytes32 messageId_) {
if (msg.sender != address(tokenOrVault__))
revert NotSuperTokenOrVault();
return
socket__.outbound{value: msg.value}(
siblingChainSlug_,
msgGasLimit_,
bytes32(0),
bytes32(0),
payload_
);
}
function inbound(
uint32 siblingChainSlug_,
bytes memory payload_
) external payable override {
if (msg.sender != address(socket__)) revert NotSocket();
tokenOrVault__.inbound(siblingChainSlug_, payload_);
}
function getMinFees(
uint32 siblingChainSlug_,
uint256 msgGasLimit_
) external view returns (uint256 totalFees) {
return
socket__.getMinFees(
msgGasLimit_,
96,
bytes32(0),
bytes32(0),
siblingChainSlug_,
address(this)
);
}
function setSuperTokenOrVault(address tokenOrVault_) external onlyOwner {
if (address(tokenOrVault__) != address(0))
revert TokenOrVaultAlreadySet();
tokenOrVault__ = ISuperTokenOrVault(tokenOrVault_);
emit SuperTokenOrVaultSet();
}
function connect(
uint32 siblingChainSlug_,
address siblingPlug_,
address inboundSwitchboard_,
address outboundSwitchboard_
) external onlyOwner {
siblingPlugs[siblingChainSlug_] = siblingPlug_;
socket__.connect(
siblingChainSlug_,
siblingPlug_,
inboundSwitchboard_,
outboundSwitchboard_
);
}
function disconnect(uint32 siblingChainSlug_) external onlyOwner {
(
,
address inboundSwitchboard,
address outboundSwitchboard,
,
) = socket__.getPlugConfig(address(this), siblingChainSlug_);
socket__.connect(
siblingChainSlug_,
address(0),
inboundSwitchboard,
outboundSwitchboard
);
emit SocketPlugDisconnected(siblingChainSlug_);
}
function getMessageId(
uint32 siblingChainSlug_
) public view returns (bytes32) {
return
bytes32(
(uint256(chainSlug) << 224) |
(uint256(uint160(siblingPlugs[siblingChainSlug_])) << 64) |
(ISocket(socket__).globalMessageCount())
);
}
function rescueFunds(
address token_,
address rescueTo_,
uint256 amount_
) external onlyRole(RESCUE_ROLE) {
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}
文件 21 的 23:SuperToken.sol
pragma solidity 0.8.13;
import "lib/solmate/src/tokens/ERC20.sol";
import {AccessControl} from "../common/AccessControl.sol";
import {Gauge} from "../common/Gauge.sol";
import {RescueFundsLib} from "../libraries/RescueFundsLib.sol";
import "./Execute.sol";
import "./IMessageBridge.sol";
import "./ISuperTokenOrVault.sol";
contract SuperToken is
ERC20,
Gauge,
ISuperTokenOrVault,
AccessControl,
Execute
{
struct UpdateLimitParams {
bool isMint;
uint32 siblingChainSlug;
uint256 maxLimit;
uint256 ratePerSecond;
}
bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE");
bytes32 constant LIMIT_UPDATER_ROLE = keccak256("LIMIT_UPDATER_ROLE");
IMessageBridge public bridge__;
mapping(uint32 => LimitParams) _receivingLimitParams;
mapping(uint32 => LimitParams) _sendingLimitParams;
mapping(uint32 => mapping(address => mapping(bytes32 => uint256)))
public pendingMints;
mapping(uint32 => uint256) public siblingPendingMints;
error SiblingNotSupported();
error MessageIdMisMatched();
error ZeroAmount();
error NotMessageBridge();
event LimitParamsUpdated(UpdateLimitParams[] updates);
event MessageBridgeUpdated(address newBridge);
event BridgeTokens(
uint32 siblingChainSlug,
address withdrawer,
address receiver,
uint256 bridgedAmount,
bytes32 identifier
);
event PendingTokensBridged(
uint32 siblingChainSlug,
address receiver,
uint256 mintAmount,
uint256 pendingAmount,
bytes32 identifier
);
event TokensPending(
uint32 siblingChainSlug,
address receiver,
uint256 pendingAmount,
uint256 totalPendingAmount,
bytes32 identifier
);
event TokensBridged(
uint32 siblingChainSlug,
address receiver,
uint256 mintAmount,
uint256 totalAmount,
bytes32 identifier
);
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_,
address initialSupplyHolder_,
address owner_,
uint256 initialSupply_,
address bridge_
) ERC20(name_, symbol_, decimals_) AccessControl(owner_) {
_mint(initialSupplyHolder_, initialSupply_);
bridge__ = IMessageBridge(bridge_);
}
function updateMessageBridge(address bridge_) external onlyOwner {
bridge__ = IMessageBridge(bridge_);
emit MessageBridgeUpdated(bridge_);
}
function updateLimitParams(
UpdateLimitParams[] calldata updates_
) external onlyRole(LIMIT_UPDATER_ROLE) {
for (uint256 i; i < updates_.length; i++) {
if (updates_[i].isMint) {
_consumePartLimit(
0,
_receivingLimitParams[updates_[i].siblingChainSlug]
);
_receivingLimitParams[updates_[i].siblingChainSlug]
.maxLimit = updates_[i].maxLimit;
_receivingLimitParams[updates_[i].siblingChainSlug]
.ratePerSecond = updates_[i].ratePerSecond;
} else {
_consumePartLimit(
0,
_sendingLimitParams[updates_[i].siblingChainSlug]
);
_sendingLimitParams[updates_[i].siblingChainSlug]
.maxLimit = updates_[i].maxLimit;
_sendingLimitParams[updates_[i].siblingChainSlug]
.ratePerSecond = updates_[i].ratePerSecond;
}
}
emit LimitParamsUpdated(updates_);
}
function bridge(
address receiver_,
uint32 siblingChainSlug_,
uint256 sendingAmount_,
uint256 msgGasLimit_,
bytes calldata payload_,
bytes calldata options_
) external payable {
if (_sendingLimitParams[siblingChainSlug_].maxLimit == 0)
revert SiblingNotSupported();
if (sendingAmount_ == 0) revert ZeroAmount();
_consumeFullLimit(
sendingAmount_,
_sendingLimitParams[siblingChainSlug_]
);
_burn(msg.sender, sendingAmount_);
bytes32 messageId = bridge__.getMessageId(siblingChainSlug_);
bytes32 returnedMessageId = bridge__.outbound{value: msg.value}(
siblingChainSlug_,
msgGasLimit_,
abi.encode(receiver_, sendingAmount_, messageId, payload_),
options_
);
if (returnedMessageId != messageId) revert MessageIdMisMatched();
emit BridgeTokens(
siblingChainSlug_,
msg.sender,
receiver_,
sendingAmount_,
messageId
);
}
function mintPendingFor(
address receiver_,
uint32 siblingChainSlug_,
bytes32 identifier_
) external nonReentrant {
if (_receivingLimitParams[siblingChainSlug_].maxLimit == 0)
revert SiblingNotSupported();
uint256 pendingMint = pendingMints[siblingChainSlug_][receiver_][
identifier_
];
(uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit(
pendingMint,
_receivingLimitParams[siblingChainSlug_]
);
pendingMints[siblingChainSlug_][receiver_][identifier_] = pendingAmount;
siblingPendingMints[siblingChainSlug_] -= consumedAmount;
_mint(receiver_, consumedAmount);
if (
pendingAmount == 0 &&
pendingExecutions[identifier_].receiver != address(0)
) {
pendingExecutions[identifier_].isAmountPending = false;
bool success = _execute(
receiver_,
pendingExecutions[identifier_].payload
);
if (success) _clearPayload(identifier_);
}
emit PendingTokensBridged(
siblingChainSlug_,
receiver_,
consumedAmount,
pendingAmount,
identifier_
);
}
function inbound(
uint32 siblingChainSlug_,
bytes memory payload_
) external payable override nonReentrant {
if (msg.sender != address(bridge__)) revert NotMessageBridge();
if (_receivingLimitParams[siblingChainSlug_].maxLimit == 0)
revert SiblingNotSupported();
(
address receiver,
uint256 mintAmount,
bytes32 identifier,
bytes memory execPayload
) = abi.decode(payload_, (address, uint256, bytes32, bytes));
(uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit(
mintAmount,
_receivingLimitParams[siblingChainSlug_]
);
if (receiver == address(this) || receiver == address(bridge__))
revert CannotExecuteOnBridgeContracts();
_mint(receiver, consumedAmount);
if (pendingAmount > 0) {
pendingMints[siblingChainSlug_][receiver][
identifier
] = pendingAmount;
siblingPendingMints[siblingChainSlug_] += pendingAmount;
if (execPayload.length > 0)
_cachePayload(
identifier,
siblingChainSlug_,
true,
receiver,
execPayload
);
emit TokensPending(
siblingChainSlug_,
receiver,
pendingAmount,
pendingMints[siblingChainSlug_][receiver][identifier],
identifier
);
} else if (execPayload.length > 0) {
bool success = _execute(receiver, execPayload);
if (!success)
_cachePayload(
identifier,
siblingChainSlug_,
false,
receiver,
execPayload
);
}
emit TokensBridged(
siblingChainSlug_,
receiver,
consumedAmount,
mintAmount,
identifier
);
}
function getCurrentReceivingLimit(
uint32 siblingChainSlug_
) external view returns (uint256) {
return _getCurrentLimit(_receivingLimitParams[siblingChainSlug_]);
}
function getCurrentSendingLimit(
uint32 siblingChainSlug_
) external view returns (uint256) {
return _getCurrentLimit(_sendingLimitParams[siblingChainSlug_]);
}
function getReceivingLimitParams(
uint32 siblingChainSlug_
) external view returns (LimitParams memory) {
return _receivingLimitParams[siblingChainSlug_];
}
function getSendingLimitParams(
uint32 siblingChainSlug_
) external view returns (LimitParams memory) {
return _sendingLimitParams[siblingChainSlug_];
}
function rescueFunds(
address token_,
address rescueTo_,
uint256 amount_
) external onlyRole(RESCUE_ROLE) {
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}
文件 22 的 23:SuperTokenVault.sol
pragma solidity 0.8.13;
import "lib/solmate/src/utils/SafeTransferLib.sol";
import {AccessControl} from "../common/AccessControl.sol";
import {Gauge} from "../common/Gauge.sol";
import {RescueFundsLib} from "../libraries/RescueFundsLib.sol";
import "./Execute.sol";
import {ISuperTokenOrVault} from "./ISuperTokenOrVault.sol";
import {IMessageBridge} from "./IMessageBridge.sol";
contract SuperTokenVault is Gauge, ISuperTokenOrVault, AccessControl, Execute {
using SafeTransferLib for ERC20;
struct UpdateLimitParams {
bool isLock;
uint32 siblingChainSlug;
uint256 maxLimit;
uint256 ratePerSecond;
}
bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE");
bytes32 constant LIMIT_UPDATER_ROLE = keccak256("LIMIT_UPDATER_ROLE");
ERC20 public immutable token__;
IMessageBridge public bridge__;
mapping(uint32 => mapping(address => mapping(bytes32 => uint256)))
public pendingUnlocks;
mapping(uint32 => uint256) public siblingPendingUnlocks;
mapping(uint32 => LimitParams) _lockLimitParams;
mapping(uint32 => LimitParams) _unlockLimitParams;
error SiblingChainSlugUnavailable();
error ZeroAmount();
error NotMessageBridge();
event MessageBridgeUpdated(address bridge);
event LimitParamsUpdated(UpdateLimitParams[] updates);
event TokensDeposited(
uint32 siblingChainSlug,
address depositor,
address receiver,
uint256 depositAmount
);
event PendingTokensTransferred(
uint32 siblingChainSlug,
address receiver,
uint256 unlockedAmount,
uint256 pendingAmount
);
event TokensPending(
uint32 siblingChainSlug,
address receiver,
uint256 pendingAmount,
uint256 totalPendingAmount
);
event TokensUnlocked(
uint32 siblingChainSlug,
address receiver,
uint256 unlockedAmount
);
constructor(
address token_,
address owner_,
address bridge_
) AccessControl(owner_) {
token__ = ERC20(token_);
bridge__ = IMessageBridge(bridge_);
}
function updateMessageBridge(address bridge_) external onlyOwner {
bridge__ = IMessageBridge(bridge_);
emit MessageBridgeUpdated(bridge_);
}
function updateLimitParams(
UpdateLimitParams[] calldata updates_
) external onlyRole(LIMIT_UPDATER_ROLE) {
for (uint256 i; i < updates_.length; i++) {
if (updates_[i].isLock) {
_consumePartLimit(
0,
_lockLimitParams[updates_[i].siblingChainSlug]
);
_lockLimitParams[updates_[i].siblingChainSlug]
.maxLimit = updates_[i].maxLimit;
_lockLimitParams[updates_[i].siblingChainSlug]
.ratePerSecond = updates_[i].ratePerSecond;
} else {
_consumePartLimit(
0,
_unlockLimitParams[updates_[i].siblingChainSlug]
);
_unlockLimitParams[updates_[i].siblingChainSlug]
.maxLimit = updates_[i].maxLimit;
_unlockLimitParams[updates_[i].siblingChainSlug]
.ratePerSecond = updates_[i].ratePerSecond;
}
}
emit LimitParamsUpdated(updates_);
}
function bridge(
address receiver_,
uint32 siblingChainSlug_,
uint256 amount_,
uint256 msgGasLimit_,
bytes calldata payload_,
bytes calldata options_
) external payable {
if (amount_ == 0) revert ZeroAmount();
if (_lockLimitParams[siblingChainSlug_].maxLimit == 0)
revert SiblingChainSlugUnavailable();
_consumeFullLimit(amount_, _lockLimitParams[siblingChainSlug_]);
token__.safeTransferFrom(msg.sender, address(this), amount_);
bytes32 messageId = bridge__.getMessageId(siblingChainSlug_);
bridge__.outbound{value: msg.value}(
siblingChainSlug_,
msgGasLimit_,
abi.encode(receiver_, amount_, messageId, payload_),
options_
);
emit TokensDeposited(siblingChainSlug_, msg.sender, receiver_, amount_);
}
function unlockPendingFor(
address receiver_,
uint32 siblingChainSlug_,
bytes32 identifier_
) external nonReentrant {
if (_unlockLimitParams[siblingChainSlug_].maxLimit == 0)
revert SiblingChainSlugUnavailable();
uint256 pendingUnlock = pendingUnlocks[siblingChainSlug_][receiver_][
identifier_
];
(uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit(
pendingUnlock,
_unlockLimitParams[siblingChainSlug_]
);
pendingUnlocks[siblingChainSlug_][receiver_][
identifier_
] = pendingAmount;
siblingPendingUnlocks[siblingChainSlug_] -= consumedAmount;
token__.safeTransfer(receiver_, consumedAmount);
if (
pendingAmount == 0 &&
pendingExecutions[identifier_].receiver != address(0)
) {
pendingExecutions[identifier_].isAmountPending = false;
bool success = _execute(
receiver_,
pendingExecutions[identifier_].payload
);
if (success) _clearPayload(identifier_);
}
emit PendingTokensTransferred(
siblingChainSlug_,
receiver_,
consumedAmount,
pendingAmount
);
}
function inbound(
uint32 siblingChainSlug_,
bytes memory payload_
) external payable override nonReentrant {
if (msg.sender != address(bridge__)) revert NotMessageBridge();
if (_unlockLimitParams[siblingChainSlug_].maxLimit == 0)
revert SiblingChainSlugUnavailable();
(
address receiver,
uint256 unlockAmount,
bytes32 identifier,
bytes memory execPayload
) = abi.decode(payload_, (address, uint256, bytes32, bytes));
if (receiver == address(this) || receiver == address(bridge__))
revert CannotExecuteOnBridgeContracts();
(uint256 consumedAmount, uint256 pendingAmount) = _consumePartLimit(
unlockAmount,
_unlockLimitParams[siblingChainSlug_]
);
token__.safeTransfer(receiver, consumedAmount);
if (pendingAmount > 0) {
pendingUnlocks[siblingChainSlug_][receiver][
identifier
] += pendingAmount;
siblingPendingUnlocks[siblingChainSlug_] += pendingAmount;
if (execPayload.length > 0)
_cachePayload(
identifier,
siblingChainSlug_,
true,
receiver,
execPayload
);
emit TokensPending(
siblingChainSlug_,
receiver,
pendingAmount,
pendingUnlocks[siblingChainSlug_][receiver][identifier]
);
} else if (execPayload.length > 0) {
bool success = _execute(receiver, execPayload);
if (!success)
_cachePayload(
identifier,
siblingChainSlug_,
false,
receiver,
execPayload
);
}
emit TokensUnlocked(siblingChainSlug_, receiver, consumedAmount);
}
function getCurrentLockLimit(
uint32 siblingChainSlug_
) external view returns (uint256) {
return _getCurrentLimit(_lockLimitParams[siblingChainSlug_]);
}
function getCurrentUnlockLimit(
uint32 siblingChainSlug_
) external view returns (uint256) {
return _getCurrentLimit(_unlockLimitParams[siblingChainSlug_]);
}
function getLockLimitParams(
uint32 siblingChainSlug_
) external view returns (LimitParams memory) {
return _lockLimitParams[siblingChainSlug_];
}
function getUnlockLimitParams(
uint32 siblingChainSlug_
) external view returns (LimitParams memory) {
return _unlockLimitParams[siblingChainSlug_];
}
function rescueFunds(
address token_,
address rescueTo_,
uint256 amount_
) external onlyRole(RESCUE_ROLE) {
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}
文件 23 的 23:Vault.sol
pragma solidity 0.8.13;
import "lib/solmate/src/utils/SafeTransferLib.sol";
import "../common/Ownable.sol";
import {Gauge} from "../common/Gauge.sol";
import {IConnector, IHub} from "./ConnectorPlug.sol";
import {RescueFundsLib} from "../libraries/RescueFundsLib.sol";
contract Vault is Gauge, IHub, Ownable(msg.sender) {
using SafeTransferLib for ERC20;
ERC20 public immutable token__;
struct UpdateLimitParams {
bool isLock;
address connector;
uint256 maxLimit;
uint256 ratePerSecond;
}
mapping(address => mapping(address => uint256)) public pendingUnlocks;
mapping(address => uint256) public connectorPendingUnlocks;
mapping(address => LimitParams) _lockLimitParams;
mapping(address => LimitParams) _unlockLimitParams;
error ConnectorUnavailable();
error ZeroAmount();
event LimitParamsUpdated(UpdateLimitParams[] updates);
event TokensDeposited(
address connector,
address depositor,
address receiver,
uint256 depositAmount
);
event PendingTokensTransferred(
address connector,
address receiver,
uint256 unlockedAmount,
uint256 pendingAmount
);
event TokensPending(
address connector,
address receiver,
uint256 pendingAmount,
uint256 totalPendingAmount
);
event TokensUnlocked(
address connector,
address receiver,
uint256 unlockedAmount
);
constructor(address token_) {
token__ = ERC20(token_);
}
function updateLimitParams(
UpdateLimitParams[] calldata updates_
) external onlyOwner {
for (uint256 i; i < updates_.length; i++) {
if (updates_[i].isLock) {
_consumePartLimit(0, _lockLimitParams[updates_[i].connector]);
_lockLimitParams[updates_[i].connector].maxLimit = updates_[i]
.maxLimit;
_lockLimitParams[updates_[i].connector]
.ratePerSecond = updates_[i].ratePerSecond;
} else {
_consumePartLimit(0, _unlockLimitParams[updates_[i].connector]);
_unlockLimitParams[updates_[i].connector].maxLimit = updates_[i]
.maxLimit;
_unlockLimitParams[updates_[i].connector]
.ratePerSecond = updates_[i].ratePerSecond;
}
}
emit LimitParamsUpdated(updates_);
}
function depositToAppChain(
address receiver_,
uint256 amount_,
uint256 msgGasLimit_,
address connector_
) external payable {
if (amount_ == 0) revert ZeroAmount();
if (_lockLimitParams[connector_].maxLimit == 0)
revert ConnectorUnavailable();
_consumeFullLimit(amount_, _lockLimitParams[connector_]);
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_);
}
function unlockPendingFor(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
);
}
function receiveInbound(bytes memory payload_) external override {
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) {
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);
}
function getMinFees(
address connector_,
uint256 msgGasLimit_
) external view returns (uint256 totalFees) {
return IConnector(connector_).getMinFees(msgGasLimit_);
}
function getCurrentLockLimit(
address connector_
) external view returns (uint256) {
return _getCurrentLimit(_lockLimitParams[connector_]);
}
function getCurrentUnlockLimit(
address connector_
) external view returns (uint256) {
return _getCurrentLimit(_unlockLimitParams[connector_]);
}
function getLockLimitParams(
address connector_
) external view returns (LimitParams memory) {
return _lockLimitParams[connector_];
}
function getUnlockLimitParams(
address connector_
) external view returns (LimitParams memory) {
return _unlockLimitParams[connector_];
}
function rescueFunds(
address token_,
address rescueTo_,
uint256 amount_
) external onlyOwner {
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}
{
"compilationTarget": {
"contracts/superbridge/Vault.sol": "Vault"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 999999
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"token_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AmountOutsideLimit","type":"error"},{"inputs":[],"name":"ConnectorUnavailable","type":"error"},{"inputs":[],"name":"InvalidTokenAddress","type":"error"},{"inputs":[],"name":"OnlyNominee","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"components":[{"internalType":"bool","name":"isLock","type":"bool"},{"internalType":"address","name":"connector","type":"address"},{"internalType":"uint256","name":"maxLimit","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"}],"indexed":false,"internalType":"struct Vault.UpdateLimitParams[]","name":"updates","type":"tuple[]"}],"name":"LimitParamsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimer","type":"address"}],"name":"OwnerClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nominee","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"connector","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"unlockedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"pendingAmount","type":"uint256"}],"name":"PendingTokensTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"connector","type":"address"},{"indexed":false,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"depositAmount","type":"uint256"}],"name":"TokensDeposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"connector","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"pendingAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalPendingAmount","type":"uint256"}],"name":"TokensPending","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"connector","type":"address"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"unlockedAmount","type":"uint256"}],"name":"TokensUnlocked","type":"event"},{"inputs":[],"name":"claimOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"connectorPendingUnlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"},{"internalType":"uint256","name":"msgGasLimit_","type":"uint256"},{"internalType":"address","name":"connector_","type":"address"}],"name":"depositToAppChain","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"connector_","type":"address"}],"name":"getCurrentLockLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"connector_","type":"address"}],"name":"getCurrentUnlockLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"connector_","type":"address"}],"name":"getLockLimitParams","outputs":[{"components":[{"internalType":"uint256","name":"lastUpdateTimestamp","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"},{"internalType":"uint256","name":"maxLimit","type":"uint256"},{"internalType":"uint256","name":"lastUpdateLimit","type":"uint256"}],"internalType":"struct Gauge.LimitParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"connector_","type":"address"},{"internalType":"uint256","name":"msgGasLimit_","type":"uint256"}],"name":"getMinFees","outputs":[{"internalType":"uint256","name":"totalFees","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"connector_","type":"address"}],"name":"getUnlockLimitParams","outputs":[{"components":[{"internalType":"uint256","name":"lastUpdateTimestamp","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"},{"internalType":"uint256","name":"maxLimit","type":"uint256"},{"internalType":"uint256","name":"lastUpdateLimit","type":"uint256"}],"internalType":"struct Gauge.LimitParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nominee_","type":"address"}],"name":"nominateOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominee","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"pendingUnlocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"payload_","type":"bytes"}],"name":"receiveInbound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token_","type":"address"},{"internalType":"address","name":"rescueTo_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"rescueFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"token__","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"address","name":"connector_","type":"address"}],"name":"unlockPendingFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"isLock","type":"bool"},{"internalType":"address","name":"connector","type":"address"},{"internalType":"uint256","name":"maxLimit","type":"uint256"},{"internalType":"uint256","name":"ratePerSecond","type":"uint256"}],"internalType":"struct Vault.UpdateLimitParams[]","name":"updates_","type":"tuple[]"}],"name":"updateLimitParams","outputs":[],"stateMutability":"nonpayable","type":"function"}]