编译器
0.8.13+commit.abaa5c0e
文件 1 的 44: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 的 44:Base.sol
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__;
mapping(bytes32 => bytes) public identifierCache;
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);
}
function updateHook(
address hook_,
bool approve_
) external virtual onlyOwner {
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]);
}
}
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_)
);
}
}
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
);
}
function _beforeMint(
uint32,
TransferInfo memory transferInfo_
)
internal
returns (bytes memory postHookData, TransferInfo memory transferInfo)
{
if (!validConnectors[msg.sender]) revert InvalidConnector();
if (
transferInfo_.receiver == address(this) ||
transferInfo_.receiver == token
) revert CannotTransferOrExecuteOnBridgeContracts();
if (address(hook__) != address(0)) {
(postHookData, transferInfo) = hook__.dstPreHookCall(
DstPreHookCallParams(
msg.sender,
connectorCache[msg.sender],
transferInfo_
)
);
}
}
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_
);
}
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)
);
}
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;
}
function getMinFees(
address connector_,
uint256 msgGasLimit_,
uint256 payloadSize_
) external view returns (uint256 totalFees) {
return IConnector(connector_).getMinFees(msgGasLimit_, payloadSize_);
}
}
文件 3 的 44:ConnectorPlug.sol
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;
uint256 public messageIdPart;
event ConnectorPlugDisconnected();
constructor(
address bridge_,
address socket_,
uint32 siblingChainSlug_
) AccessControl(msg.sender) {
bridge__ = IBridge(bridge_);
socket__ = ISocket(socket_);
siblingChainSlug = siblingChainSlug_;
_grantRole(RESCUE_ROLE, msg.sender);
}
function outbound(
uint256 msgGasLimit_,
bytes memory payload_,
bytes memory
) external payable override returns (bytes32 messageId_) {
if (msg.sender != address(bridge__)) revert NotBridge();
return
socket__.outbound{value: msg.value}(
siblingChainSlug,
msgGasLimit_,
bytes32(0),
bytes32(0),
payload_
);
}
function inbound(
uint32 siblingChainSlug_,
bytes calldata payload_
) external payable override {
if (msg.sender != address(socket__)) revert NotSocket();
bridge__.receiveInbound(siblingChainSlug_, payload_);
}
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();
}
function getMessageId() external view returns (bytes32) {
return bytes32(messageIdPart | (socket__.globalMessageCount()));
}
}
文件 4 的 44:ConnectorPoolPlugin.sol
pragma solidity 0.8.13;
import "../HookBase.sol";
abstract contract ConnectorPoolPlugin is HookBase {
mapping(uint256 => uint256) public poolLockedAmounts;
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_;
}
}
文件 5 的 44:Constants.sol
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 LYRA_VAULT_ZAP_HOOK = keccak256("LYRA_VAULT_ZAP_HOOK");
bytes32 constant LYRA_SHARE_HANDLER_HOOK = keccak256("LYRA_VAULT_ZAP_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");
文件 6 的 44:Controller.sol
pragma solidity 0.8.13;
import "./Base.sol";
contract Controller is Base {
uint256 public totalMinted;
constructor(address token_) Base(token_) {
bridgeType = NORMAL_CONTROLLER;
}
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_)
);
totalMinted -= transferInfo.amount;
_burn(msg.sender, transferInfo.amount);
_afterBridge(
msgGasLimit_,
connector_,
options_,
postHookData,
transferInfo
);
}
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));
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);
}
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_);
}
}
文件 7 的 44:Controller_YieldLimitExecHook.sol
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);
}
contract Controller_YieldLimitExecHook is LimitExecutionHook {
using SafeTransferLib for IMintableERC20;
using FixedPointMathLib for uint256;
uint256 private constant MAX_BPS = 10_000;
IYieldToken public immutable yieldToken__;
uint256 public totalUnderlyingAssets;
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);
}
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)
);
}
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;
}
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) {
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) {
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
);
}
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(""));
}
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_);
}
}
文件 8 的 44: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);
}
}
文件 9 的 44:Errors.sol
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();
文件 10 的 44: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)
}
}
}
文件 11 的 44:ExecutionHelper.sol
pragma solidity 0.8.13;
import "../../libraries/ExcessivelySafeCall.sol";
import "../../utils/RescueBase.sol";
import "../../common/Errors.sol";
contract ExecutionHelper is RescueBase {
using ExcessivelySafeCall for address;
uint16 private constant MAX_COPY_BYTES = 0;
address public hook;
bytes32 public messageId;
uint256 public bridgeAmount;
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_;
}
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_;
(success, ) = target_.excessivelySafeCall(
gasleft(),
MAX_COPY_BYTES,
payload_
);
messageId = bytes32(0);
bridgeAmount = 0;
}
}
文件 12 的 44:Faucet.sol
pragma solidity 0.8.13;
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);
}
}
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");
}
}
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_);
}
}
}
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_);
}
}
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_];
}
}
abstract contract RescueBase is AccessControl {
bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE");
function rescueFunds(
address token_,
address rescueTo_,
uint256 amount_
) external onlyRole(RESCUE_ROLE) {
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}
contract Faucet is RescueBase {
using SafeTransferLib for ERC20;
constructor() AccessControl(msg.sender) {
_grantRole(RESCUE_ROLE, msg.sender);
}
function getTokens(address receiver_, address[] calldata tokens_) external {
for (uint256 i = 0; i < tokens_.length; i++) {
ERC20 token = ERC20(tokens_[i]);
uint256 amount = 10 ** token.decimals() * 1000;
token.safeTransfer(receiver_, amount);
}
}
}
文件 13 的 44:FiatTokenV2_1_Controller.sol
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_);
}
}
文件 14 的 44:FixedPointMathLib.sol
pragma solidity >=0.8.0;
library FixedPointMathLib {
uint256 internal constant MAX_UINT256 = 2**256 - 1;
uint256 internal constant WAD = 1e18;
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD);
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD);
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y);
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y);
}
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
z := div(mul(x, y), denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
assembly {
switch x
case 0 {
switch n
case 0 {
z := scalar
}
default {
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
z := scalar
}
default {
z := x
}
let half := shr(1, scalar)
for {
n := shr(1, n)
} n {
n := shr(1, n)
} {
if shr(128, x) {
revert(0, 0)
}
let xx := mul(x, x)
let xxRound := add(xx, half)
if lt(xxRound, xx) {
revert(0, 0)
}
x := div(xxRound, scalar)
if mod(n, 2) {
let zx := mul(z, x)
if iszero(eq(div(zx, x), z)) {
if iszero(iszero(x)) {
revert(0, 0)
}
}
let zxRound := add(zx, half)
if lt(zxRound, zx) {
revert(0, 0)
}
z := div(zxRound, scalar)
}
}
}
}
}
function sqrt(uint256 x) internal pure returns (uint256 z) {
assembly {
let y := x
z := 181
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
z := shr(18, mul(z, add(y, 65536)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := sub(z, lt(div(x, z), z))
}
}
function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := mod(x, y)
}
}
function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
assembly {
r := div(x, y)
}
}
function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := add(gt(mod(x, y), 0), div(x, y))
}
}
}
文件 15 的 44:Gauge.sol
pragma solidity 0.8.13;
import "../common/Structs.sol";
abstract contract Gauge {
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();
}
}
}
文件 16 的 44:HookBase.sol
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";
abstract contract HookBase is ReentrancyGuard, IHook, RescueBase {
address public immutable vaultOrController;
bytes32 public hookType;
constructor(
address owner_,
address vaultOrController_
) AccessControl(owner_) {
vaultOrController = vaultOrController_;
_grantRole(RESCUE_ROLE, owner_);
}
modifier isVaultOrController() {
if (msg.sender != vaultOrController) revert NotAuthorized();
_;
}
}
文件 17 的 44:IBridge.sol
pragma solidity ^0.8.3;
interface IBridge {
function bridge(
address receiver_,
uint256 amount_,
uint256 msgGasLimit_,
address connector_,
bytes calldata extraData_,
bytes calldata options_
) external payable;
function receiveInbound(
uint32 siblingChainSlug_,
bytes memory payload_
) external payable;
function retry(address connector_, bytes32 messageId_) external;
}
文件 18 的 44:IConnector.sol
pragma solidity ^0.8.13;
interface IConnector {
function outbound(
uint256 msgGasLimit_,
bytes memory payload_,
bytes memory options_
) external payable returns (bytes32 messageId_);
function siblingChainSlug() external view returns (uint32);
function getMinFees(
uint256 msgGasLimit_,
uint256 payloadSize_
) external view returns (uint256 totalFees);
function getMessageId() external view returns (bytes32);
}
文件 19 的 44:IController.sol
pragma solidity ^0.8.13;
interface IController {
function identifierCache(
bytes32 messageId_
) external payable returns (bytes memory cache);
function connectorCache(
address connector_
) external payable returns (bytes memory cache);
}
文件 20 的 44:IERC20.sol
pragma solidity ^0.8.0;
interface IERC20 {
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function transfer(address to, uint256 amount) external returns (bool);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}
文件 21 的 44: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;
}
文件 22 的 44:IHook.sol
pragma solidity ^0.8.3;
import "../common/Structs.sol";
interface IHook {
function srcPreHookCall(
SrcPreHookCallParams calldata params_
)
external
returns (TransferInfo memory transferInfo, bytes memory postHookData);
function srcPostHookCall(
SrcPostHookCallParams calldata params_
) external returns (TransferInfo memory transferInfo);
function dstPreHookCall(
DstPreHookCallParams calldata params_
)
external
returns (bytes memory postHookData, TransferInfo memory transferInfo);
function dstPostHookCall(
DstPostHookCallParams calldata params_
) external returns (CacheData memory cacheData);
function preRetryHook(
PreRetryHookCallParams calldata params_
)
external
returns (bytes memory postHookData, TransferInfo memory transferInfo);
function postRetryHook(
PostRetryHookCallParams calldata params_
) external returns (CacheData memory cacheData);
}
文件 23 的 44:ILimitHook.sol
pragma solidity 0.8.13;
import "./IHook.sol";
interface ILimitHook is IHook {
function updateLimitParams(UpdateLimitParams[] calldata updates) external;
function checkLimit(address user, uint256 amount) external view;
function getIdentifierPendingAmount(
bytes32 messageId_
) external returns (uint256);
function getConnectorPendingAmount(
address connector_
) external returns (uint256);
function getCurrentReceivingLimit(
address connector_
) external view returns (uint256);
function getCurrentSendingLimit(
address connector_
) external view returns (uint256);
function getReceivingLimitParams(
address connector_
) external view returns (LimitParams memory);
function getSendingLimitParams(
address connector_
) external view returns (LimitParams memory);
}
文件 24 的 44:IMintableERC20.sol
pragma solidity 0.8.13;
interface IMintableERC20 {
function mint(address receiver_, uint256 amount_) external;
function burn(address burner_, uint256 amount_) external;
}
文件 25 的 44:IPlug.sol
pragma solidity 0.8.13;
interface IPlug {
function inbound(
uint32 srcChainSlug_,
bytes calldata payload_
) external payable;
}
文件 26 的 44: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__
);
}
文件 27 的 44:IStrategy.sol
pragma solidity 0.8.13;
interface IStrategy {
function withdraw(uint256 amount_) external returns (uint256 loss_);
function withdrawAll() external;
function estimatedTotalAssets()
external
view
returns (uint256 totalUnderlyingAssets_);
function invest() external;
}
文件 28 的 44:LimitExecutionHook.sol
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);
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) {
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) {
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);
}
}
文件 29 的 44:LimitHook.sol
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;
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
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);
transferInfo = params_.transferInfo;
transferInfo.amount = consumedAmount;
}
function dstPostHookCall(
DstPostHookCallParams memory params_
)
external
virtual
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));
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);
}
}
文件 30 的 44:LimitPlugin.sol
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");
mapping(address => LimitParams) _receivingLimitParams;
mapping(address => LimitParams) _sendingLimitParams;
event LimitParamsUpdated(UpdateLimitParams[] updates);
event PendingTokensBridged(
address connector,
address receiver,
uint256 consumedAmount,
uint256 pendingAmount,
bytes32 messageId
);
event TokensPending(
address connector,
address receiver,
uint256 consumedAmount,
uint256 pendingAmount,
bytes32 messageId
);
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]
);
_receivingLimitParams[updates[i].connector].maxLimit = updates[
i
].maxLimit;
_receivingLimitParams[updates[i].connector]
.ratePerSecond = updates[i].ratePerSecond;
} else {
_consumePartLimit(0, _sendingLimitParams[updates[i].connector]);
_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_]);
}
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;
}
}
文件 31 的 44:LyraTSAHooks.sol
pragma solidity 0.8.13;
import "../plugins/LimitPlugin.sol";
import "../../interfaces/IController.sol";
import "../plugins/ConnectorPoolPlugin.sol";
import "../LimitHook.sol";
import {IERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "../../interfaces/IBridge.sol";
import {IConnector} from "../../interfaces/IConnector.sol";
interface LyraTSA is IERC20 {
function underlying() external view returns (IERC20);
function withdrawTo(
address account,
uint256 amount
) external returns (bool);
function depositFor(
address account,
uint256 amount
) external returns (bool);
}
interface IBridgeExt is IBridge {
function token() external view returns (address);
}
interface IConnectorPlugExt is IConnector {
function bridge__() external returns (IBridge);
}
abstract contract LyraTSAHookBase is LimitHook {
struct PackedAddresses {
address returnRecipient;
address fallbackReceiver;
address withdrawConnector;
IBridgeExt withdrawVault;
IERC20 withdrawToken;
}
uint withdrawalMinGasLimit = 500000;
constructor(
address owner_,
address controller_,
bool useControllerPools_
) LimitHook(owner_, controller_, useControllerPools_) {
hookType = LYRA_VAULT_ZAP_HOOK;
}
receive() external payable {}
function setWithdrawalMinGasLimit(uint limit) external onlyOwner {
withdrawalMinGasLimit = limit;
}
function recoverEth(address payable recipient) external onlyOwner {
recipient.transfer(address(this).balance);
}
function recoverERC20(IERC20 token, address recipient) external onlyOwner {
token.transfer(recipient, token.balanceOf(address(this)));
}
function dstPreHookCall(
DstPreHookCallParams memory params_
)
external
override
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
);
transferInfo = params_.transferInfo;
transferInfo.amount = consumedAmount;
if (params_.transferInfo.extraData.length == 64 && pendingAmount == 0) {
(address returnRecipient, address withdrawConnector) = abi.decode(
params_.transferInfo.extraData,
(address, address)
);
if (
returnRecipient == address(0) || withdrawConnector == address(0)
) {
postHookData = abi.encode(consumedAmount, pendingAmount);
} else {
postHookData = abi.encode(
consumedAmount,
pendingAmount,
params_.transferInfo.receiver,
returnRecipient,
withdrawConnector
);
transferInfo.receiver = address(this);
}
} else {
postHookData = abi.encode(consumedAmount, pendingAmount);
}
}
function dstPostHookCall(
DstPostHookCallParams memory params_
)
external
override
isVaultOrController
returns (CacheData memory cacheData)
{
(
uint256 consumedAmount,
uint256 pendingAmount,
PackedAddresses memory addrs,
bool attemptToWithdraw
) = _parseParameters(params_);
if (attemptToWithdraw) {
IERC20 mintedToken = IERC20(IBridgeExt(vaultOrController).token());
uint balance = mintedToken.balanceOf(address(this));
if (balance != consumedAmount) {
revert("MINTED_BALANCE_MISMATCH");
}
bool conversionSucceeded = _convertToken(
mintedToken,
addrs.withdrawToken,
balance
);
if (conversionSucceeded) {
bool withdrew = _withdrawToRecipient(addrs);
if (!withdrew) {
addrs.withdrawToken.transfer(
addrs.fallbackReceiver,
addrs.withdrawToken.balanceOf(address(this))
);
}
} else {
mintedToken.transfer(addrs.fallbackReceiver, balance);
}
}
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 _parseParameters(
DstPostHookCallParams memory params_
)
internal
returns (
uint256 consumedAmount,
uint256 pendingAmount,
PackedAddresses memory addrs,
bool attemptToWithdraw
)
{
attemptToWithdraw = false;
if (params_.postHookData.length == 64) {
(consumedAmount, pendingAmount) = abi.decode(
params_.postHookData,
(uint256, uint256)
);
return (consumedAmount, pendingAmount, addrs, false);
} else if (params_.postHookData.length == 160) {
IERC20 mintedToken = IERC20(IBridgeExt(vaultOrController).token());
(
consumedAmount,
pendingAmount,
addrs.fallbackReceiver,
addrs.returnRecipient,
addrs.withdrawConnector
) = abi.decode(
params_.postHookData,
(uint256, uint256, address, address, address)
);
if (pendingAmount != 0) {
revert("INVALID_PENDING_AMOUNT");
}
addrs.withdrawVault = tryGetWithdrawVault(addrs.withdrawConnector);
if (address(addrs.withdrawVault) == address(0)) {
mintedToken.transfer(addrs.fallbackReceiver, consumedAmount);
return (consumedAmount, pendingAmount, addrs, false);
}
addrs.withdrawToken = tryGetToken(addrs.withdrawVault);
if (address(addrs.withdrawToken) == address(0)) {
mintedToken.transfer(addrs.fallbackReceiver, consumedAmount);
return (consumedAmount, pendingAmount, addrs, false);
}
return (consumedAmount, pendingAmount, addrs, true);
} else {
revert("parse: INVALID_DATA_LENGTH");
}
}
function _withdrawToRecipient(
PackedAddresses memory addrs
) internal returns (bool success) {
uint256 amount = addrs.withdrawToken.balanceOf(address(this));
addrs.withdrawToken.approve(address(addrs.withdrawVault), amount);
uint256 fees = IConnectorPlugExt(addrs.withdrawConnector).getMinFees(
withdrawalMinGasLimit,
0
);
if (fees > address(this).balance) {
revert("INSUFFICIENT_ETH_BALANCE");
}
try
addrs.withdrawVault.bridge{value: fees}(
addrs.returnRecipient,
amount,
withdrawalMinGasLimit,
addrs.withdrawConnector,
new bytes(0),
new bytes(0)
)
{
return true;
} catch {
return false;
}
}
function tryGetWithdrawVault(
address connector
) internal returns (IBridgeExt withdrawVault) {
(bool success, bytes memory data) = connector.call(
abi.encodeWithSignature("bridge__()")
);
if (!success || data.length == 0) {
return IBridgeExt(address(0));
}
return IBridgeExt(abi.decode(data, (address)));
}
function tryGetToken(
IBridgeExt withdrawVault
) internal returns (IERC20 withdrawToken) {
(bool success, bytes memory data) = address(withdrawVault).call(
abi.encodeWithSignature("token()")
);
if (!success || data.length == 0) {
return IERC20(address(0));
}
return IERC20(abi.decode(data, (address)));
}
function _convertToken(
IERC20 depositToken,
IERC20 withdrawToken,
uint256 amount
) internal virtual returns (bool success);
}
contract LyraTSADepositHook is LyraTSAHookBase {
constructor(
address owner_,
address controller_,
bool useControllerPools_
) LyraTSAHookBase(owner_, controller_, useControllerPools_) {}
function _convertToken(
IERC20 depositToken,
IERC20 withdrawToken,
uint256 amount
) internal override returns (bool success) {
LyraTSA tsa = LyraTSA(address(withdrawToken));
depositToken.approve(address(tsa), amount);
try tsa.depositFor(address(this), amount) returns (bool) {
return true;
} catch {
return false;
}
}
}
contract LyraTSAWithdrawHook is LyraTSAHookBase {
constructor(
address owner_,
address controller_,
bool useControllerPools_
) LyraTSAHookBase(owner_, controller_, useControllerPools_) {}
function _convertToken(
IERC20 depositToken,
IERC20 withdrawToken,
uint256 amount
) internal override returns (bool success) {
LyraTSA tsa = LyraTSA(address(depositToken));
if (tsa.underlying() != withdrawToken) {
return false;
}
try tsa.withdrawTo(address(this), amount) returns (bool) {
return true;
} catch {
return false;
}
}
}
文件 32 的 44:LyraTSAShareHandlerHooks.sol
pragma solidity 0.8.13;
import "../plugins/LimitPlugin.sol";
import "../../interfaces/IController.sol";
import "../plugins/ConnectorPoolPlugin.sol";
import "../LimitHook.sol";
import {IERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import "../../interfaces/IBridge.sol";
import {IConnector} from "../../interfaces/IConnector.sol";
interface LyraTSAShareHandler {
function initiateDeposit(
address toVault,
address fallbackDest,
address withdrawalConnector,
address withdrawalRecipient,
uint amount
) external;
function initiateWithdrawal(
address fromVault,
address fallbackDest,
address withdrawalConnector,
address withdrawalRecipient,
uint amount
) external;
}
interface IBridgeExt is IBridge {
function token() external view returns (address);
}
interface IConnectorPlugExt is IConnector {
function bridge__() external returns (IBridge);
}
interface IBaseTSA {
function getSharesValue(uint numShares) external view returns (uint);
}
abstract contract LyraTSAShareHandlerHookBase is LimitHook {
struct PackedAddresses {
address returnRecipient;
address fallbackReceiver;
address withdrawConnector;
}
uint public withdrawalMinGasLimit = 500000;
LyraTSAShareHandler public lyraTSAShareHandler;
constructor(
address owner_,
address controller_,
bool useControllerPools_
) LimitHook(owner_, controller_, useControllerPools_) {
hookType = LYRA_VAULT_ZAP_HOOK;
}
receive() external payable {}
function setWithdrawalMinGasLimit(uint limit) external onlyOwner {
withdrawalMinGasLimit = limit;
}
function recoverEth(address payable recipient) external onlyOwner {
recipient.transfer(address(this).balance);
}
function recoverERC20(IERC20 token, address recipient) external onlyOwner {
token.transfer(recipient, token.balanceOf(address(this)));
}
function setShareHandler(address shareHandler) external onlyOwner {
lyraTSAShareHandler = LyraTSAShareHandler(shareHandler);
}
function dstPreHookCall(
DstPreHookCallParams memory params_
)
external
override
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
);
transferInfo = params_.transferInfo;
transferInfo.amount = consumedAmount;
if (params_.transferInfo.extraData.length == 64 && pendingAmount == 0) {
(address returnRecipient, address withdrawConnector) = abi.decode(
params_.transferInfo.extraData,
(address, address)
);
if (
returnRecipient == address(0) || withdrawConnector == address(0)
) {
postHookData = abi.encode(consumedAmount, pendingAmount);
} else {
postHookData = abi.encode(
consumedAmount,
pendingAmount,
params_.transferInfo.receiver,
returnRecipient,
withdrawConnector
);
transferInfo.receiver = address(this);
}
} else {
postHookData = abi.encode(consumedAmount, pendingAmount);
}
}
function dstPostHookCall(
DstPostHookCallParams memory params_
)
external
override
isVaultOrController
returns (CacheData memory cacheData)
{
(
uint256 consumedAmount,
uint256 pendingAmount,
PackedAddresses memory addrs,
bool attemptToProcess
) = _parseParameters(params_);
if (attemptToProcess) {
IERC20 mintedToken = IERC20(IBridgeExt(vaultOrController).token());
uint balance = mintedToken.balanceOf(address(this));
if (balance < consumedAmount) {
revert("MINTED_BALANCE_MISMATCH");
}
_completeAction(mintedToken, addrs, consumedAmount);
}
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 _parseParameters(
DstPostHookCallParams memory params_
)
internal
pure
returns (
uint256 consumedAmount,
uint256 pendingAmount,
PackedAddresses memory addrs,
bool attemptToProcess
)
{
if (params_.postHookData.length == 64) {
(consumedAmount, pendingAmount) = abi.decode(
params_.postHookData,
(uint256, uint256)
);
return (consumedAmount, pendingAmount, addrs, false);
} else if (params_.postHookData.length == 160) {
(
consumedAmount,
pendingAmount,
addrs.fallbackReceiver,
addrs.returnRecipient,
addrs.withdrawConnector
) = abi.decode(
params_.postHookData,
(uint256, uint256, address, address, address)
);
if (pendingAmount != 0) {
revert("INVALID_PENDING_AMOUNT");
}
if (
addrs.fallbackReceiver == address(0) ||
addrs.returnRecipient == address(0) ||
addrs.withdrawConnector == address(0)
) {
return (consumedAmount, pendingAmount, addrs, false);
}
return (consumedAmount, pendingAmount, addrs, true);
} else {
revert("parse: INVALID_DATA_LENGTH");
}
}
function _completeAction(
IERC20 mintedToken,
PackedAddresses memory addrs,
uint256 consumedAmount
) internal virtual;
function _checkFeedsStale(IBaseTSA tsa) internal {
try tsa.getSharesValue(1e18) {
} catch {
revert("LYRA_FEEDS_STALE");
}
}
}
contract LyraTSAShareHandlerDepositHook is LyraTSAShareHandlerHookBase {
constructor(
address owner_,
address controller_,
bool useControllerPools_
) LyraTSAShareHandlerHookBase(owner_, controller_, useControllerPools_) {}
function _completeAction(
IERC20 mintedToken,
PackedAddresses memory addrs,
uint256 consumedAmount
) internal override {
IBridgeExt withdrawVault = tryGetWithdrawVault(addrs.withdrawConnector);
address token = address(tryGetToken(withdrawVault));
_checkFeedsStale(IBaseTSA(address(token)));
mintedToken.approve(address(lyraTSAShareHandler), consumedAmount);
try
lyraTSAShareHandler.initiateDeposit(
token,
addrs.fallbackReceiver,
addrs.withdrawConnector,
addrs.returnRecipient,
consumedAmount
)
{
} catch {
mintedToken.transfer(addrs.fallbackReceiver, consumedAmount);
}
}
function tryGetWithdrawVault(
address connector
) internal returns (IBridgeExt withdrawVault) {
(bool success, bytes memory data) = connector.call(
abi.encodeWithSignature("bridge__()")
);
if (!success || data.length == 0) {
return IBridgeExt(address(0));
}
return IBridgeExt(abi.decode(data, (address)));
}
function tryGetToken(
IBridgeExt withdrawVault
) internal returns (IERC20 withdrawToken) {
(bool success, bytes memory data) = address(withdrawVault).call(
abi.encodeWithSignature("token()")
);
if (!success || data.length == 0) {
return IERC20(address(0));
}
return IERC20(abi.decode(data, (address)));
}
}
contract LyraTSAShareHandlerWithdrawHook is LyraTSAShareHandlerHookBase {
constructor(
address owner_,
address controller_,
bool useControllerPools_
) LyraTSAShareHandlerHookBase(owner_, controller_, useControllerPools_) {}
function _completeAction(
IERC20 mintedToken,
PackedAddresses memory addrs,
uint256 consumedAmount
) internal override {
_checkFeedsStale(IBaseTSA(address(mintedToken)));
mintedToken.approve(address(lyraTSAShareHandler), consumedAmount);
try
lyraTSAShareHandler.initiateWithdrawal(
address(mintedToken),
addrs.fallbackReceiver,
addrs.withdrawConnector,
addrs.returnRecipient,
consumedAmount
)
{
} catch {
mintedToken.transfer(addrs.fallbackReceiver, consumedAmount);
}
}
}
文件 33 的 44:Math.sol
pragma solidity ^0.8.0;
library Math {
enum Rounding {
Down,
Up,
Zero
}
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
function average(uint256 a, uint256 b) internal pure returns (uint256) {
return (a & b) + (a ^ b) / 2;
}
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
return a == 0 ? 0 : (a - 1) / b + 1;
}
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
uint256 prod0;
uint256 prod1;
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
if (prod1 == 0) {
return prod0 / denominator;
}
require(denominator > prod1, "Math: mulDiv overflow");
uint256 remainder;
assembly {
remainder := mulmod(x, y, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
uint256 twos = denominator & (~denominator + 1);
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
uint256 inverse = (3 * denominator) ^ 2;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
inverse *= 2 - denominator * inverse;
result = prod0 * inverse;
return result;
}
}
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
uint256 result = 1 << (log2(a) >> 1);
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}
文件 34 的 44: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_);
}
}
文件 35 的 44:ReentrancyGuard.sol
pragma solidity ^0.8.0;
abstract contract ReentrancyGuard {
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
_status = _ENTERED;
}
function _nonReentrantAfter() private {
_status = _NOT_ENTERED;
}
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == _ENTERED;
}
}
文件 36 的 44:RescueBase.sol
pragma solidity 0.8.13;
import {RescueFundsLib} from "../libraries/RescueFundsLib.sol";
import {AccessControl} from "./AccessControl.sol";
abstract contract RescueBase is AccessControl {
bytes32 constant RESCUE_ROLE = keccak256("RESCUE_ROLE");
function rescueFunds(
address token_,
address rescueTo_,
uint256 amount_
) external onlyRole(RESCUE_ROLE) {
RescueFundsLib.rescueFunds(token_, rescueTo_, amount_);
}
}
文件 37 的 44:RescueFundsLib.sol
pragma solidity 0.8.13;
import "lib/solmate/src/utils/SafeTransferLib.sol";
import "lib/solmate/src/tokens/ERC20.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_);
}
}
}
文件 38 的 44: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");
}
}
文件 39 的 44:Structs.sol
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;
}
文件 40 的 44:SuperToken.sol
pragma solidity 0.8.13;
import "lib/solmate/src/tokens/ERC20.sol";
import "../utils/RescueBase.sol";
import "../interfaces/IHook.sol";
contract SuperToken is ERC20, RescueBase {
bytes32 constant CONTROLLER_ROLE = keccak256("CONTROLLER_ROLE");
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_,
address initialSupplyHolder_,
address owner_,
uint256 initialSupply_
) ERC20(name_, symbol_, decimals_) AccessControl(owner_) {
_mint(initialSupplyHolder_, initialSupply_);
_grantRole(RESCUE_ROLE, owner_);
}
function burn(
address user_,
uint256 amount_
) external onlyRole(CONTROLLER_ROLE) {
_burn(user_, amount_);
}
function mint(
address receiver_,
uint256 amount_
) external onlyRole(CONTROLLER_ROLE) {
_mint(receiver_, amount_);
}
}
文件 41 的 44:Vault.sol
pragma solidity 0.8.13;
import "./Base.sol";
import "../interfaces/IConnector.sol";
import "lib/solmate/src/tokens/ERC20.sol";
contract Vault is Base {
using SafeTransferLib for ERC20;
constructor(address token_) Base(token_) {
bridgeType = token_ == ETH_ADDRESS ? NATIVE_VAULT : ERC20_VAULT;
}
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
);
}
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);
}
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_);
}
}
文件 42 的 44:Vault_YieldLimitExecHook.sol
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 "lib/solmate/src/tokens/ERC20.sol";
import "lib/solmate/src/utils/SafeTransferLib.sol";
import {IConnector} from "../ConnectorPlug.sol";
import "./LimitExecutionHook.sol";
contract Vault_YieldLimitExecHook is LimitExecutionHook {
using SafeTransferLib for ERC20;
using FixedPointMathLib for uint256;
uint256 private constant MAX_BPS = 10_000;
IStrategy public strategy;
ERC20 public immutable underlyingAsset__;
uint256 public totalLockedInStrategy;
uint256 public totalIdle;
uint256 public totalDebt;
uint128 public lastRebalanceTimestamp;
uint128 public rebalanceDelay;
uint256 public debtRatio;
bool public emergencyShutdown;
uint256 public lastTotalUnderlyingAssetsSynced;
event WithdrawFromStrategy(uint256 withdrawn);
event Rebalanced(
uint256 totalIdle,
uint256 totalDebt,
uint256 credit,
uint256 debtOutstanding
);
event ShutdownStateUpdated(bool shutdownState);
event DebtRatioUpdated(uint256 debtRatio);
event StrategyUpdated(address strategy);
event RebalanceDelayUpdated(uint128 rebalanceDelay);
modifier notShutdown() {
if (emergencyShutdown) revert VaultShutdown();
_;
}
constructor(
uint256 debtRatio_,
uint128 rebalanceDelay_,
address strategy_,
address underlyingAsset_,
address vault_,
address executionHelper_,
bool useControllerPools_
)
LimitExecutionHook(
msg.sender,
vault_,
executionHelper_,
useControllerPools_
)
{
underlyingAsset__ = ERC20(underlyingAsset_);
debtRatio = debtRatio_;
rebalanceDelay = rebalanceDelay_;
strategy = IStrategy(strategy_);
hookType = LIMIT_EXECUTION_YIELD_HOOK;
_grantRole(LIMIT_UPDATER_ROLE, msg.sender);
}
function srcPreHookCall(
SrcPreHookCallParams calldata params_
) public override notShutdown returns (TransferInfo memory, bytes memory) {
totalIdle += params_.transferInfo.amount;
return super.srcPreHookCall(params_);
}
function srcPostHookCall(
SrcPostHookCallParams memory srcPostHookCallParams_
)
public
override
isVaultOrController
returns (TransferInfo memory transferInfo)
{
_checkDelayAndRebalance();
uint256 totalUnderlyingAsset = strategy.estimatedTotalAssets() +
totalIdle;
uint256 totalYieldSync = totalUnderlyingAsset -
lastTotalUnderlyingAssetsSynced;
lastTotalUnderlyingAssetsSynced = totalUnderlyingAsset;
transferInfo = srcPostHookCallParams_.transferInfo;
if (srcPostHookCallParams_.transferInfo.amount == 0) {
transferInfo.extraData = abi.encode(totalYieldSync, bytes(""));
} else {
transferInfo.extraData = abi.encode(
totalYieldSync,
srcPostHookCallParams_.transferInfo.extraData
);
}
}
function dstPreHookCall(
DstPreHookCallParams calldata params_
)
public
override
notShutdown
returns (bytes memory postHookData, TransferInfo memory transferInfo)
{
(postHookData, transferInfo) = super.dstPreHookCall(params_);
if (transferInfo.amount > totalUnderlyingAssets())
revert NotEnoughAssets();
(bytes memory options_, bytes memory payload_) = abi.decode(
params_.transferInfo.extraData,
(bytes, bytes)
);
bool pullFromStrategy = abi.decode(options_, (bool));
if (transferInfo.amount > totalIdle) {
if (pullFromStrategy) {
_withdrawFromStrategy(transferInfo.amount - totalIdle);
} else {
(
uint256 consumedUnderlying,
uint256 pendingUnderlying,
uint256 bridgeUnderlying
) = abi.decode(postHookData, (uint256, uint256, uint256));
pendingUnderlying += transferInfo.amount - totalIdle;
postHookData = abi.encode(
transferInfo.amount,
pendingUnderlying,
bridgeUnderlying
);
transferInfo.amount = totalIdle;
LimitParams storage receivingParams = _receivingLimitParams[
params_.connector
];
receivingParams.lastUpdateLimit +=
consumedUnderlying -
transferInfo.amount;
}
totalIdle = 0;
} else totalIdle -= transferInfo.amount;
transferInfo.extraData = payload_;
transferInfo.receiver = params_.transferInfo.receiver;
}
function dstPostHookCall(
DstPostHookCallParams calldata params_
) public override returns (CacheData memory cacheData) {
return super.dstPostHookCall(params_);
}
function preRetryHook(
PreRetryHookCallParams calldata params_
)
public
override
notShutdown
returns (bytes memory postHookData, TransferInfo memory transferInfo)
{
(postHookData, transferInfo) = super.preRetryHook(params_);
if (transferInfo.amount > totalIdle) {
_withdrawFromStrategy(transferInfo.amount - totalIdle);
totalIdle = 0;
} else totalIdle -= transferInfo.amount;
}
function postRetryHook(
PostRetryHookCallParams calldata params_
) public override returns (CacheData memory cacheData) {
return super.postRetryHook(params_);
}
function withdrawFromStrategy(
uint256 underlyingAsset_
) external onlyOwner returns (uint256) {
return _withdrawFromStrategy(underlyingAsset_);
}
function _withdrawFromStrategy(
uint256 underlyingAsset_
) internal returns (uint256 withdrawn) {
uint256 preBalance = underlyingAsset__.balanceOf(address(this));
strategy.withdraw(underlyingAsset_);
withdrawn = underlyingAsset__.balanceOf(address(this)) - preBalance;
totalIdle += withdrawn;
totalDebt -= withdrawn;
underlyingAsset__.transfer(vaultOrController, withdrawn);
emit WithdrawFromStrategy(withdrawn);
}
function _withdrawAllFromStrategy() internal returns (uint256) {
uint256 preBalance = underlyingAsset__.balanceOf(address(this));
strategy.withdrawAll();
uint256 withdrawn = underlyingAsset__.balanceOf(address(this)) -
preBalance;
totalIdle += withdrawn;
totalDebt = 0;
underlyingAsset__.transfer(vaultOrController, withdrawn);
emit WithdrawFromStrategy(withdrawn);
return withdrawn;
}
function rebalance() external notShutdown {
_rebalance();
}
function _checkDelayAndRebalance() internal {
uint128 timeElapsed = uint128(block.timestamp) - lastRebalanceTimestamp;
if (timeElapsed >= rebalanceDelay) {
_rebalance();
}
}
function _rebalance() internal {
if (address(strategy) == address(0)) return;
lastRebalanceTimestamp = uint128(block.timestamp);
uint256 credit = _creditAvailable();
uint256 pendingDebt = _debtOutstanding();
if (credit > 0) {
totalIdle -= credit;
totalDebt += credit;
totalLockedInStrategy += credit;
underlyingAsset__.safeTransferFrom(
vaultOrController,
address(strategy),
credit
);
strategy.invest();
} else if (pendingDebt > 0) {
_withdrawFromStrategy(pendingDebt);
}
emit Rebalanced(totalIdle, totalDebt, credit, pendingDebt);
}
function totalUnderlyingAssets() public view returns (uint256) {
return strategy.estimatedTotalAssets() + totalIdle;
}
function _creditAvailable() internal view returns (uint256) {
uint256 vaultTotalAssets = totalUnderlyingAssets();
uint256 vaultDebtLimit = (debtRatio * vaultTotalAssets) / MAX_BPS;
uint256 vaultTotalDebt = totalDebt;
if (vaultDebtLimit <= vaultTotalDebt) return 0;
uint256 availableCredit = vaultDebtLimit - vaultTotalDebt;
return Math.min(availableCredit, totalIdle);
}
function creditAvailable() external view returns (uint256) {
return _creditAvailable();
}
function _debtOutstanding() internal view returns (uint256) {
if (debtRatio == 0) {
return totalDebt;
}
uint256 debtLimit = ((debtRatio * totalUnderlyingAssets()) / MAX_BPS);
if (totalDebt <= debtLimit) return 0;
else return totalDebt - debtLimit;
}
function debtOutstanding() external view returns (uint256) {
return _debtOutstanding();
}
function updateEmergencyShutdownState(
bool shutdownState_,
bool detachStrategy
) external onlyOwner {
if (shutdownState_ && detachStrategy) {
_withdrawAllFromStrategy();
strategy = IStrategy(address(0));
}
emergencyShutdown = shutdownState_;
emit ShutdownStateUpdated(shutdownState_);
}
function setDebtRatio(uint256 debtRatio_) external onlyOwner {
if (debtRatio_ > MAX_BPS) revert DebtRatioTooHigh();
debtRatio = debtRatio_;
emit DebtRatioUpdated(debtRatio_);
}
function setStrategy(address strategy_) external onlyOwner {
strategy = IStrategy(strategy_);
emit StrategyUpdated(strategy_);
}
function setRebalanceDelay(uint128 rebalanceDelay_) external onlyOwner {
rebalanceDelay = rebalanceDelay_;
emit RebalanceDelayUpdated(rebalanceDelay_);
}
}
文件 43 的 44:YieldToken.sol
pragma solidity 0.8.13;
import "./YieldTokenBase.sol";
import {IStrategy} from "../../interfaces/IStrategy.sol";
import {IConnector} from "../../interfaces/IConnector.sol";
import {IHook} from "../../interfaces/IHook.sol";
contract YieldToken is YieldTokenBase {
using FixedPointMathLib for uint256;
bytes32 constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 constant HOOK_ROLE = keccak256("HOOK_ROLE");
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_
) YieldTokenBase(name_, symbol_, decimals_) AccessControl(msg.sender) {
_grantRole(RESCUE_ROLE, msg.sender);
}
function calculateMintAmount(
uint256 underlyingAssets_
) external view returns (uint256) {
uint256 supply = _totalSupply;
return
supply == 0
? underlyingAssets_
: underlyingAssets_.mulDivDown(
supply,
totalUnderlyingAssets - underlyingAssets_
);
}
function burn(
address user_,
uint256 shares_
) external nonReentrant onlyRole(MINTER_ROLE) {
_burn(user_, shares_);
}
function mint(
address receiver_,
uint256 amount_
) external nonReentrant onlyRole(MINTER_ROLE) {
_mint(receiver_, amount_);
}
function updateTotalUnderlyingAssets(
uint256 amount_
) external onlyRole(HOOK_ROLE) {
_updateTotalUnderlyingAssets(amount_);
}
function _updateTotalUnderlyingAssets(uint256 amount_) internal {
totalUnderlyingAssets = amount_;
}
}
文件 44 的 44:YieldTokenBase.sol
pragma solidity >=0.8.0;
import "lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";
import {FixedPointMathLib} from "lib/solmate/src/utils/FixedPointMathLib.sol";
import "../../utils/RescueBase.sol";
import "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {PermitDeadlineExpired, InvalidSigner} from "../../common/Errors.sol";
abstract contract YieldTokenBase is RescueBase, ReentrancyGuard, IERC20 {
using FixedPointMathLib for uint256;
string public name;
string public symbol;
uint8 public immutable decimals;
uint256 internal _totalSupply;
mapping(address => uint256) internal _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;
uint256 public totalUnderlyingAssets;
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 convertToShares(
uint256 underlyingAssets
) public view virtual returns (uint256) {
uint256 supply = _totalSupply;
return
supply == 0
? underlyingAssets
: underlyingAssets.mulDivDown(supply, totalUnderlyingAssets);
}
function convertToAssets(
uint256 shares
) public view virtual returns (uint256) {
uint256 supply = _totalSupply;
return
supply == 0
? shares
: shares.mulDivDown(totalUnderlyingAssets, supply);
}
function balanceOf(address user_) external view returns (uint256) {
uint256 balance = _balanceOf[user_];
if (balance == 0) return 0;
return convertToAssets(balance);
}
function totalSupply() external view returns (uint256) {
if (_totalSupply == 0) return 0;
return totalUnderlyingAssets;
}
function approve(
address spender_,
uint256 amount_
) public virtual returns (bool) {
uint256 shares = convertToShares(amount_);
allowance[msg.sender][spender_] = shares;
emit Approval(msg.sender, spender_, shares);
return true;
}
function transfer(
address to_,
uint256 amount_
) public override returns (bool) {
uint256 sharesToTransfer = convertToShares(amount_);
_balanceOf[msg.sender] -= sharesToTransfer;
unchecked {
_balanceOf[to_] += sharesToTransfer;
}
emit Transfer(msg.sender, to_, amount_);
return true;
}
function transferFrom(
address from_,
address to_,
uint256 amount_
) public override returns (bool) {
uint256 sharesToTransfer = convertToShares(amount_);
uint256 allowed = allowance[from_][msg.sender];
if (allowed != type(uint256).max)
allowance[from_][msg.sender] = allowed - sharesToTransfer;
_balanceOf[from_] -= sharesToTransfer;
unchecked {
_balanceOf[to_] += sharesToTransfer;
}
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 {
if (deadline < block.timestamp) revert PermitDeadlineExpired();
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,
convertToShares(value),
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
if (recoveredAddress == address(0) || recoveredAddress != owner)
revert InvalidSigner();
allowance[recoveredAddress][spender] = convertToShares(value);
}
emit Approval(owner, spender, convertToShares(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);
}
}
{
"compilationTarget": {
"contracts/token/SuperToken.sol": "SuperToken"
},
"evmVersion": "london",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 999999
},
"remappings": []
}
[{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"uint8","name":"decimals_","type":"uint8"},{"internalType":"address","name":"initialSupplyHolder_","type":"address"},{"internalType":"address","name":"owner_","type":"address"},{"internalType":"uint256","name":"initialSupply_","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidTokenAddress","type":"error"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"NoPermit","type":"error"},{"inputs":[],"name":"OnlyNominee","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"claimer","type":"address"}],"name":"OwnerClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"nominee","type":"address"}],"name":"OwnerNominated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"grantee","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"revokee","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role_","type":"bytes32"},{"internalType":"address","name":"grantee_","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role_","type":"bytes32"},{"internalType":"address","name":"address_","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver_","type":"address"},{"internalType":"uint256","name":"amount_","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"nominee_","type":"address"}],"name":"nominateOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"nominee","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","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":[{"internalType":"bytes32","name":"role_","type":"bytes32"},{"internalType":"address","name":"revokee_","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]