编译器
0.8.20+commit.a1b79de6
文件 1 的 33:Address.sol
pragma solidity ^0.8.20;
library Address {
error AddressInsufficientBalance(address account);
error AddressEmptyCode(address target);
error FailedInnerCall();
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
function _revert(bytes memory returndata) private pure {
if (returndata.length > 0) {
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}
文件 2 的 33:AggregatorV3Interface.sol
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals() external view returns (uint8);
function description() external view returns (string memory);
function version() external view returns (uint256);
function getRoundData(
uint80 _roundId
) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
function latestRoundData()
external
view
returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound);
}
文件 3 的 33:CommonAccountingLogic.sol
pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IWETH} from "@wormhole/interfaces/IWETH.sol";
import {HubSpokeStructs} from "../../../contracts/HubSpokeStructs.sol";
library CommonAccountingLogic {
using SafeERC20 for IERC20;
error InsufficientMsgValue();
error UnusedParameterMustBeZero();
function handleInboundTokensAndAdjustAction(
HubSpokeStructs.Action _action,
address _asset,
uint256 _amount,
IWETH _weth
) public returns (
HubSpokeStructs.Action action,
address asset,
uint256 amount
) {
(action, asset, amount, ) = handleInboundTokensAndAdjustAction(_action, _asset, _amount, _weth, 0);
}
function handleInboundTokensAndAdjustAction(
HubSpokeStructs.Action _action,
address _asset,
uint256 _amount,
IWETH _weth,
uint256 _cost
) public returns (
HubSpokeStructs.Action action,
address asset,
uint256 amount,
uint256 remainingMsgValue
) {
action = _action;
asset = _asset;
amount = _amount;
remainingMsgValue = msg.value;
if (action == HubSpokeStructs.Action.RepayNative || action == HubSpokeStructs.Action.DepositNative) {
if (asset != address(0) || amount != 0) revert UnusedParameterMustBeZero();
if (msg.value <= _cost) {
revert InsufficientMsgValue();
}
asset = address(_weth);
amount = msg.value - _cost;
_weth.deposit{value: amount}();
action = action == HubSpokeStructs.Action.RepayNative ? HubSpokeStructs.Action.Repay : HubSpokeStructs.Action.Deposit;
remainingMsgValue = _cost;
} else if (action == HubSpokeStructs.Action.Deposit || action == HubSpokeStructs.Action.Repay) {
IERC20(asset).safeTransferFrom(msg.sender, address(this), amount);
}
}
}
文件 4 的 33:CommonOptimisticFinalityLogic.sol
pragma solidity ^0.8.0;
import {HubSpokeStructs} from "../../../contracts/HubSpokeStructs.sol";
library CommonOptimisticFinalityLogic {
function creditMissingOrConflicting(HubSpokeStructs.Credit memory credit, HubSpokeStructs.Credit storage storedCredit) public view returns (bool) {
return storedCredit.createdAt == 0 ||
storedCredit.user != credit.user ||
storedCredit.token != credit.token ||
storedCredit.creditedAmount != credit.creditedAmount;
}
function getActionDirection(HubSpokeStructs.Action _action) public pure returns (HubSpokeStructs.ActionDirection) {
if (
_action == HubSpokeStructs.Action.Withdraw ||
_action == HubSpokeStructs.Action.WithdrawNative ||
_action == HubSpokeStructs.Action.Borrow ||
_action == HubSpokeStructs.Action.BorrowNative
) {
return HubSpokeStructs.ActionDirection.Outbound;
}
return HubSpokeStructs.ActionDirection.Inbound;
}
}
文件 5 的 33:ContextUpgradeable.sol
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
文件 6 的 33:HubSpokeEvents.sol
pragma solidity ^0.8.0;
import "../interfaces/ILiquidationCalculator.sol";
contract HubSpokeEvents {
event Liquidation(address indexed liquidator, address indexed vault, ILiquidationCalculator.DenormalizedLiquidationAsset[] liquidationAssets);
event AccrualIndexUpdated(bytes32 indexed asset, uint256 deposit, uint256 borrow, uint256 timestamp);
event Deposit(address indexed vault, bytes32 indexed asset, uint256 amount, uint256 vaultTotalDeposited);
event Withdraw(address indexed vault, bytes32 indexed asset, uint256 amount, uint256 vaultTotalDeposited);
event Borrow(address indexed vault, bytes32 indexed asset, uint256 amount, uint256 vaultTotalBorrowed);
event Repay(address indexed vault, bytes32 indexed asset, uint256 amount, uint256 vaultTotalBorrowed);
event ReservesWithdrawn(bytes32 indexed asset, uint256 amount, uint16 destinationChain, bytes32 destinationAddress);
event SpokeRegistered(uint16 chainId, bytes32 spoke);
event GlobalStateMigrated();
event UserMigrated(address indexed user);
event SetLiquidationFee(uint256 value, uint256 precision);
event SpokeCreditCreated(address indexed user, address indexed asset, uint256 amount, uint256 nonce);
event SpokeCreditLost(address indexed user, address indexed asset, uint256 amount, uint256 nonce);
event SpokeCreditRefundable(address indexed user, address indexed asset, uint256 amount, uint256 nonce);
event SpokeCreditConfirmed(uint256 nonce);
event SpokeCreditFinalized(uint256 nonce);
event SpokeCreditRefunded(address indexed user, address indexed asset, uint256 amount, uint256 nonce);
event SpokeLimitsChanged(address indexed asset, uint256 creditLimit, uint256 custodyLimit, uint256 transactionLimit);
event SpokeFeesChanged(uint256 inboundTokenFee, uint256 outboundTokenFee);
event SpokeFundsReleased(address indexed user, address indexed asset, uint256 amount, uint256 nonce);
event SpokeTopUpReceived(address indexed asset, uint256 amount);
event SpokeRefundSent(address user, address token, uint256 amount);
event HubCreditCreated(uint16 indexed chainId, bytes32 indexed user, bytes32 indexed asset, uint256 amount, uint256 nonce);
event HubCreditLost(uint16 indexed chainId, bytes32 indexed user, bytes32 indexed asset, uint256 amount, uint256 nonce);
event HubCreditRefundable(uint16 indexed chainId, bytes32 indexed user, bytes32 indexed asset, uint256 amount, uint256 nonce);
event HubCreditFinalized(uint16 indexed chainId, uint256 nonce);
event PossibleHubMisconfiguration(address assetSentAsWeth, address realWeth);
event AccountPaired(uint16 indexed chainId, bytes32 indexed account, bytes32 indexed userId);
event AccountPairingRequestReceived(uint16 indexed chainId, bytes32 indexed account, bytes32 indexed userId);
}
文件 7 的 33:HubSpokeStructs.sol
pragma solidity ^0.8.0;
import {ILiquidationCalculator} from "../interfaces/ILiquidationCalculator.sol";
import {IHubPriceUtilities} from "../interfaces/IHubPriceUtilities.sol";
import {IAssetRegistry} from "../interfaces/IAssetRegistry.sol";
import {ILegacyAssetRegistry} from "../interfaces/ILegacyAssetRegistry.sol";
import {IWormholeTunnel} from "../interfaces/IWormholeTunnel.sol";
import {IMoneyMarketRewardsDistributor} from "../interfaces/rewards/IMoneyMarketRewardsDistributor.sol";
library HubSpokeStructs {
struct ConstructorArgs {
uint256 interestAccrualIndexPrecision;
uint256 liquidationFee;
uint256 liquidationFeePrecision;
IWormholeTunnel wormholeTunnel;
}
struct StoredVaultAmount {
DenormalizedVaultAmount amounts;
AccrualIndices accrualIndices;
}
struct DenormalizedVaultAmount {
uint256 deposited;
uint256 borrowed;
}
struct NotionalVaultAmount {
uint256 deposited;
uint256 borrowed;
}
struct AccrualIndices {
uint256 deposited;
uint256 borrowed;
}
struct UserActionPayload {
bytes32 user;
Action action;
bytes32 token;
uint256 amount;
uint256 nonce;
}
struct ConfirmCreditPayload {
Credit credit;
}
struct ReleaseFundsPayload {
bytes32 user;
bytes32 token;
uint256 amount;
uint256 nonce;
bool unwrapWeth;
}
struct ConfirmTopUpPayload {
bytes32 token;
uint256 amount;
}
struct ConfirmFixLostCreditPayload {
bytes32 token;
uint256 amount;
}
struct FinalizeCreditPayload {
Credit credit;
}
struct RequestPairingPayload {
bytes32 newAccount;
bytes32 userId;
}
enum Action {
Deposit,
Borrow,
Withdraw,
Repay,
DepositNative,
RepayNative,
WithdrawNative,
BorrowNative
}
enum ActionDirection {
Inbound,
Outbound
}
enum CreditStatus {
PENDING,
CONFIRMED,
REFUNDABLE,
FINALIZED,
REFUNDED,
LOST
}
struct Credit {
bytes32 user;
bytes32 token;
uint256 creditedAmount;
uint256 _deprecated_forwardedAmount;
uint256 nonce;
uint256 createdAt;
uint256 updatedAt;
CreditStatus status;
}
struct SpokeBalances {
uint256 creditGiven;
uint256 creditLost;
uint256 unlocksPending;
uint256 creditLimit;
uint256 custodyLimit;
uint256 transactionLimit;
uint256 deposits;
uint256 lastUpdated;
}
struct SpokeFundRelease {
uint256 nonce;
bytes32 user;
bytes32 token;
uint256 amount;
uint256 releasedAt;
}
struct HubSpokeBalances {
uint256 finalized;
uint256 unfinalized;
}
struct SpokeState {
bytes32 spoke;
bytes32 wrappedNativeAsset;
mapping(bytes32 => HubSpokeBalances) balances;
mapping(bytes32 => mapping(uint256 => Credit)) credits;
mapping(bytes32 => uint256) maxNonces;
uint256[50] __gap;
}
struct HubState {
uint8 _deprecated_consistencyLevel;
mapping(address => mapping(address => HubSpokeStructs.StoredVaultAmount)) _deprecated_vault;
mapping(address => HubSpokeStructs.StoredVaultAmount) _deprecated_totalAssets;
mapping(address => HubSpokeStructs.AccrualIndices) _deprecated_indices;
mapping(address => uint256) _deprecated_lastActivityBlockTimestamps;
uint256 _deprecated_interestAccrualIndexPrecision;
ILiquidationCalculator _deprecated_liquidationCalculator;
IHubPriceUtilities _deprecated_priceUtilities;
ILegacyAssetRegistry _deprecated_assetRegistry;
uint256 _deprecated_liquidationFee;
uint256 _deprecated_defaultGasLimit;
uint256 _deprecated_refundGasLimit;
bool _deprecated_isUsingCCTP;
uint256 _deprecated_liquidationFeePrecision;
mapping(uint16 => SpokeState) _deprecated_spokeStates;
IWormholeTunnel _deprecated_wormholeTunnel;
mapping(address => HubSpokeBalances) _deprecated_wrappedTokenSpokeBalances;
IMoneyMarketRewardsDistributor _deprecated_rewardDistributor;
}
struct AssetState {
StoredVaultAmount totals;
AccrualIndices indices;
uint256 lastActivityBlockTimestamp;
mapping(address => HubSpokeStructs.StoredVaultAmount) userVaults;
}
struct UserIdState {
mapping(bytes32 => mapping(uint16 => bytes32)) spokeChainAddresses;
mapping(uint16 => mapping(bytes32 => bytes32)) userIds;
mapping(uint16 => mapping(bytes32 => bytes32)) pairingRequests;
}
struct FeesLimitsAndPrecisionsState {
uint256 interestAccrualIndexPrecision;
uint256 liquidationFee;
uint256 defaultGasLimit;
uint256 refundGasLimit;
uint256 liquidationFeePrecision;
}
struct AuxilaryContracts {
ILiquidationCalculator liquidationCalculator;
IHubPriceUtilities priceUtilities;
IAssetRegistry assetRegistry;
IWormholeTunnel wormholeTunnel;
IMoneyMarketRewardsDistributor rewardDistributor;
}
struct SpokeCommunicationState {
IWormholeTunnel wormholeTunnel;
uint16 hubChainId;
bytes32 hubContractAddress;
uint256 defaultGasLimitRoundtrip;
uint256[50] __gap;
}
struct SpokeOptimisticFinalityState {
uint256 inboundTokenInstantMessageFee;
uint256 outboundTokenInstantMessageFee;
mapping(bytes32 => HubSpokeStructs.SpokeBalances) tokenBalances;
mapping(address => mapping(uint256 => HubSpokeStructs.Credit)) storedCredits;
mapping(address => mapping(uint256 => HubSpokeStructs.SpokeFundRelease)) fundReleases;
mapping(address => uint256) lastInstantActionNonces;
uint256 avgTransactionsPerTopUp;
uint256[50] __gap;
}
}
文件 8 的 33:IAssetRegistry.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IWETH} from "@wormhole/interfaces/IWETH.sol";
import "../contracts/HubSpokeStructs.sol";
interface IAssetRegistry {
struct AssetInfo {
uint256 collateralizationRatioDeposit;
uint256 collateralizationRatioBorrow;
uint8 decimals;
address interestRateCalculator;
bool exists;
uint256 borrowLimit;
uint256 supplyLimit;
uint256 maxLiquidationBonus;
uint256[50] __gap;
}
function registerAsset(
string memory assetName,
uint8 decimals,
uint256 collateralizationRatioDeposit,
uint256 collateralizationRatioBorrow,
address interestRateCalculator,
uint256 maxLiquidationBonus,
uint256 supplyLimit,
uint256 borrowLimit
) external;
function PROTOCOL_MAX_DECIMALS() external pure returns (uint8);
function COLLATERALIZATION_RATIO_PRECISION() external pure returns (uint256);
function LIQUIDATION_BONUS_PRECISION() external pure returns (uint256);
function deregisterAsset(string memory _name) external;
function getAssetId(string memory _name) external pure returns (bytes32);
function getAssetId(uint16 _chainId, bytes32 _address) external view returns (bytes32);
function getAssetName(bytes32 _assetId) external view returns (string memory);
function getAssetName(uint16 _chainId, bytes32 _address) external view returns (string memory);
function assetExists(string memory _name) external view returns (bool);
function assetExists(bytes32 _id) external view returns (bool);
function getAssetInfo(string memory _name) external view returns (AssetInfo memory);
function getAssetInfo(bytes32 _id) external view returns (AssetInfo memory);
function getAssetAddress(string memory _name, uint16 _chainId) external view returns (bytes32);
function getAssetAddress(bytes32 _id, uint16 _chainId) external view returns (bytes32);
function requireAssetAddress(bytes32 _id, uint16 _chainId) external view returns (bytes32);
function getRegisteredAssets() external view returns (bytes32[] memory);
function getSupportedChains() external view returns (uint16[] memory);
function WETH() external view returns (IWETH);
function setCollateralizationRatios(string memory _name, uint256 _deposit, uint256 _borrow) external;
function setCollateralizationRatios(bytes32 _id, uint256 _deposit, uint256 _borrow) external;
function setLimits(string memory _name, uint256 _deposit, uint256 _borrow) external;
function setLimits(bytes32 _id, uint256 _deposit, uint256 _borrow) external;
function setMaxLiquidationBonus(string memory _name, uint256 _bonus) external;
function setMaxLiquidationBonus(bytes32 _id, uint256 _bonus) external;
function setInterestRateCalculator(string memory _name, address _calculator) external;
function setInterestRateCalculator(bytes32 _id, address _calculator) external;
function bindAsset(string memory _name, uint16 _chainId, bytes32 _address) external;
function bindAsset(bytes32 _id, uint16 _chainId, bytes32 _address) external;
function bindAssets(string memory _name, uint16[] calldata _chains, bytes32[] calldata _addresses) external;
function bindAssets(bytes32 _id, uint16[] calldata _chains, bytes32[] calldata _addresses) external;
function unbindAsset(string memory _name, uint16 _chainId) external;
function unbindAsset(bytes32 _id, uint16 _chainId) external;
function unbindAsset(uint16 _chainId, bytes32 _address) external;
}
文件 9 的 33: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);
}
文件 10 的 33:IERC20Permit.sol
pragma solidity ^0.8.20;
interface IERC20Permit {
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
function nonces(address owner) external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
文件 11 的 33:IHub.sol
pragma solidity ^0.8.0;
import "../contracts/HubSpokeStructs.sol";
import "./ILiquidationCalculator.sol";
import "./IHubPriceUtilities.sol";
import "./IAssetRegistry.sol";
import {IWormholeTunnel} from "./IWormholeTunnel.sol";
interface IHub {
function checkVaultHasAssets(address vault, bytes32 assetId, uint256 normalizedAmount)
external
view;
function checkProtocolGloballyHasAssets(
bytes32 assetId,
uint256 normalizedAmount
) external view;
function checkProtocolGloballyHasAssets(
bytes32 assetId,
uint256 normalizedAmount,
uint256 borrowLimit
) external view;
function getInterestAccrualIndices(bytes32 assetId)
external
view
returns (HubSpokeStructs.AccrualIndices memory);
function getInterestAccrualIndexPrecision() external view returns (uint256);
function getVaultAmounts(address vaultOwner, bytes32 assetId)
external
view
returns (HubSpokeStructs.DenormalizedVaultAmount memory);
function getCurrentAccrualIndices(bytes32 assetId)
external
view
returns (HubSpokeStructs.AccrualIndices memory);
function updateAccrualIndices(bytes32 assetId) external;
function getLastActivityBlockTimestamp(bytes32 assetId) external view returns (uint256);
function getGlobalAmounts(bytes32 assetId) external view returns (HubSpokeStructs.DenormalizedVaultAmount memory);
function getReserveAmount(bytes32 assetId) external view returns (uint256);
function getSpokeBalances(uint16 chainId, bytes32 homeAddress) external view returns (HubSpokeStructs.HubSpokeBalances memory);
function getSpokeBalances(bytes32 assetId) external view returns (HubSpokeStructs.HubSpokeBalances memory);
function getLiquidationCalculator() external view returns (ILiquidationCalculator);
function getPriceUtilities() external view returns (IHubPriceUtilities);
function getAssetRegistry() external view returns (IAssetRegistry);
function getWormholeTunnel() external view returns (IWormholeTunnel);
function getLiquidationFeeAndPrecision() external view returns (uint256, uint256);
function liquidation(ILiquidationCalculator.LiquidationInput memory input) external;
function userActions(HubSpokeStructs.Action action, bytes32 asset, uint256 amount) external payable;
function confirmPairingRequest(uint16 _chainId, bytes32 _account) external;
function userActionMessage(
IWormholeTunnel.MessageSource calldata source,
IERC20 token,
uint256 amount,
bytes calldata payload
) external payable;
function instantActionMessage(
IWormholeTunnel.MessageSource calldata source,
IERC20 token,
uint256 amount,
bytes calldata payload
) external;
function finalizeCreditMessage(
IWormholeTunnel.MessageSource calldata source,
IERC20 token,
uint256 amount,
bytes calldata payload
) external;
function confirmTopUpMessage(
IWormholeTunnel.MessageSource calldata source,
IERC20 token,
uint256 amount,
bytes calldata payload
) external;
function confirmFixLostCreditMessage(
IWormholeTunnel.MessageSource calldata source,
IERC20 token,
uint256 amount,
bytes calldata payload
) external;
function pairingRequestMessage(
IWormholeTunnel.MessageSource calldata source,
IERC20 token,
uint256 amount,
bytes calldata payload
) external;
}
文件 12 的 33:IHubPriceUtilities.sol
pragma solidity ^0.8.0;
import "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
import "./IHub.sol";
import "./IAssetRegistry.sol";
import "./ISynonymPriceOracle.sol";
import "../contracts/HubSpokeStructs.sol";
interface IHubPriceUtilities {
function getAssetRegistry() external view returns (IAssetRegistry);
function getPrices(bytes32 asset) external view returns (uint256, uint256, uint256, uint256);
function getVaultEffectiveNotionals(address vaultOwner, bool collateralizationRatios) external view returns (HubSpokeStructs.NotionalVaultAmount memory);
function calculateNotionals(bytes32 asset, HubSpokeStructs.DenormalizedVaultAmount memory vaultAmount) external view returns (HubSpokeStructs.NotionalVaultAmount memory);
function calculateEffectiveNotionals(bytes32 asset, HubSpokeStructs.DenormalizedVaultAmount memory vaultAmount) external view returns (HubSpokeStructs.NotionalVaultAmount memory);
function invertNotionals(bytes32 asset, HubSpokeStructs.NotionalVaultAmount memory realValues) external view returns (HubSpokeStructs.DenormalizedVaultAmount memory);
function applyCollateralizationRatios(bytes32 asset, HubSpokeStructs.NotionalVaultAmount memory vaultAmount) external view returns (HubSpokeStructs.NotionalVaultAmount memory);
function removeCollateralizationRatios(bytes32 asset, HubSpokeStructs.NotionalVaultAmount memory vaultAmount) external view returns (HubSpokeStructs.NotionalVaultAmount memory);
function getHub() external view returns (IHub);
function setHub(IHub _hub) external;
function getPriceOracle() external view returns (ISynonymPriceOracle);
function setPriceOracle(ISynonymPriceOracle _priceOracle) external;
function getPriceStandardDeviations() external view returns (uint256, uint256);
function setPriceStandardDeviations(uint256 _priceStandardDeviations, uint256 _precision) external;
}
文件 13 的 33:ILegacyAssetRegistry.sol
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IWETH} from "@wormhole/interfaces/IWETH.sol";
import "../contracts/HubSpokeStructs.sol";
interface ILegacyAssetRegistry {
struct AssetInfo {
uint256 collateralizationRatioDeposit;
uint256 collateralizationRatioBorrow;
uint8 decimals;
address interestRateCalculator;
bool exists;
uint256 borrowLimit;
uint256 supplyLimit;
uint256 maxLiquidationPortion;
uint256 maxLiquidationBonus;
}
function registerAsset(
address assetAddress,
uint256 collateralizationRatioDeposit,
uint256 collateralizationRatioBorrow,
address interestRateCalculator,
uint256 maxLiquidationPortion,
uint256 maxLiquidationBonus
) external;
function getAssetInfo(address assetAddress) external view returns (AssetInfo memory);
function setAssetParams(
address assetAddress,
uint256 borrowLimit,
uint256 supplyLimit,
uint256 maxLiquidationPortion,
uint256 maxLiquidationBonus,
address interestRateCalculatorAddress
) external;
function setCollateralizationRatios(address _asset, uint256 _deposit, uint256 _borrow) external;
function getRegisteredAssets() external view returns (address[] memory);
function getCollateralizationRatioPrecision() external view returns (uint256);
function getMaxLiquidationPortionPrecision() external view returns (uint256);
function WETH() external view returns (IWETH);
function getMaxDecimals() external view returns (uint8);
}
文件 14 的 33:ILiquidationCalculator.sol
pragma solidity ^0.8.0;
import "../contracts/HubSpokeStructs.sol";
interface ILiquidationCalculator {
enum RepaymentMethod {
TOKEN_TRANSFER,
FROM_DEPOSIT,
DEBT_TAKEOVER
}
enum PaymentMethod {
TOKEN_TRANSFER,
DEPOSIT_TAKEOVER
}
struct DenormalizedLiquidationAsset {
bytes32 assetId;
uint256 repaidAmount;
uint256 receivedAmount;
RepaymentMethod repaymentMethod;
PaymentMethod paymentMethod;
}
struct LiquidationInput {
address vault;
DenormalizedLiquidationAsset[] assets;
}
function getMaxHealthFactor() external view returns (uint256, uint256);
}
文件 15 的 33:IMoneyMarketRewardsDistributor.sol
pragma solidity ^0.8.0;
import {IHub} from "../IHub.sol";
interface IMoneyMarketRewardsDistributor {
event MarketSupportAdded(bytes32 indexed token);
event MarketSupportRemoved(bytes32 indexed token);
event UserSharesMigrated(address indexed user, bytes32 indexed pool);
function handleBalanceChange(address _user, bytes32 _assetId) external;
function migrateShares(address _user, bytes32 _assetId) external;
}
文件 16 的 33:IPyth.sol
pragma solidity ^0.8.0;
import "./PythStructs.sol";
import "./IPythEvents.sol";
interface IPyth is IPythEvents {
function getValidTimePeriod() external view returns (uint validTimePeriod);
function getPrice(
bytes32 id
) external view returns (PythStructs.Price memory price);
function getEmaPrice(
bytes32 id
) external view returns (PythStructs.Price memory price);
function getPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
function getPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (PythStructs.Price memory price);
function getEmaPriceUnsafe(
bytes32 id
) external view returns (PythStructs.Price memory price);
function getEmaPriceNoOlderThan(
bytes32 id,
uint age
) external view returns (PythStructs.Price memory price);
function updatePriceFeeds(bytes[] calldata updateData) external payable;
function updatePriceFeedsIfNecessary(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64[] calldata publishTimes
) external payable;
function getUpdateFee(
bytes[] calldata updateData
) external view returns (uint feeAmount);
function parsePriceFeedUpdates(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
function parsePriceFeedUpdatesUnique(
bytes[] calldata updateData,
bytes32[] calldata priceIds,
uint64 minPublishTime,
uint64 maxPublishTime
) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
}
文件 17 的 33:IPythEvents.sol
pragma solidity ^0.8.0;
interface IPythEvents {
event PriceFeedUpdate(
bytes32 indexed id,
uint64 publishTime,
int64 price,
uint64 conf
);
event BatchPriceFeedUpdate(uint16 chainId, uint64 sequenceNumber);
}
文件 18 的 33:ISpoke.sol
pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IWormholeTunnel} from "./IWormholeTunnel.sol";
interface ISpoke {
error CreditLimitExceeded();
error CustodyLimitExceeded();
error FundsAlreadyReleased();
error InsufficientMsgValue();
error InsufficientFunds();
error InvalidAction();
error InvalidAmount();
error InvalidCostForReturnDeliveryLength();
error InvalidDeliveryCost();
error InvalidReleaseFundsPayload();
error OnlyHubSender();
error OnlyWormholeTunnel();
error TransactionLimitExceeded();
error TransferFailed();
error UnusedParameterMustBeZero();
function releaseFunds(
IWormholeTunnel.MessageSource calldata source,
IERC20 token,
uint256 amount,
bytes calldata payload
) external payable;
function topUp(
IWormholeTunnel.MessageSource calldata source,
IERC20 token,
uint256 amount,
bytes calldata payload
) external payable;
function confirmCredit(
IWormholeTunnel.MessageSource calldata source,
IERC20 token,
uint256 amount,
bytes calldata payload
) external payable;
function finalizeCredit(
IWormholeTunnel.MessageSource calldata source,
IERC20 token,
uint256 amount,
bytes calldata payload
) external payable;
}
文件 19 的 33:ISynonymPriceOracle.sol
pragma solidity ^0.8.0;
import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
import {ISynonymPriceSource} from "./ISynonymPriceSource.sol";
interface ISynonymPriceOracle is ISynonymPriceSource {
struct PriceSource {
ISynonymPriceSource priceSource;
uint256 maxPriceAge;
}
function sequencerUptimeFeed() external view returns (AggregatorV3Interface);
function sequencerGracePeriod() external view returns (uint256);
function getPrice(bytes32 _asset) external view returns (Price memory price);
function setPriceSource(bytes32 _asset, PriceSource memory _priceSource) external;
function removePriceSource(bytes32 _asset) external;
function getPriceSource(bytes32 _asset) external view returns (PriceSource memory);
}
文件 20 的 33:ISynonymPriceSource.sol
pragma solidity ^0.8.0;
interface ISynonymPriceSource {
error NoPriceForAsset();
error StalePrice();
struct Price {
uint256 price;
uint256 confidence;
uint256 precision;
uint256 updatedAt;
}
function getPrice(bytes32 _asset, uint256 _maxAge) external view returns (Price memory price);
function priceAvailable(bytes32 _asset) external view returns (bool);
function outputAsset() external view returns (string memory);
}
文件 21 的 33:IWETH.sol
pragma solidity ^0.8.0;
import "./IERC20.sol";
interface IWETH is IERC20 {
function deposit() external payable;
function withdraw(uint256 amount) external;
}
文件 22 的 33:IWormholeRelayer.sol
pragma solidity ^0.8.0;
struct VaaKey {
uint16 chainId;
bytes32 emitterAddress;
uint64 sequence;
}
uint8 constant VAA_KEY_TYPE = 1;
struct MessageKey {
uint8 keyType;
bytes encodedKey;
}
interface IWormholeRelayerBase {
event SendEvent(
uint64 indexed sequence,
uint256 deliveryQuote,
uint256 paymentForExtraReceiverValue
);
function getRegisteredWormholeRelayerContract(
uint16 chainId
) external view returns (bytes32);
function deliveryAttempted(
bytes32 deliveryHash
) external view returns (bool attempted);
function deliverySuccessBlock(
bytes32 deliveryHash
) external view returns (uint256 blockNumber);
function deliveryFailureBlock(
bytes32 deliveryHash
) external view returns (uint256 blockNumber);
}
interface IWormholeRelayerSend is IWormholeRelayerBase {
function sendPayloadToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit
) external payable returns (uint64 sequence);
function sendPayloadToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
uint16 refundChain,
address refundAddress
) external payable returns (uint64 sequence);
function sendVaasToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
VaaKey[] memory vaaKeys
) external payable returns (uint64 sequence);
function sendVaasToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 gasLimit,
VaaKey[] memory vaaKeys,
uint16 refundChain,
address refundAddress
) external payable returns (uint64 sequence);
function sendToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 paymentForExtraReceiverValue,
uint256 gasLimit,
uint16 refundChain,
address refundAddress,
address deliveryProviderAddress,
VaaKey[] memory vaaKeys,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
function sendToEvm(
uint16 targetChain,
address targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 paymentForExtraReceiverValue,
uint256 gasLimit,
uint16 refundChain,
address refundAddress,
address deliveryProviderAddress,
MessageKey[] memory messageKeys,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
function send(
uint16 targetChain,
bytes32 targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 paymentForExtraReceiverValue,
bytes memory encodedExecutionParameters,
uint16 refundChain,
bytes32 refundAddress,
address deliveryProviderAddress,
VaaKey[] memory vaaKeys,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
function send(
uint16 targetChain,
bytes32 targetAddress,
bytes memory payload,
uint256 receiverValue,
uint256 paymentForExtraReceiverValue,
bytes memory encodedExecutionParameters,
uint16 refundChain,
bytes32 refundAddress,
address deliveryProviderAddress,
MessageKey[] memory messageKeys,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
function resendToEvm(
VaaKey memory deliveryVaaKey,
uint16 targetChain,
uint256 newReceiverValue,
uint256 newGasLimit,
address newDeliveryProviderAddress
) external payable returns (uint64 sequence);
function resend(
VaaKey memory deliveryVaaKey,
uint16 targetChain,
uint256 newReceiverValue,
bytes memory newEncodedExecutionParameters,
address newDeliveryProviderAddress
) external payable returns (uint64 sequence);
function quoteEVMDeliveryPrice(
uint16 targetChain,
uint256 receiverValue,
uint256 gasLimit
)
external
view
returns (
uint256 nativePriceQuote,
uint256 targetChainRefundPerGasUnused
);
function quoteEVMDeliveryPrice(
uint16 targetChain,
uint256 receiverValue,
uint256 gasLimit,
address deliveryProviderAddress
)
external
view
returns (
uint256 nativePriceQuote,
uint256 targetChainRefundPerGasUnused
);
function quoteDeliveryPrice(
uint16 targetChain,
uint256 receiverValue,
bytes memory encodedExecutionParameters,
address deliveryProviderAddress
)
external
view
returns (uint256 nativePriceQuote, bytes memory encodedExecutionInfo);
function quoteNativeForChain(
uint16 targetChain,
uint256 currentChainAmount,
address deliveryProviderAddress
) external view returns (uint256 targetChainAmount);
function getDefaultDeliveryProvider()
external
view
returns (address deliveryProvider);
}
interface IWormholeRelayerDelivery is IWormholeRelayerBase {
enum DeliveryStatus {
SUCCESS,
RECEIVER_FAILURE
}
enum RefundStatus {
REFUND_SENT,
REFUND_FAIL,
CROSS_CHAIN_REFUND_SENT,
CROSS_CHAIN_REFUND_FAIL_PROVIDER_NOT_SUPPORTED,
CROSS_CHAIN_REFUND_FAIL_NOT_ENOUGH,
NO_REFUND_REQUESTED
}
event Delivery(
address indexed recipientContract,
uint16 indexed sourceChain,
uint64 indexed sequence,
bytes32 deliveryVaaHash,
DeliveryStatus status,
uint256 gasUsed,
RefundStatus refundStatus,
bytes additionalStatusInfo,
bytes overridesInfo
);
function deliver(
bytes[] memory encodedVMs,
bytes memory encodedDeliveryVAA,
address payable relayerRefundAddress,
bytes memory deliveryOverrides
) external payable;
}
interface IWormholeRelayer is IWormholeRelayerDelivery, IWormholeRelayerSend {}
uint256 constant RETURNDATA_TRUNCATION_THRESHOLD = 132;
error InvalidMsgValue(uint256 msgValue, uint256 totalFee);
error RequestedGasLimitTooLow();
error DeliveryProviderDoesNotSupportTargetChain(
address relayer,
uint16 chainId
);
error DeliveryProviderCannotReceivePayment();
error DeliveryProviderDoesNotSupportMessageKeyType(uint8 keyType);
error ReentrantDelivery(address msgSender, address lockedBy);
error InvalidPayloadId(uint8 parsed, uint8 expected);
error InvalidPayloadLength(uint256 received, uint256 expected);
error InvalidVaaKeyType(uint8 parsed);
error TooManyMessageKeys(uint256 numMessageKeys);
error InvalidDeliveryVaa(string reason);
error InvalidEmitter(bytes32 emitter, bytes32 registered, uint16 chainId);
error MessageKeysLengthDoesNotMatchMessagesLength(uint256 keys, uint256 vaas);
error VaaKeysDoNotMatchVaas(uint8 index);
error RequesterNotWormholeRelayer();
error TargetChainIsNotThisChain(uint16 targetChain);
error InvalidOverrideGasLimit();
error InvalidOverrideReceiverValue();
error InvalidOverrideRefundPerGasUnused();
error InsufficientRelayerFunds(uint256 msgValue, uint256 minimum);
error NotAnEvmAddress(bytes32);
文件 23 的 33:IWormholeTunnel.sol
pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IWormholeRelayer} from "@wormhole/interfaces/IWormholeRelayer.sol";
interface IWormholeTunnel {
error InsufficientMsgValue();
error InvalidCCTPConfig();
error InvalidTunnelMessage();
error InvalidVaa();
error OnlyWormholeRelayer();
error ReplayProtection();
error SameChainCallsNotSupported();
error TransferFailed();
error TunnelEndNotRegistered();
event CCTPConfigChanged(address indexed messageTransmitter, address indexed tokenMessenger, address indexed usdc);
event TunnelEndRegistered(uint16 indexed chainId, bytes32 indexed sender, bool supportsCCTP);
event CustomRelayerSet(uint16 indexed chainId, IWormholeRelayer relayerAddress);
event TargetReverted(bytes ret);
enum MessageFinality {
FINALIZED,
SAFE,
INSTANT
}
struct TunnelMessage {
MessageSource source;
MessageTarget target;
bytes32 token;
uint256 amount;
uint256 receiverValue;
MessageFinality finality;
}
struct MessageSource {
uint16 chainId;
bytes32 sender;
bytes32 refundRecipient;
}
struct MessageTarget {
uint16 chainId;
bytes32 recipient;
bytes4 selector;
bytes payload;
}
function sendEvmMessage(
TunnelMessage calldata _message,
uint256 _gasLimit
) external payable;
function sendMessage(
TunnelMessage calldata _message
) external payable;
function sendMessage(
TunnelMessage calldata _message,
bytes calldata encodedExecutionParams
) external payable;
function USDC() external view returns (IERC20);
function supportsCCTP() external view returns (bool);
function tunnelEndSupportsCCTP(uint16 _chainId) external view returns (bool);
function chainId() external view returns (uint16);
function isEvm(uint16 _chainId) external view returns (bool);
function GAS_USAGE_WITH_TOKEN() external view returns (uint256);
function GAS_USAGE_WITHOUT_TOKEN() external view returns (uint256);
function getTokenAddressOnThisChain(uint16 tokenHomeChain, bytes32 tokenHomeAddress) external view returns (address);
function getMessageCost(uint16 _targetChain, uint256 _gasLimitOnTarget, uint256 _receiverValue, bool _withTokenTransfer) external view returns (uint256 cost);
function isValidAmount(IERC20 token, uint256 amount) external view returns (bool);
function getRelayer(uint16 chainId) external view returns (IWormholeRelayer);
}
文件 24 的 33:Initializable.sol
pragma solidity ^0.8.20;
abstract contract Initializable {
struct InitializableStorage {
uint64 _initialized;
bool _initializing;
}
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
error InvalidInitialization();
error NotInitializing();
event Initialized(uint64 version);
modifier initializer() {
InitializableStorage storage $ = _getInitializableStorage();
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
modifier reinitializer(uint64 version) {
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
modifier onlyInitializing() {
_checkInitializing();
_;
}
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
function _disableInitializers() internal virtual {
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}
文件 25 的 33:OwnableUpgradeable.sol
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
struct OwnableStorage {
address _owner;
}
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
error OwnableUnauthorizedAccount(address account);
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
modifier onlyOwner() {
_checkOwner();
_;
}
function owner() public view virtual returns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
文件 26 的 33:PausableUpgradeable.sol
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
struct PausableStorage {
bool _paused;
}
bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
function _getPausableStorage() private pure returns (PausableStorage storage $) {
assembly {
$.slot := PausableStorageLocation
}
}
event Paused(address account);
event Unpaused(address account);
error EnforcedPause();
error ExpectedPause();
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
}
modifier whenNotPaused() {
_requireNotPaused();
_;
}
modifier whenPaused() {
_requirePaused();
_;
}
function paused() public view virtual returns (bool) {
PausableStorage storage $ = _getPausableStorage();
return $._paused;
}
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
function _pause() internal virtual whenNotPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = true;
emit Paused(_msgSender());
}
function _unpause() internal virtual whenPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
emit Unpaused(_msgSender());
}
}
文件 27 的 33:PythStructs.sol
pragma solidity ^0.8.0;
contract PythStructs {
struct Price {
int64 price;
uint64 conf;
int32 expo;
uint publishTime;
}
struct PriceFeed {
bytes32 id;
Price price;
Price emaPrice;
}
}
文件 28 的 33:SafeERC20.sol
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
library SafeERC20 {
using Address for address;
error SafeERC20FailedOperation(address token);
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
function _callOptionalReturn(IERC20 token, bytes memory data) private {
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}
文件 29 的 33:Spoke.sol
pragma solidity ^0.8.0;
import {IWETH} from "@wormhole/interfaces/IWETH.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {ISpoke} from "../../interfaces/ISpoke.sol";
import {IHub} from "../../interfaces/IHub.sol";
import {IWormholeTunnel} from "../../interfaces/IWormholeTunnel.sol";
import {HubSpokeStructs} from "../HubSpokeStructs.sol";
import {HubSpokeEvents} from "../HubSpokeEvents.sol";
import {TunnelMessageBuilder} from "../wormhole/TunnelMessageBuilder.sol";
import {SpokeOptimisticFinalityLogic} from "../../libraries/logic/optimisticFinality/SpokeOptimisticFinalityLogic.sol";
import {CommonAccountingLogic} from "../../libraries/logic/accounting/CommonAccountingLogic.sol";
import {SpokeAccountingLogic} from "../../libraries/logic/accounting/SpokeAccountingLogic.sol";
import "@wormhole/Utils.sol";
contract Spoke is ISpoke, Initializable, OwnableUpgradeable, PausableUpgradeable, HubSpokeEvents {
using SafeERC20 for IERC20;
using SpokeOptimisticFinalityLogic for HubSpokeStructs.SpokeOptimisticFinalityState;
HubSpokeStructs.SpokeCommunicationState commState;
HubSpokeStructs.SpokeOptimisticFinalityState ofState;
IWETH public weth;
modifier onlyWormholeTunnel() {
if (msg.sender != address(commState.wormholeTunnel)) {
revert OnlyWormholeTunnel();
}
_;
}
modifier onlyHubSender(IWormholeTunnel.MessageSource calldata source) {
if (source.sender != commState.hubContractAddress || source.chainId != commState.hubChainId) {
revert OnlyHubSender();
}
_;
}
function initialize(
uint16 _hubChainId,
address _hubContractAddress,
IWormholeTunnel _tunnel,
IWETH _weth
) public initializer {
OwnableUpgradeable.__Ownable_init(msg.sender);
PausableUpgradeable.__Pausable_init();
commState.hubChainId = _hubChainId;
commState.hubContractAddress = toWormholeFormat(_hubContractAddress);
commState.wormholeTunnel = _tunnel;
commState.defaultGasLimitRoundtrip = 5_000_000;
ofState.avgTransactionsPerTopUp = 10;
weth = _weth;
}
function setDefaultGasLimitRoundtrip(uint256 value) external onlyOwner {
commState.defaultGasLimitRoundtrip = value;
}
function getCommState() public view returns (HubSpokeStructs.SpokeCommunicationState memory) {
return commState;
}
function setLimits(address _token, uint256 _creditLimit, uint256 _custodyLimit, uint256 _transactionLimit) external onlyOwner {
ofState.setLimits(_token, _creditLimit, _custodyLimit, _transactionLimit);
}
function getSpokeBalances(address _token) external view returns (HubSpokeStructs.SpokeBalances memory) {
return ofState.tokenBalances[toWormholeFormat(_token)];
}
function getCredit(address _user, uint256 _nonce) external view returns (HubSpokeStructs.Credit memory) {
return ofState.storedCredits[_user][_nonce];
}
function getInstantMessageFee(HubSpokeStructs.ActionDirection _direction) external view returns (uint256) {
return _direction == HubSpokeStructs.ActionDirection.Inbound ? ofState.inboundTokenInstantMessageFee : ofState.outboundTokenInstantMessageFee;
}
function getLastUserActionNonce(address _user) external view returns (uint256) {
return ofState.lastInstantActionNonces[_user];
}
function defaultGasLimitRoundtrip() external view returns (uint256) {
return commState.defaultGasLimitRoundtrip;
}
function setInstantMessageFees(uint256 _inboundTokenInstantMessageFee, uint256 _outboundTokenInstantMessageFee) external onlyOwner {
ofState.setInstantMessageFees(_inboundTokenInstantMessageFee, _outboundTokenInstantMessageFee);
}
function setHub(uint16 _hubChainId, address _hubContractAddress) external onlyOwner {
commState.hubChainId = _hubChainId;
commState.hubContractAddress = toWormholeFormat(_hubContractAddress);
}
function setWormholeTunnel(IWormholeTunnel _tunnel) external onlyOwner {
commState.wormholeTunnel = _tunnel;
}
function _checkAndConvertNativeOutboundAsset(HubSpokeStructs.Action action, IERC20 asset) internal view returns (IERC20) {
if (action == HubSpokeStructs.Action.BorrowNative || action == HubSpokeStructs.Action.WithdrawNative) {
if (address(asset) != address(0)) {
revert UnusedParameterMustBeZero();
}
asset = IERC20(address(weth));
}
return asset;
}
function userActions(HubSpokeStructs.Action action, IERC20 asset, uint256 amount, uint256 costForReturnDelivery) external payable {
asset = _checkAndConvertNativeOutboundAsset(action, asset);
if (!_isTokenSend(action)) {
uint256[] memory returnCosts = new uint256[](1);
returnCosts[0] = costForReturnDelivery;
SpokeOptimisticFinalityLogic.handleInstantAction(ofState, commState, weth, action, address(asset), amount, returnCosts);
return;
}
if (costForReturnDelivery > 0) {
revert InvalidDeliveryCost();
}
uint256 totalCost = getFullFinalityDepositRepayCost();
if (msg.value < totalCost) {
revert InsufficientMsgValue();
}
uint256 valueToSend = msg.value;
address assetAddress = address(asset);
(action, assetAddress, amount, valueToSend) = CommonAccountingLogic.handleInboundTokensAndAdjustAction(action, assetAddress, amount, weth, totalCost);
HubSpokeStructs.SpokeBalances storage balances = ofState.tokenBalances[toWormholeFormat(assetAddress)];
if (balances.deposits + amount > balances.custodyLimit) {
revert CustodyLimitExceeded();
}
balances.deposits += amount;
if (amount == 0) {
revert InvalidAmount();
}
IWormholeTunnel.TunnelMessage memory message;
message.source.refundRecipient = toWormholeFormat(msg.sender);
message.source.sender = toWormholeFormat(address(this));
message.target.chainId = commState.hubChainId;
message.target.recipient = commState.hubContractAddress;
message.target.selector = IHub.userActionMessage.selector;
message.target.payload = abi.encode(HubSpokeStructs.UserActionPayload({
user: toWormholeFormat(msg.sender),
action: action,
token: toWormholeFormat(address(assetAddress)),
amount: amount,
nonce: 0
}));
commState.wormholeTunnel.sendEvmMessage{value: valueToSend}(
message,
commState.defaultGasLimitRoundtrip
);
}
function instantActions(
HubSpokeStructs.Action action,
IERC20 asset,
uint256 amount,
uint256[] calldata costForReturnDelivery
) external payable {
asset = _checkAndConvertNativeOutboundAsset(action, asset);
SpokeOptimisticFinalityLogic.handleInstantAction(ofState, commState, weth, action, address(asset), amount, costForReturnDelivery);
}
function requestPairing(bytes32 userId) external payable {
SpokeAccountingLogic.handlePairingRequest(commState, userId);
}
function getPairingCost() public view returns (uint256) {
return SpokeAccountingLogic.getPairingCost(commState);
}
function getFullFinalityDepositRepayCost()
public
view
returns (uint256)
{
return commState.wormholeTunnel.getMessageCost(
commState.hubChainId,
commState.defaultGasLimitRoundtrip,
0,
false
);
}
function getInstantActionDeliveryCosts(HubSpokeStructs.Action, uint256[] calldata returnCosts) public view returns (uint256 total, uint256[] memory costs) {
return SpokeOptimisticFinalityLogic.getInstantActionDeliveryCosts(commState, returnCosts);
}
function getReserveAmount(address asset) public view returns (uint256) {
return SpokeAccountingLogic.getReserveAmount(ofState, asset);
}
function withdrawReserves(address asset, uint256 amount, address recipient) external onlyOwner {
SpokeAccountingLogic.withdrawReserves(ofState, asset, amount, recipient);
}
function _isTokenSend(HubSpokeStructs.Action action) internal pure returns (bool) {
return action == HubSpokeStructs.Action.Deposit || action == HubSpokeStructs.Action.Repay || action == HubSpokeStructs.Action.DepositNative || action == HubSpokeStructs.Action.RepayNative;
}
function releaseFunds(
IWormholeTunnel.MessageSource calldata source,
IERC20,
uint256,
bytes calldata payload
) external payable onlyWormholeTunnel onlyHubSender(source) {
SpokeOptimisticFinalityLogic.handleReleaseFunds(ofState, weth, payload);
}
function topUp(
IWormholeTunnel.MessageSource calldata source,
IERC20 token,
uint256 amount,
bytes calldata
) external payable onlyWormholeTunnel onlyHubSender(source) {
SpokeOptimisticFinalityLogic.handleTopUp(ofState, commState, source, token, amount);
}
function confirmCredit(
IWormholeTunnel.MessageSource calldata source,
IERC20,
uint256,
bytes calldata payload
) external payable onlyWormholeTunnel onlyHubSender(source) {
SpokeOptimisticFinalityLogic.handleConfirmCredit(ofState, payload);
}
function finalizeCredit(
IWormholeTunnel.MessageSource calldata source,
IERC20,
uint256,
bytes calldata payload
) external payable onlyWormholeTunnel onlyHubSender(source) {
SpokeOptimisticFinalityLogic.handleFinalizeCredit(ofState, payload);
}
function fixLostCredit(IERC20 token, uint256 amount, bool fromReserves) external payable onlyOwner {
SpokeOptimisticFinalityLogic.handleFixLostCredit(ofState, commState, token, amount, fromReserves);
}
function refundCredit(address _user, uint256 _nonce) external onlyOwner {
SpokeOptimisticFinalityLogic.handleRefundCredit(ofState, _user, _nonce);
}
function overrideBalances(address token, uint256 creditGiven, uint256 unlocksPending, uint256 deposits, uint256 creditLost) external onlyOwner {
HubSpokeStructs.SpokeBalances storage balance = ofState.tokenBalances[toWormholeFormat(token)];
balance.creditGiven = creditGiven;
balance.unlocksPending = unlocksPending;
balance.deposits = deposits;
balance.creditLost = creditLost;
}
function refundFailedDeposit(address _user, address _token, uint256 _amount) external onlyOwner {
HubSpokeStructs.SpokeBalances storage balance = ofState.tokenBalances[toWormholeFormat(_token)];
IERC20 tokenE20 = IERC20(_token);
if (balance.deposits < _amount || tokenE20.balanceOf(address(this)) < _amount) {
revert InsufficientFunds();
}
balance.deposits -= _amount;
tokenE20.safeTransfer(_user, _amount);
emit SpokeRefundSent(_user, _token, _amount);
}
function pause() external onlyOwner {
_pause();
}
function unpause() external onlyOwner {
_unpause();
}
fallback() external payable {}
receive() external payable {}
}
文件 30 的 33:SpokeAccountingLogic.sol
pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@wormhole/Utils.sol";
import {IWormholeTunnel} from "../../../interfaces/IWormholeTunnel.sol";
import {IHub} from "../../../interfaces/IHub.sol";
import {HubSpokeStructs} from "../../../contracts/HubSpokeStructs.sol";
library SpokeAccountingLogic {
using SafeERC20 for IERC20;
uint256 public constant REQUEST_PAIRING_GAS_LIMIT = 100_000;
error InsufficientFunds();
error InsufficientMsgValue();
error TransferFailed();
error ZeroAddress();
event ReservesWithdrawn(address indexed asset, uint256 amount, address destination);
function getReserveAmount(
HubSpokeStructs.SpokeOptimisticFinalityState storage ofState,
address asset
) public view returns (uint256) {
HubSpokeStructs.SpokeBalances storage balance = ofState.tokenBalances[toWormholeFormat(address(asset))];
return IERC20(asset).balanceOf(address(this)) - balance.deposits - balance.creditGiven;
}
function withdrawReserves(
HubSpokeStructs.SpokeOptimisticFinalityState storage ofState,
address asset,
uint256 amount,
address recipient
) public {
if (recipient == address(0)) {
revert ZeroAddress();
}
if (asset == address(0)) {
if (address(this).balance < amount) {
revert InsufficientFunds();
}
(bool success,) = payable(recipient).call{value: amount}("");
if (!success) {
revert TransferFailed();
}
} else {
if (amount > getReserveAmount(ofState, asset)) {
revert InsufficientFunds();
}
IERC20(asset).safeTransfer(recipient, amount);
}
emit ReservesWithdrawn(asset, amount, recipient);
}
function getPairingCost(
HubSpokeStructs.SpokeCommunicationState storage commState
) public view returns (uint256) {
return commState.wormholeTunnel.getMessageCost(
commState.hubChainId,
REQUEST_PAIRING_GAS_LIMIT,
0,
false
);
}
function handlePairingRequest(
HubSpokeStructs.SpokeCommunicationState storage commState,
bytes32 userId
) public {
uint256 cost = getPairingCost(commState);
if (msg.value < cost) {
revert InsufficientMsgValue();
}
bytes32 senderWhFormat = toWormholeFormat(msg.sender);
IWormholeTunnel.TunnelMessage memory message;
message.source.refundRecipient = senderWhFormat;
message.source.sender = toWormholeFormat(address(this));
message.source.chainId = commState.wormholeTunnel.chainId();
message.target.chainId = commState.hubChainId;
message.target.recipient = commState.hubContractAddress;
message.target.selector = IHub.pairingRequestMessage.selector;
message.target.payload = abi.encode(HubSpokeStructs.RequestPairingPayload({
newAccount: senderWhFormat,
userId: userId
}));
message.finality = IWormholeTunnel.MessageFinality.INSTANT;
commState.wormholeTunnel.sendEvmMessage{value: cost}(
message,
REQUEST_PAIRING_GAS_LIMIT
);
}
}
文件 31 的 33:SpokeOptimisticFinalityLogic.sol
pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IWormholeTunnel} from "../../../interfaces/IWormholeTunnel.sol";
import {ISpoke} from "../../../interfaces/ISpoke.sol";
import {IHub} from "../../../interfaces/IHub.sol";
import {IWETH} from "@wormhole/interfaces/IWETH.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {HubSpokeStructs} from "../../../contracts/HubSpokeStructs.sol";
import {HubSpokeEvents} from "../../../contracts/HubSpokeEvents.sol";
import "@wormhole/Utils.sol";
import {CommonAccountingLogic} from "../accounting/CommonAccountingLogic.sol";
import {SpokeAccountingLogic} from "../accounting/SpokeAccountingLogic.sol";
import {CommonOptimisticFinalityLogic} from "./CommonOptimisticFinalityLogic.sol";
library SpokeOptimisticFinalityLogic {
using SafeERC20 for IERC20;
event SpokeCreditCreated(address indexed user, address indexed asset, uint256 amount, uint256 nonce);
event SpokeCreditLost(address indexed user, address indexed asset, uint256 amount, uint256 nonce);
event SpokeCreditRefundable(address indexed user, address indexed asset, uint256 amount, uint256 nonce);
event SpokeCreditConfirmed(uint256 nonce);
event SpokeCreditFinalized(uint256 nonce);
event SpokeCreditRefunded(address indexed user, address indexed asset, uint256 amount, uint256 nonce);
event SpokeLimitsChanged(address indexed asset, uint256 creditLimit, uint256 custodyLimit, uint256 transactionLimit);
event SpokeFeesChanged(uint256 inboundTokenFee, uint256 outboundTokenFee);
event SpokeFundsReleased(address indexed user, address indexed asset, uint256 amount, uint256 nonce);
event SpokeTopUpReceived(address indexed asset, uint256 amount);
event PossibleHubMisconfiguration(address assetSentAsWeth, address realWeth);
error CreditAlreadyFinalized();
error CreditLimitExceeded();
error CustodyLimitExceeded();
error FundsAlreadyReleased();
error InsufficientFunds();
error InsufficientMsgValue();
error InsufficientSpokeBalance();
error InvalidAction();
error InvalidAmount();
error InvalidCostForReturnDeliveryLength();
error InvalidReleaseFundsPayload();
error TransactionLimitExceeded();
error TransferFailed();
error UnexpectedTokenReceived();
function SPOKE_TOP_UP_GAS_LIMIT() public pure returns (uint256) {
return 500_000;
}
function INSTANT_MESSAGE_FEE_PRECISION() public pure returns (uint256) {
return 1e18;
}
function HUB_FINALIZE_CREDIT_GAS_LIMIT() public pure returns (uint256) {
return 600_000;
}
function HUB_CONFIRM_TOP_UP_GAS_LIMIT() public pure returns (uint256) {
return 100_000;
}
function setLimits(
HubSpokeStructs.SpokeOptimisticFinalityState storage ofState,
address _token,
uint256 _creditLimit,
uint256 _custodyLimit,
uint256 _transactionLimit
) public {
HubSpokeStructs.SpokeBalances storage balance = ofState.tokenBalances[toWormholeFormat(_token)];
balance.creditLimit = _creditLimit;
balance.custodyLimit = _custodyLimit;
balance.transactionLimit = _transactionLimit;
balance.lastUpdated = block.timestamp;
emit SpokeLimitsChanged(_token, _creditLimit, _custodyLimit, _transactionLimit);
}
function setInstantMessageFees(
HubSpokeStructs.SpokeOptimisticFinalityState storage ofState,
uint256 _inboundTokenInstantMessageFee,
uint256 _outboundTokenInstantMessageFee
) public {
uint256 precision = INSTANT_MESSAGE_FEE_PRECISION();
if (_inboundTokenInstantMessageFee > precision || _outboundTokenInstantMessageFee > precision) {
revert InvalidAmount();
}
ofState.inboundTokenInstantMessageFee = _inboundTokenInstantMessageFee;
ofState.outboundTokenInstantMessageFee = _outboundTokenInstantMessageFee;
emit SpokeFeesChanged(_inboundTokenInstantMessageFee, _outboundTokenInstantMessageFee);
}
function handleInstantAction(
HubSpokeStructs.SpokeOptimisticFinalityState storage ofState,
HubSpokeStructs.SpokeCommunicationState storage commState,
IWETH weth,
HubSpokeStructs.Action action,
address asset,
uint256 amount,
uint256[] calldata costForReturnDelivery
) public {
uint256[] memory costs;
uint256 valueToSend = msg.value;
{
uint256 totalCost;
(totalCost, costs) = getInstantActionDeliveryCosts(commState, costForReturnDelivery);
if (msg.value < totalCost) {
revert InsufficientMsgValue();
}
(action, asset, amount, valueToSend) = CommonAccountingLogic.handleInboundTokensAndAdjustAction(action, asset, amount, weth, totalCost);
}
if (amount == 0 || address(asset) == address(0)) {
revert InvalidAction();
}
ofState.lastInstantActionNonces[msg.sender]++;
if (CommonOptimisticFinalityLogic.getActionDirection(action) == HubSpokeStructs.ActionDirection.Inbound) {
if (amount > ofState.tokenBalances[toWormholeFormat(address(asset))].transactionLimit) {
revert TransactionLimitExceeded();
}
if (costs.length != 2) {
revert InvalidCostForReturnDeliveryLength();
}
HubSpokeStructs.SpokeBalances storage balance = ofState.tokenBalances[toWormholeFormat(address(asset))];
amount -= amount * ofState.inboundTokenInstantMessageFee / INSTANT_MESSAGE_FEE_PRECISION();
HubSpokeStructs.Credit storage storedCredit = ofState.storedCredits[msg.sender][ofState.lastInstantActionNonces[msg.sender]];
if (
storedCredit.createdAt != 0 &&
(
storedCredit.token != toWormholeFormat(address(asset)) ||
storedCredit.creditedAmount != amount
)
) {
storedCredit.status = HubSpokeStructs.CreditStatus.LOST;
storedCredit.updatedAt = block.timestamp;
emit SpokeCreditLost(msg.sender, asset, amount, ofState.lastInstantActionNonces[msg.sender]);
return;
}
balance.unlocksPending += amount;
balance.lastUpdated = block.timestamp;
if (storedCredit.createdAt == 0) {
balance.creditGiven += amount;
if (balance.creditGiven + balance.creditLost > balance.creditLimit) {
revert CreditLimitExceeded();
}
if (balance.deposits + balance.unlocksPending > balance.custodyLimit) {
revert CustodyLimitExceeded();
}
storedCredit.user = toWormholeFormat(msg.sender);
storedCredit.token = toWormholeFormat(address(asset));
storedCredit.creditedAmount = amount;
storedCredit.nonce = ofState.lastInstantActionNonces[msg.sender];
storedCredit.createdAt = block.timestamp;
emit SpokeCreditCreated(msg.sender, address(asset), amount, storedCredit.nonce);
_sendInstantActionTunnelMessage(
ofState,
commState,
action,
storedCredit.token,
amount,
costs[0],
costForReturnDelivery[0]
);
} else {
balance.creditLost -= amount;
balance.creditGiven += amount;
}
_sendFinalizeCreditTunnelMessage(
commState,
storedCredit,
costs[1],
costForReturnDelivery[1]
);
} else {
if (costForReturnDelivery.length != 1) {
revert InvalidCostForReturnDeliveryLength();
}
if (ofState.tokenBalances[toWormholeFormat(address(asset))].deposits < amount) {
revert InsufficientFunds();
}
_sendInstantActionTunnelMessage(
ofState,
commState,
action,
toWormholeFormat(asset),
amount,
costs[0],
costForReturnDelivery[0]
);
}
}
function handleReleaseFunds(
HubSpokeStructs.SpokeOptimisticFinalityState storage ofState,
IWETH weth,
bytes calldata payload
) public {
HubSpokeStructs.ReleaseFundsPayload memory rfp = abi.decode(payload, (HubSpokeStructs.ReleaseFundsPayload));
if (rfp.token == bytes32(0) || rfp.amount == 0) {
revert InvalidReleaseFundsPayload();
}
address user = fromWormholeFormat(rfp.user);
if (ofState.fundReleases[user][rfp.nonce].amount != 0) {
revert FundsAlreadyReleased();
}
if (ofState.tokenBalances[rfp.token].deposits < rfp.amount) {
revert InsufficientFunds();
}
ofState.tokenBalances[rfp.token].deposits -= rfp.amount;
ofState.tokenBalances[rfp.token].lastUpdated = block.timestamp;
ofState.fundReleases[user][rfp.nonce] = HubSpokeStructs.SpokeFundRelease({
nonce: rfp.nonce,
user: rfp.user,
token: rfp.token,
amount: rfp.amount,
releasedAt: block.timestamp
});
if (rfp.unwrapWeth && rfp.token == toWormholeFormat(address(weth))) {
weth.withdraw(rfp.amount);
(bool success,) = fromWormholeFormat(rfp.user).call{value: rfp.amount}("");
if (!success) {
revert TransferFailed();
}
} else {
address token = fromWormholeFormat(rfp.token);
if (rfp.unwrapWeth) {
emit PossibleHubMisconfiguration(token, address(weth));
}
IERC20(token).safeTransfer(fromWormholeFormat(rfp.user), rfp.amount);
}
emit SpokeFundsReleased(fromWormholeFormat(rfp.user), fromWormholeFormat(rfp.token), rfp.amount, rfp.nonce);
}
function handleTopUp(
HubSpokeStructs.SpokeOptimisticFinalityState storage ofState,
HubSpokeStructs.SpokeCommunicationState storage commState,
IWormholeTunnel.MessageSource calldata source,
IERC20 token,
uint256 amount
) public {
token.safeTransferFrom(msg.sender, address(this), amount);
HubSpokeStructs.SpokeBalances storage balance = ofState.tokenBalances[toWormholeFormat(address(token))];
balance.deposits += amount;
balance.lastUpdated = block.timestamp;
emit SpokeTopUpReceived(address(token), amount);
IWormholeTunnel.TunnelMessage memory message;
message.source.refundRecipient = source.refundRecipient;
message.source.sender = toWormholeFormat(address(this));
message.target.chainId = commState.hubChainId;
message.target.recipient = commState.hubContractAddress;
message.target.selector = IHub.confirmTopUpMessage.selector;
message.target.payload = abi.encode(HubSpokeStructs.ConfirmTopUpPayload({
token: toWormholeFormat(address(token)),
amount: amount
}));
message.finality = IWormholeTunnel.MessageFinality.INSTANT;
uint256 cost = commState.wormholeTunnel.getMessageCost(commState.hubChainId, HUB_CONFIRM_TOP_UP_GAS_LIMIT(), 0, false);
commState.wormholeTunnel.sendEvmMessage{value: cost}(message, HUB_CONFIRM_TOP_UP_GAS_LIMIT());
}
function handleConfirmCredit(
HubSpokeStructs.SpokeOptimisticFinalityState storage ofState,
bytes calldata payload
) public {
HubSpokeStructs.ConfirmCreditPayload memory ccp = abi.decode(payload, (HubSpokeStructs.ConfirmCreditPayload));
address user = fromWormholeFormat(ccp.credit.user);
HubSpokeStructs.Credit storage storedCredit = ofState.storedCredits[user][ccp.credit.nonce];
if (CommonOptimisticFinalityLogic.creditMissingOrConflicting(ccp.credit, storedCredit)) {
address token = fromWormholeFormat(ccp.credit.token);
HubSpokeStructs.SpokeBalances storage balance = ofState.tokenBalances[ccp.credit.token];
if (storedCredit.createdAt != 0) {
ccp.credit.status = HubSpokeStructs.CreditStatus.LOST;
emit SpokeCreditLost(user, token, ccp.credit.creditedAmount, ccp.credit.nonce);
} else {
ccp.credit.status = HubSpokeStructs.CreditStatus.CONFIRMED;
emit SpokeCreditCreated(user, token, ccp.credit.creditedAmount, ccp.credit.nonce);
}
ccp.credit.createdAt = block.timestamp;
ofState.storedCredits[user][ccp.credit.nonce] = ccp.credit;
balance.creditLost += ccp.credit.creditedAmount;
balance.lastUpdated = block.timestamp;
} else {
storedCredit.status = HubSpokeStructs.CreditStatus.CONFIRMED;
}
storedCredit.updatedAt = block.timestamp;
emit SpokeCreditConfirmed(ccp.credit.nonce);
}
function handleFinalizeCredit(
HubSpokeStructs.SpokeOptimisticFinalityState storage ofState,
bytes calldata payload
) public {
HubSpokeStructs.FinalizeCreditPayload memory fcp = abi.decode(payload, (HubSpokeStructs.FinalizeCreditPayload));
address user = fromWormholeFormat(fcp.credit.user);
HubSpokeStructs.SpokeBalances storage balance = ofState.tokenBalances[fcp.credit.token];
HubSpokeStructs.Credit storage storedCredit = ofState.storedCredits[user][fcp.credit.nonce];
if (storedCredit.status == HubSpokeStructs.CreditStatus.CONFIRMED) {
storedCredit.status = HubSpokeStructs.CreditStatus.FINALIZED;
storedCredit.updatedAt = block.timestamp;
emit SpokeCreditFinalized(fcp.credit.nonce);
} else if (storedCredit.createdAt > 0 && storedCredit.status == HubSpokeStructs.CreditStatus.PENDING) {
storedCredit.status = HubSpokeStructs.CreditStatus.REFUNDABLE;
storedCredit.updatedAt = block.timestamp;
emit SpokeCreditRefundable(user, fromWormholeFormat(storedCredit.token), storedCredit.creditedAmount, storedCredit.nonce);
}
balance.creditGiven -= fcp.credit.creditedAmount;
balance.lastUpdated = block.timestamp;
balance.unlocksPending -= fcp.credit.creditedAmount;
if (ofState.storedCredits[user][fcp.credit.nonce].status == HubSpokeStructs.CreditStatus.FINALIZED) {
balance.deposits += fcp.credit.creditedAmount;
}
}
function handleFixLostCredit(
HubSpokeStructs.SpokeOptimisticFinalityState storage ofState,
HubSpokeStructs.SpokeCommunicationState storage commState,
IERC20 token,
uint256 amount,
bool fromReserves
) public {
HubSpokeStructs.SpokeBalances storage balance = ofState.tokenBalances[toWormholeFormat(address(token))];
if (balance.creditLost < amount) {
revert InvalidAmount();
}
uint256 cost = commState.wormholeTunnel.getMessageCost(commState.hubChainId, HUB_CONFIRM_TOP_UP_GAS_LIMIT(), 0, false);
if (address(this).balance < cost) {
revert InsufficientMsgValue();
}
if (!fromReserves) {
token.safeTransferFrom(msg.sender, address(this), amount);
} else if (SpokeAccountingLogic.getReserveAmount(ofState, address(token)) < amount) {
revert InsufficientFunds();
}
balance.creditLost -= amount;
balance.deposits += amount;
bytes32 spokeAddressWhFormat = toWormholeFormat(address(this));
IWormholeTunnel.TunnelMessage memory message;
message.source.refundRecipient = spokeAddressWhFormat;
message.source.sender = spokeAddressWhFormat;
message.target.chainId = commState.hubChainId;
message.target.recipient = commState.hubContractAddress;
message.target.selector = IHub.confirmFixLostCreditMessage.selector;
message.target.payload = abi.encode(HubSpokeStructs.ConfirmFixLostCreditPayload({
token: toWormholeFormat(address(token)),
amount: amount
}));
message.finality = IWormholeTunnel.MessageFinality.INSTANT;
commState.wormholeTunnel.sendEvmMessage{value: cost}(message, HUB_CONFIRM_TOP_UP_GAS_LIMIT());
}
function handleRefundCredit(
HubSpokeStructs.SpokeOptimisticFinalityState storage ofState,
address _user,
uint256 _nonce
) public {
HubSpokeStructs.Credit storage credit = ofState.storedCredits[_user][_nonce];
if (credit.status != HubSpokeStructs.CreditStatus.REFUNDABLE) {
revert InvalidAction();
}
IERC20 token = IERC20(fromWormholeFormat(credit.token));
if (SpokeAccountingLogic.getReserveAmount(ofState, address(token)) < credit.creditedAmount) {
revert InsufficientFunds();
}
credit.status = HubSpokeStructs.CreditStatus.REFUNDED;
token.safeTransfer(_user, credit.creditedAmount);
emit SpokeCreditRefunded(_user, address(token), credit.creditedAmount, _nonce);
}
function _sendFinalizeCreditTunnelMessage(
HubSpokeStructs.SpokeCommunicationState storage commState,
HubSpokeStructs.Credit storage credit,
uint256 actionCost,
uint256 returnDeliveryCost
) internal {
IWormholeTunnel.TunnelMessage memory message;
message.source.refundRecipient = message.source.refundRecipient;
message.source.sender = message.source.refundRecipient;
message.source.chainId = commState.wormholeTunnel.chainId();
message.target.chainId = commState.hubChainId;
message.target.recipient = commState.hubContractAddress;
message.target.selector = IHub.finalizeCreditMessage.selector;
message.target.payload = abi.encode(HubSpokeStructs.FinalizeCreditPayload({
credit: credit
}));
message.receiverValue = returnDeliveryCost;
commState.wormholeTunnel.sendEvmMessage{value: actionCost}(
message,
HUB_FINALIZE_CREDIT_GAS_LIMIT()
);
}
function _sendInstantActionTunnelMessage(
HubSpokeStructs.SpokeOptimisticFinalityState storage ofState,
HubSpokeStructs.SpokeCommunicationState storage commState,
HubSpokeStructs.Action action,
bytes32 asset,
uint256 amount,
uint256 actionCost,
uint256 returnDeliveryCost
) internal {
IWormholeTunnel.TunnelMessage memory message;
message.source.refundRecipient = toWormholeFormat(msg.sender);
message.source.sender = toWormholeFormat(address(this));
message.target.chainId = commState.hubChainId;
message.target.recipient = commState.hubContractAddress;
message.target.selector = IHub.instantActionMessage.selector;
message.target.payload = abi.encode(HubSpokeStructs.UserActionPayload({
user: message.source.refundRecipient,
action: action,
token: asset,
amount: amount,
nonce: ofState.lastInstantActionNonces[msg.sender]
}));
message.finality = IWormholeTunnel.MessageFinality.INSTANT;
message.receiverValue = returnDeliveryCost;
commState.wormholeTunnel.sendEvmMessage{value: actionCost}(
message,
commState.defaultGasLimitRoundtrip
);
}
function getInstantActionDeliveryCosts(
HubSpokeStructs.SpokeCommunicationState storage commState,
uint256[] calldata returnCosts
) public view returns (uint256 total, uint256[] memory costs) {
costs = new uint256[](returnCosts.length);
for (uint256 i = 0; i < returnCosts.length; i++) {
costs[i] = commState.wormholeTunnel.getMessageCost(
commState.hubChainId,
commState.defaultGasLimitRoundtrip,
returnCosts[i],
false
);
total += costs[i];
}
}
}
文件 32 的 33:TunnelMessageBuilder.sol
pragma solidity ^0.8.0;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IWormholeTunnel} from "../../interfaces/IWormholeTunnel.sol";
import "@wormhole/Utils.sol";
library TunnelMessageBuilder {
error InvalidInput();
function createMessage(
address addressFrom,
address addressTo,
uint16 targetChainId
) public pure returns (IWormholeTunnel.TunnelMessage memory message) {
return createMessage(toWormholeFormat(addressFrom), toWormholeFormat(addressTo), targetChainId);
}
function createMessage(
bytes32 addressFrom,
bytes32 addressTo,
uint16 targetChainId
) public pure returns (IWormholeTunnel.TunnelMessage memory message) {
if (addressFrom == bytes32(0) || addressTo == bytes32(0) || targetChainId == 0) {
revert InvalidInput();
}
message = IWormholeTunnel.TunnelMessage({
source: IWormholeTunnel.MessageSource({
chainId: 0,
sender: bytes32(0),
refundRecipient: addressFrom
}),
target: IWormholeTunnel.MessageTarget({
chainId: targetChainId,
recipient: addressTo,
selector: bytes4(0),
payload: bytes("")
}),
token: bytes32(0),
amount: 0,
receiverValue: 0,
finality: IWormholeTunnel.MessageFinality.FINALIZED
});
}
function addTokenTransferToMessage(IWormholeTunnel.TunnelMessage memory message, IERC20 token, uint256 amount) public pure returns (IWormholeTunnel.TunnelMessage memory) {
if (address(token) == address(0) || amount == 0) {
revert InvalidInput();
}
message.token = toWormholeFormat(address(token));
message.amount = amount;
return message;
}
function addReceiverValueToMessage(IWormholeTunnel.TunnelMessage memory message, uint256 receiverValue) public pure returns (IWormholeTunnel.TunnelMessage memory) {
if (receiverValue == 0) {
revert InvalidInput();
}
message.receiverValue = receiverValue;
return message;
}
function callEvmContract(
address from,
address contractAddress,
bytes4 selector,
bytes memory payload,
uint16 targetChainId
) public pure returns (IWormholeTunnel.TunnelMessage memory message) {
return callContract(toWormholeFormat(from), toWormholeFormat(contractAddress), selector, payload, targetChainId);
}
function callContract(
bytes32 from,
bytes32 contractAddress,
bytes4 selector,
bytes memory payload,
uint16 targetChainId
) public pure returns (IWormholeTunnel.TunnelMessage memory message) {
message = createMessage(from, contractAddress, targetChainId);
if (selector == bytes4(0)) {
revert InvalidInput();
}
message.target.selector = selector;
message.target.payload = payload;
}
function sendTokenAndEtherToAddress(
bytes32 addressFrom,
bytes32 addressTo,
uint16 targetChainId,
IERC20 token,
uint256 amount,
uint256 receiverValue
) public pure returns (IWormholeTunnel.TunnelMessage memory message) {
message = createMessage(addressFrom, addressTo, targetChainId);
bool valid = false;
if (address(token) != address(0)) {
message = addTokenTransferToMessage(message, token, amount);
valid = true;
}
if (receiverValue > 0) {
message = addReceiverValueToMessage(message, receiverValue);
valid = true;
}
if (!valid) {
revert InvalidInput();
}
}
}
文件 33 的 33:Utils.sol
pragma solidity ^0.8.13;
import "./interfaces/IWormholeRelayer.sol";
function toWormholeFormat(address addr) pure returns (bytes32) {
return bytes32(uint256(uint160(addr)));
}
function fromWormholeFormat(bytes32 whFormatAddress) pure returns (address) {
if (uint256(whFormatAddress) >> 160 != 0) {
revert NotAnEvmAddress(whFormatAddress);
}
return address(uint160(uint256(whFormatAddress)));
}
{
"compilationTarget": {
"src/contracts/lendingSpoke/Spoke.sol": "Spoke"
},
"evmVersion": "paris",
"libraries": {
"src/libraries/logic/accounting/CommonAccountingLogic.sol:CommonAccountingLogic": "0x6e0e8c78d7b894beff66ade8b27b089a53cf4d04",
"src/libraries/logic/accounting/SpokeAccountingLogic.sol:SpokeAccountingLogic": "0xf93353f929b19b82a7f01b61b02844cabf76c764",
"src/libraries/logic/optimisticFinality/SpokeOptimisticFinalityLogic.sol:SpokeOptimisticFinalityLogic": "0xe29578c5aef73b045d0baabee52b223d5cf02443"
},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 2000
},
"remappings": []
}
[{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"CreditLimitExceeded","type":"error"},{"inputs":[],"name":"CustodyLimitExceeded","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"FundsAlreadyReleased","type":"error"},{"inputs":[],"name":"InsufficientFunds","type":"error"},{"inputs":[],"name":"InsufficientMsgValue","type":"error"},{"inputs":[],"name":"InvalidAction","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"InvalidCostForReturnDeliveryLength","type":"error"},{"inputs":[],"name":"InvalidDeliveryCost","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidReleaseFundsPayload","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"OnlyHubSender","type":"error"},{"inputs":[],"name":"OnlyWormholeTunnel","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"TransactionLimitExceeded","type":"error"},{"inputs":[],"name":"TransferFailed","type":"error"},{"inputs":[],"name":"UnusedParameterMustBeZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"chainId","type":"uint16"},{"indexed":true,"internalType":"bytes32","name":"account","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"userId","type":"bytes32"}],"name":"AccountPaired","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"chainId","type":"uint16"},{"indexed":true,"internalType":"bytes32","name":"account","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"userId","type":"bytes32"}],"name":"AccountPairingRequestReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"deposit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrow","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"AccrualIndexUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":true,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vaultTotalBorrowed","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":true,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vaultTotalDeposited","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[],"name":"GlobalStateMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"chainId","type":"uint16"},{"indexed":true,"internalType":"bytes32","name":"user","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"HubCreditCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"chainId","type":"uint16"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"HubCreditFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"chainId","type":"uint16"},{"indexed":true,"internalType":"bytes32","name":"user","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"HubCreditLost","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint16","name":"chainId","type":"uint16"},{"indexed":true,"internalType":"bytes32","name":"user","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"HubCreditRefundable","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"liquidator","type":"address"},{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"components":[{"internalType":"bytes32","name":"assetId","type":"bytes32"},{"internalType":"uint256","name":"repaidAmount","type":"uint256"},{"internalType":"uint256","name":"receivedAmount","type":"uint256"},{"internalType":"enum ILiquidationCalculator.RepaymentMethod","name":"repaymentMethod","type":"uint8"},{"internalType":"enum ILiquidationCalculator.PaymentMethod","name":"paymentMethod","type":"uint8"}],"indexed":false,"internalType":"struct ILiquidationCalculator.DenormalizedLiquidationAsset[]","name":"liquidationAssets","type":"tuple[]"}],"name":"Liquidation","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"assetSentAsWeth","type":"address"},{"indexed":false,"internalType":"address","name":"realWeth","type":"address"}],"name":"PossibleHubMisconfiguration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":true,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vaultTotalBorrowed","type":"uint256"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"destinationChain","type":"uint16"},{"indexed":false,"internalType":"bytes32","name":"destinationAddress","type":"bytes32"}],"name":"ReservesWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"precision","type":"uint256"}],"name":"SetLiquidationFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"SpokeCreditConfirmed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"SpokeCreditCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"SpokeCreditFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"SpokeCreditLost","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"SpokeCreditRefundable","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"SpokeCreditRefunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"inboundTokenFee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"outboundTokenFee","type":"uint256"}],"name":"SpokeFeesChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"}],"name":"SpokeFundsReleased","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"creditLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"custodyLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"transactionLimit","type":"uint256"}],"name":"SpokeLimitsChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SpokeRefundSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"chainId","type":"uint16"},{"indexed":false,"internalType":"bytes32","name":"spoke","type":"bytes32"}],"name":"SpokeRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SpokeTopUpReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"}],"name":"UserMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"vault","type":"address"},{"indexed":true,"internalType":"bytes32","name":"asset","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"vaultTotalDeposited","type":"uint256"}],"name":"Withdraw","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"bytes32","name":"refundRecipient","type":"bytes32"}],"internalType":"struct IWormholeTunnel.MessageSource","name":"source","type":"tuple"},{"internalType":"contract IERC20","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"confirmCredit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"defaultGasLimitRoundtrip","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"bytes32","name":"refundRecipient","type":"bytes32"}],"internalType":"struct IWormholeTunnel.MessageSource","name":"source","type":"tuple"},{"internalType":"contract IERC20","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"finalizeCredit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bool","name":"fromReserves","type":"bool"}],"name":"fixLostCredit","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getCommState","outputs":[{"components":[{"internalType":"contract IWormholeTunnel","name":"wormholeTunnel","type":"address"},{"internalType":"uint16","name":"hubChainId","type":"uint16"},{"internalType":"bytes32","name":"hubContractAddress","type":"bytes32"},{"internalType":"uint256","name":"defaultGasLimitRoundtrip","type":"uint256"},{"internalType":"uint256[50]","name":"__gap","type":"uint256[50]"}],"internalType":"struct HubSpokeStructs.SpokeCommunicationState","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_nonce","type":"uint256"}],"name":"getCredit","outputs":[{"components":[{"internalType":"bytes32","name":"user","type":"bytes32"},{"internalType":"bytes32","name":"token","type":"bytes32"},{"internalType":"uint256","name":"creditedAmount","type":"uint256"},{"internalType":"uint256","name":"_deprecated_forwardedAmount","type":"uint256"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint256","name":"createdAt","type":"uint256"},{"internalType":"uint256","name":"updatedAt","type":"uint256"},{"internalType":"enum HubSpokeStructs.CreditStatus","name":"status","type":"uint8"}],"internalType":"struct HubSpokeStructs.Credit","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFullFinalityDepositRepayCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum HubSpokeStructs.Action","name":"","type":"uint8"},{"internalType":"uint256[]","name":"returnCosts","type":"uint256[]"}],"name":"getInstantActionDeliveryCosts","outputs":[{"internalType":"uint256","name":"total","type":"uint256"},{"internalType":"uint256[]","name":"costs","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum HubSpokeStructs.ActionDirection","name":"_direction","type":"uint8"}],"name":"getInstantMessageFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"}],"name":"getLastUserActionNonce","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPairingCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"getReserveAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getSpokeBalances","outputs":[{"components":[{"internalType":"uint256","name":"creditGiven","type":"uint256"},{"internalType":"uint256","name":"creditLost","type":"uint256"},{"internalType":"uint256","name":"unlocksPending","type":"uint256"},{"internalType":"uint256","name":"creditLimit","type":"uint256"},{"internalType":"uint256","name":"custodyLimit","type":"uint256"},{"internalType":"uint256","name":"transactionLimit","type":"uint256"},{"internalType":"uint256","name":"deposits","type":"uint256"},{"internalType":"uint256","name":"lastUpdated","type":"uint256"}],"internalType":"struct HubSpokeStructs.SpokeBalances","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint16","name":"_hubChainId","type":"uint16"},{"internalType":"address","name":"_hubContractAddress","type":"address"},{"internalType":"contract IWormholeTunnel","name":"_tunnel","type":"address"},{"internalType":"contract IWETH","name":"_weth","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum HubSpokeStructs.Action","name":"action","type":"uint8"},{"internalType":"contract IERC20","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256[]","name":"costForReturnDelivery","type":"uint256[]"}],"name":"instantActions","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"creditGiven","type":"uint256"},{"internalType":"uint256","name":"unlocksPending","type":"uint256"},{"internalType":"uint256","name":"deposits","type":"uint256"},{"internalType":"uint256","name":"creditLost","type":"uint256"}],"name":"overrideBalances","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256","name":"_nonce","type":"uint256"}],"name":"refundCredit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"refundFailedDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"bytes32","name":"refundRecipient","type":"bytes32"}],"internalType":"struct IWormholeTunnel.MessageSource","name":"source","type":"tuple"},{"internalType":"contract IERC20","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"payload","type":"bytes"}],"name":"releaseFunds","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"userId","type":"bytes32"}],"name":"requestPairing","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"}],"name":"setDefaultGasLimitRoundtrip","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"_hubChainId","type":"uint16"},{"internalType":"address","name":"_hubContractAddress","type":"address"}],"name":"setHub","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_inboundTokenInstantMessageFee","type":"uint256"},{"internalType":"uint256","name":"_outboundTokenInstantMessageFee","type":"uint256"}],"name":"setInstantMessageFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_creditLimit","type":"uint256"},{"internalType":"uint256","name":"_custodyLimit","type":"uint256"},{"internalType":"uint256","name":"_transactionLimit","type":"uint256"}],"name":"setLimits","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IWormholeTunnel","name":"_tunnel","type":"address"}],"name":"setWormholeTunnel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"chainId","type":"uint16"},{"internalType":"bytes32","name":"sender","type":"bytes32"},{"internalType":"bytes32","name":"refundRecipient","type":"bytes32"}],"internalType":"struct IWormholeTunnel.MessageSource","name":"source","type":"tuple"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"topUp","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum HubSpokeStructs.Action","name":"action","type":"uint8"},{"internalType":"contract IERC20","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"costForReturnDelivery","type":"uint256"}],"name":"userActions","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"weth","outputs":[{"internalType":"contract IWETH","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"withdrawReserves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]