Diff
checker
テキスト
テキスト
画像
ドキュメント
Excel
フォルダ
Legal
Enterprise
デスクトップ
料金
ログイン
Diffchecker デスクトップのダウンロード
テキスト比較
2 つのテキスト ファイルの違いを見つける
ツール
履歴
表示
レイアウト
分割
統合
ライブエディター
空白の変更を非表示
未変更行を折りたたむ
折り返しなし
比較精度
スマート
構文
なし
外観を変更
処理
無視
テキスト変換
最初の差分へ移動
入力を編集
Diffchecker Desktop
Diffcheckerを実行する最も安全な方法。Diffchecker Desktopアプリを入手:あなたの差分はコンピューターから出ることはありません!
Desktopを入手
LayerZeroProvider Diff
作成日
9 か月前
差分は期限切れになりません
クリア
エクスポート
共有
説明
28 削除
行
合計
削除
文字
合計
削除
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
526 行
すべてコピー
69 追加
行
合計
追加
文字
合計
追加
この機能を引き続き使用するには、アップグレードしてください
Diff
checker
Pro
価格を見る
566 行
すべてコピー
// SPDX-License-Identifier: BUSL-1.1
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
pragma solidity ^0.8.20;
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ILayerZeroComposer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroComposer.sol";
import { ILayerZeroComposer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroComposer.sol";
import { MessagingFee, OFTReceipt, SendParam, MessagingReceipt } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/interfaces/IOFT.sol";
import { MessagingFee, OFTReceipt, SendParam, MessagingReceipt } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/interfaces/IOFT.sol";
import { OFTComposeMsgCodec } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/libs/OFTComposeMsgCodec.sol";
import { OFTComposeMsgCodec } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/libs/OFTComposeMsgCodec.sol";
import { IStargate } from "@stargatefinance/stg-evm-v2/src/interfaces/IStargate.sol";
import { IStargate } from "@stargatefinance/stg-evm-v2/src/interfaces/IStargate.sol";
import "../event/EventEmitter.sol";
import "../event/EventEmitter.sol";
import "../data/DataStore.sol";
import "../data/DataStore.sol";
import "../utils/Cast.sol";
import "../utils/Cast.sol";
import "../gas/GasUtils.sol";
import "../gas/GasUtils.sol";
import "../deposit/DepositStoreUtils.sol";
import "../deposit/DepositStoreUtils.sol";
import "../glv/GlvUtils.sol";
import "../glv/GlvUtils.sol";
import "./IMultichainProvider.sol";
import "./IMultichainProvider.sol";
import "./IMultichainGmRouter.sol";
import "./IMultichainGmRouter.sol";
import "./IMultichainGlvRouter.sol";
import "./IMultichainGlvRouter.sol";
import "./IMultichainOrderRouter.sol";
import "./IMultichainOrderRouter.sol";
import "./MultichainVault.sol";
import "./MultichainVault.sol";
import "./MultichainUtils.sol";
import "./MultichainUtils.sol";
/**
/**
* @title LayerZeroProvider
* @title LayerZeroProvider
* Bridging In is done throught lzCompose (receives tokens + encoded message from a source chain)
* Bridging In is done throught lzCompose (receives tokens + encoded message from a source chain)
* Bridging Out is done through bridgeOut (sends tokens to a source chain)
* Bridging Out is done through bridgeOut (sends tokens to a source chain)
* Defines lzCompose function which:
* Defines lzCompose function which:
* - is called by the Stargate executor after tokens are delivered to this contract
* - is called by the Stargate executor after tokens are delivered to this contract
* - forwards the received tokens to MultichainVault and increases user's multichain balance
* - forwards the received tokens to MultichainVault and increases user's multichain balance
* Defines bridgeOut function which:
* Defines bridgeOut function which:
* - sends tokens to the Stargate executor for bridging out to the source chain
* - sends tokens to the Stargate executor for bridging out to the source chain
*/
*/
contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer, RoleModule {
contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer, RoleModule {
struct BridgeOutCache {
struct BridgeOutCache {
uint32 dstEid;
uint32 dstEid;
uint256 valueToSend;
uint256 valueToSend;
MessagingReceipt msgReceipt;
MessagingReceipt msgReceipt;
SendParam sendParam;
SendParam sendParam;
MessagingFee messagingFee;
MessagingFee messagingFee;
OFTReceipt receipt;
OFTReceipt receipt;
}
}
DataStore public immutable dataStore;
DataStore public immutable dataStore;
EventEmitter public immutable eventEmitter;
EventEmitter public immutable eventEmitter;
MultichainVault public immutable multichainVault;
MultichainVault public immutable multichainVault;
IMultichainGmRouter public immutable multichainGmRouter;
IMultichainGmRouter public immutable multichainGmRouter;
IMultichainGlvRouter public immutable multichainGlvRouter;
IMultichainGlvRouter public immutable multichainGlvRouter;
IMultichainOrderRouter public immutable multichainOrderRouter;
IMultichainOrderRouter public immutable multichainOrderRouter;
constructor(
constructor(
DataStore _dataStore,
DataStore _dataStore,
RoleStore _roleStore,
RoleStore _roleStore,
EventEmitter _eventEmitter,
EventEmitter _eventEmitter,
MultichainVault _multichainVault,
MultichainVault _multichainVault,
IMultichainGmRouter _multichainGmRouter,
IMultichainGmRouter _multichainGmRouter,
IMultichainGlvRouter _multichainGlvRouter,
IMultichainGlvRouter _multichainGlvRouter,
IMultichainOrderRouter _multichainOrderRouter
IMultichainOrderRouter _multichainOrderRouter
) RoleModule(_roleStore) {
) RoleModule(_roleStore) {
dataStore = _dataStore;
dataStore = _dataStore;
eventEmitter = _eventEmitter;
eventEmitter = _eventEmitter;
multichainVault = _multichainVault;
multichainVault = _multichainVault;
multichainGmRouter = _multichainGmRouter;
multichainGmRouter = _multichainGmRouter;
multichainGlvRouter = _multichainGlvRouter;
multichainGlvRouter = _multichainGlvRouter;
multichainOrderRouter = _multichainOrderRouter;
multichainOrderRouter = _multichainOrderRouter;
}
}
/**
/**
* @notice Called by Stargate after tokens have been delivered to this contract.
* @notice Called by Stargate after tokens have been delivered to this contract.
* @dev Handles the receipt of bridged tokens and optionally executes a deposit action.
* @dev Handles the receipt of bridged tokens and optionally executes a deposit action.
*
*
* @dev If a user bridges tokens with deposit data, and already has sufficient funds in their multichain balance,
* @dev If a user bridges tokens with deposit data, and already has sufficient funds in their multichain balance,
* it is possible for multiple bridge transactions to result in multiple deposits (i.e. double mints).
* it is possible for multiple bridge transactions to result in multiple deposits (i.e. double mints).
* For example, if a user bridges 10 WETH and 20,000 USDC, both with deposit data, and already has enough funds,
* For example, if a user bridges 10 WETH and 20,000 USDC, both with deposit data, and already has enough funds,
* both bridge transactions could result in a deposit.
* both bridge transactions could result in a deposit.
*
*
* @dev It is recommended that the interface or frontend enforces that users only bridge amounts that would not
* @dev It is recommended that the interface or frontend enforces that users only bridge amounts that would not
* result in double deposits.
* result in double deposits.
*
*
* @param from The address of the sender (i.e. Stargate address, not user's address).
* @param from The address of the sender (i.e. Stargate address, not user's address).
* param guid A global unique identifier for tracking the packet.
* param guid A global unique identifier for tracking the packet.
* @param message Encoded message. Contains the params needed to record the deposit (account, srcChainId)
* @param message Encoded message. Contains the params needed to record the deposit (account, srcChainId)
* param executor The address of the Executor.
* param executor The address of the Executor.
* param extraData Any extra data or options to trigger on receipt.
* param extraData Any extra data or options to trigger on receipt.
*/
*/
function lzCompose(
function lzCompose(
address from,
address from,
bytes32 /*guid*/,
bytes32 /*guid*/,
bytes calldata message,
bytes calldata message,
address /*executor*/,
address /*executor*/,
bytes calldata /*extraData*/
bytes calldata /*extraData*/
) external payable {
) external payable {
MultichainUtils.validateMultichainProvider(dataStore, from);
MultichainUtils.validateMultichainProvider(dataStore, from);
MultichainUtils.validateMultichainEndpoint(dataStore, msg.sender);
MultichainUtils.validateMultichainEndpoint(dataStore, msg.sender);
/// @dev The `account` field is user-supplied and not validated; any address may be provided by the sender
/// @dev The `account` field is user-supplied and not validated; any address may be provided by the sender
// msg.sender and `from` need to be validated for `amountLD` and `token` to not be spoofable
// msg.sender and `from` need to be validated for `amountLD` and `token` to not be spoofable
// additionally, the lzReceive, _credit and _debit functions of the OFT should be checked for any overrides
// additionally, the lzReceive, _credit and _debit functions of the OFT should be checked for any overrides
// before the `amountLD` and `token` values can be trusted
// before the `amountLD` and `token` values can be trusted
(address account, uint256 srcChainId, uint256 amountLD, bytes memory data) = _decodeLzComposeMsg(message);
(address account, uint256 srcChainId, uint256 amountLD, bytes memory data) = _decodeLzComposeMsg(message);
address token = IStargate(from).token();
address token = IStargate(from).token();
コピー
コピー済み
コピー
コピー済み
if (token == address(0x0)) {
if (token == address(0x0)) {
// `from` is StargatePoolNative
// `from` is StargatePoolNative
TokenUtils.depositAndSendWrappedNativeToken(dataStore, address(multichainVault), amountLD);
TokenUtils.depositAndSendWrappedNativeToken(dataStore, address(multichainVault), amountLD);
// if token is ETH then we need to use WNT
// if token is ETH then we need to use WNT
token = TokenUtils.wnt(dataStore);
token = TokenUtils.wnt(dataStore);
} else {
} else {
// `from` is e.g. StargatePoolUSDC
// `from` is e.g. StargatePoolUSDC
TokenUtils.transfer(dataStore, token, address(multichainVault), amountLD);
TokenUtils.transfer(dataStore, token, address(multichainVault), amountLD);
}
}
MultichainUtils.recordBridgeIn(
MultichainUtils.recordBridgeIn(
dataStore,
dataStore,
eventEmitter,
eventEmitter,
multichainVault,
multichainVault,
this,
this,
token,
token,
account,
account,
srcChainId
srcChainId
);
);
コピー
コピー済み
コピー
コピー済み
// Handle native token top-up before other actions to ensure the user has the ability to
// pay relay and execution fees required by subsequent actions (Deposit, Withdrawal, etc.)
uint256 expectedNativeValue = 0;
if (data.length != 0) {
(, uint256 decodedExpectedNativeValue, ) = abi.decode(data, (ActionType, uint256, bytes));
expectedNativeValue = decodedExpectedNativeValue;
}
if (expectedNativeValue != 0 || msg.value != 0) {
_handleNativeTopUp(account, srcChainId, expectedNativeValue);
}
// note that for these functions, the remaining gas must be sufficient
// note that for these functions, the remaining gas must be sufficient
// otherwise the function will revert
// otherwise the function will revert
// if the action cannot be completed due to gas issues, the user would
// if the action cannot be completed due to gas issues, the user would
// need to manually call the function with the right amount of gas
// need to manually call the function with the right amount of gas
// srcChainId could be zero when returned from _decodeLzComposeMsg, skip follow up actions in this case
// srcChainId could be zero when returned from _decodeLzComposeMsg, skip follow up actions in this case
if (srcChainId != 0 && data.length != 0) {
if (srcChainId != 0 && data.length != 0) {
コピー
コピー済み
コピー
コピー済み
(ActionType actionType,
bytes memory actionData) = abi.decode(data, (ActionType,
bytes));
(ActionType actionType,
,
bytes memory actionData) = abi.decode(data, (ActionType,
uint256,
bytes));
// this event is emitted even though the actionType may not be valid
// this event is emitted even though the actionType may not be valid
MultichainEventUtils.emitMultichainBridgeAction(eventEmitter, address(this), account, srcChainId, uint256(actionType));
MultichainEventUtils.emitMultichainBridgeAction(eventEmitter, address(this), account, srcChainId, uint256(actionType));
/// @dev relayParams.fee.feeToken must default to WNT when feeSwapPath is not being used
/// @dev relayParams.fee.feeToken must default to WNT when feeSwapPath is not being used
/// otherwise reverts with UnexpectedRelayFeeToken
/// otherwise reverts with UnexpectedRelayFeeToken
if (actionType == ActionType.Deposit) {
if (actionType == ActionType.Deposit) {
_handleDeposit(account, srcChainId, actionType, actionData);
_handleDeposit(account, srcChainId, actionType, actionData);
} else if (actionType == ActionType.GlvDeposit) {
} else if (actionType == ActionType.GlvDeposit) {
_handleGlvDeposit(account, srcChainId, actionType, actionData);
_handleGlvDeposit(account, srcChainId, actionType, actionData);
} else if (actionType == ActionType.SetTraderReferralCode) {
} else if (actionType == ActionType.SetTraderReferralCode) {
_handleSetTraderReferralCode(account, srcChainId, actionType, actionData);
_handleSetTraderReferralCode(account, srcChainId, actionType, actionData);
コピー
コピー済み
コピー
コピー済み
} else if (actionType == ActionType.RegisterCode) {
_handleRegisterCode(account, srcChainId, actionType, actionData);
} else if (actionType == ActionType.Withdrawal) {
} else if (actionType == ActionType.Withdrawal) {
_handleWithdrawal(account, srcChainId, actionType, actionData);
_handleWithdrawal(account, srcChainId, actionType, actionData);
} else if (actionType == ActionType.GlvWithdrawal) {
} else if (actionType == ActionType.GlvWithdrawal) {
_handleGlvWithdrawal(account, srcChainId, actionType, actionData);
_handleGlvWithdrawal(account, srcChainId, actionType, actionData);
}
}
}
}
}
}
/**
/**
* Bridges tokens from the current chain to a source chain using Stargate protocol
* Bridges tokens from the current chain to a source chain using Stargate protocol
* @dev Processes a cross-chain transfer of tokens from user's multichain balance
* @dev Processes a cross-chain transfer of tokens from user's multichain balance
* @dev Called by MultichainTransferRouter
* @dev Called by MultichainTransferRouter
* This function:
* This function:
* 1. Approves tokens to be spent by Stargate
* 1. Approves tokens to be spent by Stargate
* 2. Prepares the necessary parameters and quotes fees
* 2. Prepares the necessary parameters and quotes fees
* 3. Transfers the bridging fee (in WNT) from user's multichain balance
* 3. Transfers the bridging fee (in WNT) from user's multichain balance
* 4. Unwraps WNT to ETH to pay for the cross-chain fee
* 4. Unwraps WNT to ETH to pay for the cross-chain fee
* 5. Transfers the tokens to be bridged from user's multichain balance
* 5. Transfers the tokens to be bridged from user's multichain balance
* 6. Calls Stargate to initiate the cross-chain transfer
* 6. Calls Stargate to initiate the cross-chain transfer
*
*
* @param params A struct containing:
* @param params A struct containing:
* - provider: Address of the Stargate pool
* - provider: Address of the Stargate pool
* - account: User account bridging tokens
* - account: User account bridging tokens
* - token: Address of token being bridged
* - token: Address of token being bridged
* - amount: Amount of tokens to bridge
* - amount: Amount of tokens to bridge
* - srcChainId: Source chain ID (for multichain balance accounting)
* - srcChainId: Source chain ID (for multichain balance accounting)
* - data: ABI-encoded destination endpoint ID (dstEid)
* - data: ABI-encoded destination endpoint ID (dstEid)
* @return The amount of tokens bridged out (may be slightly different from params.amount after LZ precision/path limits adjustments)
* @return The amount of tokens bridged out (may be slightly different from params.amount after LZ precision/path limits adjustments)
*/
*/
function bridgeOut(address account, uint256 srcChainId, IRelayUtils.BridgeOutParams memory params) external onlyController returns (uint256) {
function bridgeOut(address account, uint256 srcChainId, IRelayUtils.BridgeOutParams memory params) external onlyController returns (uint256) {
IStargate stargate = IStargate(params.provider);
IStargate stargate = IStargate(params.provider);
address wnt = dataStore.getAddress(Keys.WNT);
address wnt = dataStore.getAddress(Keys.WNT);
if (stargate.token() == address(0x0)) {
if (stargate.token() == address(0x0)) {
// `stargate` is StargatePoolNative
// `stargate` is StargatePoolNative
if (params.token != wnt) {
if (params.token != wnt) {
revert Errors.InvalidBridgeOutToken(params.token);
revert Errors.InvalidBridgeOutToken(params.token);
}
}
} else {
} else {
// `stargate` is e.g. StargatePoolUSDC
// `stargate` is e.g. StargatePoolUSDC
if (params.token != stargate.token()) {
if (params.token != stargate.token()) {
revert Errors.InvalidBridgeOutToken(params.token);
revert Errors.InvalidBridgeOutToken(params.token);
}
}
}
}
BridgeOutCache memory cache;
BridgeOutCache memory cache;
cache.dstEid = abi.decode(params.data, (uint32));
cache.dstEid = abi.decode(params.data, (uint32));
if (srcChainId != dataStore.getUint(Keys.eidToSrcChainId(cache.dstEid))) {
if (srcChainId != dataStore.getUint(Keys.eidToSrcChainId(cache.dstEid))) {
revert Errors.InvalidEid(cache.dstEid);
revert Errors.InvalidEid(cache.dstEid);
}
}
(cache.valueToSend, cache.sendParam, cache.messagingFee, cache.receipt) = prepareSend(
(cache.valueToSend, cache.sendParam, cache.messagingFee, cache.receipt) = prepareSend(
stargate,
stargate,
params.amount,
params.amount,
params.minAmountOut,
params.minAmountOut,
account,
account,
cache.dstEid
cache.dstEid
);
);
// LZ/Stargate would round down the `amount` to 6 decimals precision / apply path limits
// LZ/Stargate would round down the `amount` to 6 decimals precision / apply path limits
params.amount = cache.receipt.amountSentLD;
params.amount = cache.receipt.amountSentLD;
// transferOut bridging fee amount of wnt from user's multichain balance into this contract
// transferOut bridging fee amount of wnt from user's multichain balance into this contract
// for StargatePoolNative, amountSentLD is added on top of the bridging fee
// for StargatePoolNative, amountSentLD is added on top of the bridging fee
MultichainUtils.transferOut(
MultichainUtils.transferOut(
dataStore,
dataStore,
eventEmitter,
eventEmitter,
multichainVault,
multichainVault,
wnt, // token
wnt, // token
account,
account,
address(this), // receiver
address(this), // receiver
cache.valueToSend, // bridge out fee (+ amountSentLD for native token transfers)
cache.valueToSend, // bridge out fee (+ amountSentLD for native token transfers)
srcChainId
srcChainId
);
);
IWNT(wnt).withdraw(cache.valueToSend);
IWNT(wnt).withdraw(cache.valueToSend);
// if Stagrate.token() is the ZeroAddress:
// if Stagrate.token() is the ZeroAddress:
// - amountSentLD was already added to valueToSend and transferred/unwrapped with the bridging fee
// - amountSentLD was already added to valueToSend and transferred/unwrapped with the bridging fee
// - approval is not needed (since native tokens are being bridged)
// - approval is not needed (since native tokens are being bridged)
if (stargate.token() != address(0x0)) {
if (stargate.token() != address(0x0)) {
// `stargate` is e.g. StargatePoolUSDC
// `stargate` is e.g. StargatePoolUSDC
// transferOut amount of tokens from user's multichain balance into this contract
// transferOut amount of tokens from user's multichain balance into this contract
MultichainUtils.transferOut(
MultichainUtils.transferOut(
dataStore,
dataStore,
eventEmitter,
eventEmitter,
multichainVault,
multichainVault,
params.token,
params.token,
account,
account,
address(this), // receiver
address(this), // receiver
params.amount,
params.amount,
srcChainId
srcChainId
);
);
IERC20(params.token).approve(params.provider, params.amount);
IERC20(params.token).approve(params.provider, params.amount);
}
}
(cache.msgReceipt, /* OFTReceipt memory oftReceipt */) = stargate.send{ value: cache.valueToSend }(
(cache.msgReceipt, /* OFTReceipt memory oftReceipt */) = stargate.send{ value: cache.valueToSend }(
cache.sendParam,
cache.sendParam,
cache.messagingFee,
cache.messagingFee,
address(this) // refundAddress
address(this) // refundAddress
);
);
// fee refunds are send back to this contract, converted to wrapped native token,
// fee refunds are send back to this contract, converted to wrapped native token,
// sent to multichainVault and user's multichain balance is increased
// sent to multichainVault and user's multichain balance is increased
if (cache.msgReceipt.fee.nativeFee < cache.messagingFee.nativeFee) {
if (cache.msgReceipt.fee.nativeFee < cache.messagingFee.nativeFee) {
TokenUtils.depositAndSendWrappedNativeToken(
TokenUtils.depositAndSendWrappedNativeToken(
dataStore,
dataStore,
address(multichainVault), // receiver
address(multichainVault), // receiver
cache.messagingFee.nativeFee - cache.msgReceipt.fee.nativeFee // refund amount
cache.messagingFee.nativeFee - cache.msgReceipt.fee.nativeFee // refund amount
);
);
MultichainUtils.recordTransferIn(
MultichainUtils.recordTransferIn(
dataStore,
dataStore,
eventEmitter,
eventEmitter,
multichainVault,
multichainVault,
wnt, // token
wnt, // token
account,
account,
0 // srcChainId
0 // srcChainId
);
);
}
}
return params.amount;
return params.amount;
}
}
function prepareSend(
function prepareSend(
IStargate stargate,
IStargate stargate,
uint256 amount,
uint256 amount,
uint256 minAmountOut,
uint256 minAmountOut,
address receiver,
address receiver,
uint32 _dstEid
uint32 _dstEid
)
)
private
private
view
view
returns (
returns (
uint256 valueToSend,
uint256 valueToSend,
SendParam memory sendParam,
SendParam memory sendParam,
MessagingFee memory messagingFee,
MessagingFee memory messagingFee,
OFTReceipt memory receipt
OFTReceipt memory receipt
)
)
{
{
sendParam = SendParam({
sendParam = SendParam({
dstEid: _dstEid,
dstEid: _dstEid,
to: Cast.toBytes32(receiver),
to: Cast.toBytes32(receiver),
amountLD: amount,
amountLD: amount,
minAmountLD: minAmountOut,
minAmountLD: minAmountOut,
extraOptions: bytes(""),
extraOptions: bytes(""),
composeMsg: bytes(""),
composeMsg: bytes(""),
oftCmd: ""
oftCmd: ""
});
});
(, , receipt) = stargate.quoteOFT(sendParam);
(, , receipt) = stargate.quoteOFT(sendParam);
sendParam.minAmountLD = receipt.amountReceivedLD;
sendParam.minAmountLD = receipt.amountReceivedLD;
messagingFee = stargate.quoteSend(sendParam, false);
messagingFee = stargate.quoteSend(sendParam, false);
valueToSend = messagingFee.nativeFee;
valueToSend = messagingFee.nativeFee;
if (stargate.token() == address(0x0)) {
if (stargate.token() == address(0x0)) {
valueToSend += receipt.amountSentLD;
valueToSend += receipt.amountSentLD;
}
}
}
}
function _decodeLzComposeMsg(bytes calldata message) private view returns (address, uint256, uint256, bytes memory) {
function _decodeLzComposeMsg(bytes calldata message) private view returns (address, uint256, uint256, bytes memory) {
uint256 amountLD = OFTComposeMsgCodec.amountLD(message);
uint256 amountLD = OFTComposeMsgCodec.amountLD(message);
uint32 srcEid = OFTComposeMsgCodec.srcEid(message);
uint32 srcEid = OFTComposeMsgCodec.srcEid(message);
uint256 srcChainId = dataStore.getUint(Keys.eidToSrcChainId(srcEid));
uint256 srcChainId = dataStore.getUint(Keys.eidToSrcChainId(srcEid));
bytes memory composeMessage = OFTComposeMsgCodec.composeMsg(message);
bytes memory composeMessage = OFTComposeMsgCodec.composeMsg(message);
(address account, bytes memory data) = abi.decode(composeMessage, (address, bytes));
(address account, bytes memory data) = abi.decode(composeMessage, (address, bytes));
return (account, srcChainId, amountLD, data);
return (account, srcChainId, amountLD, data);
}
}
function _areValidTransferRequests(IRelayUtils.TransferRequests memory transferRequests) private pure returns (bool) {
function _areValidTransferRequests(IRelayUtils.TransferRequests memory transferRequests) private pure returns (bool) {
if (
if (
transferRequests.tokens.length != transferRequests.receivers.length ||
transferRequests.tokens.length != transferRequests.receivers.length ||
transferRequests.tokens.length != transferRequests.amounts.length
transferRequests.tokens.length != transferRequests.amounts.length
) {
) {
return false;
return false;
}
}
for (uint256 i = 0; i < transferRequests.tokens.length; i++) {
for (uint256 i = 0; i < transferRequests.tokens.length; i++) {
if (
if (
transferRequests.tokens[i] == address(0) ||
transferRequests.tokens[i] == address(0) ||
transferRequests.receivers[i] == address(0) ||
transferRequests.receivers[i] == address(0) ||
transferRequests.amounts[i] == 0
transferRequests.amounts[i] == 0
) {
) {
return false;
return false;
}
}
}
}
return true;
return true;
}
}
コピー
コピー済み
コピー
コピー済み
function _handleNativeTopUp(
address account,
uint256 srcChainId,
uint256 expectedNativeValue
) private {
// Enforce msg.value matches expected native top-up amount
// Note: This may cause bridged tokens to be locked if executor underpays,
// but other executors or users can retry lzCompose with correct msg.value
if (msg.value < expectedNativeValue) {
revert Errors.InsufficientNativeTokenAmount(msg.value, expectedNativeValue);
}
// Allow receiving native tokens alongside bridged tokens to cover execution fees
// msg.value represents ONLY the top-up amount specified in lzComposeOption (not the bridged tokens)
// Bridged tokens are already transferred to this contract separately via Stargate's flow
TokenUtils.depositAndSendWrappedNativeToken(dataStore, address(multichainVault), msg.value);
MultichainUtils.recordBridgeIn(
dataStore,
eventEmitter,
multichainVault,
this,
TokenUtils.wnt(dataStore),
account,
srcChainId
);
}
/// @dev long/short tokens are deposited from user's multichain balance
/// @dev long/short tokens are deposited from user's multichain balance
/// GM tokens are minted and transferred to user's multichain balance
/// GM tokens are minted and transferred to user's multichain balance
function _handleDeposit(
function _handleDeposit(
address account,
address account,
uint256 srcChainId,
uint256 srcChainId,
ActionType actionType,
ActionType actionType,
bytes memory actionData
bytes memory actionData
) private {
) private {
(
(
IRelayUtils.RelayParams memory relayParams,
IRelayUtils.RelayParams memory relayParams,
IRelayUtils.TransferRequests memory transferRequests,
IRelayUtils.TransferRequests memory transferRequests,
IDepositUtils.CreateDepositParams memory depositParams
IDepositUtils.CreateDepositParams memory depositParams
) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IDepositUtils.CreateDepositParams));
) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IDepositUtils.CreateDepositParams));
if (_areValidTransferRequests(transferRequests)) {
if (_areValidTransferRequests(transferRequests)) {
uint256 estimatedGasLimit = GasUtils.estimateCreateDepositGasLimit(dataStore);
uint256 estimatedGasLimit = GasUtils.estimateCreateDepositGasLimit(dataStore);
_validateGasLeft(estimatedGasLimit);
_validateGasLeft(estimatedGasLimit);
try multichainGmRouter.createDeposit(
try multichainGmRouter.createDeposit(
relayParams,
relayParams,
account,
account,
srcChainId,
srcChainId,
transferRequests,
transferRequests,
depositParams
depositParams
) {
) {
} catch Error(string memory reason) {
} catch Error(string memory reason) {
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
} catch (bytes memory reasonBytes) {
} catch (bytes memory reasonBytes) {
(string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
(string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
}
}
}
}
}
}
/// @dev long/short/GM tokens are deposited from user's multichain balance
/// @dev long/short/GM tokens are deposited from user's multichain balance
/// GLV tokens are minted and transferred to user's multichain balance
/// GLV tokens are minted and transferred to user's multichain balance
function _handleGlvDeposit(
function _handleGlvDeposit(
address account,
address account,
uint256 srcChainId,
uint256 srcChainId,
ActionType actionType,
ActionType actionType,
bytes memory actionData
bytes memory actionData
) private {
) private {
(
(
IRelayUtils.RelayParams memory relayParams,
IRelayUtils.RelayParams memory relayParams,
IRelayUtils.TransferRequests memory transferRequests,
IRelayUtils.TransferRequests memory transferRequests,
IGlvDepositUtils.CreateGlvDepositParams memory glvDepositParams
IGlvDepositUtils.CreateGlvDepositParams memory glvDepositParams
) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IGlvDepositUtils.CreateGlvDepositParams));
) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IGlvDepositUtils.CreateGlvDepositParams));
if (_areValidTransferRequests(transferRequests)) {
if (_areValidTransferRequests(transferRequests)) {
uint256 estimatedGasLimit = GasUtils.estimateCreateGlvDepositGasLimit(dataStore);
uint256 estimatedGasLimit = GasUtils.estimateCreateGlvDepositGasLimit(dataStore);
_validateGasLeft(estimatedGasLimit);
_validateGasLeft(estimatedGasLimit);
try multichainGlvRouter.createGlvDeposit(
try multichainGlvRouter.createGlvDeposit(
relayParams,
relayParams,
account,
account,
srcChainId,
srcChainId,
transferRequests,
transferRequests,
glvDepositParams
glvDepositParams
) {
) {
} catch Error(string memory reason) {
} catch Error(string memory reason) {
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
} catch (bytes memory reasonBytes) {
} catch (bytes memory reasonBytes) {
(string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
(string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
}
}
}
}
}
}
/// @dev `account` is expected to be `msg.sender` from the source chain, as
/// @dev `account` is expected to be `msg.sender` from the source chain, as
/// MultichainOrderRouter would use it to validate the signature.
/// MultichainOrderRouter would use it to validate the signature.
function _handleSetTraderReferralCode(
function _handleSetTraderReferralCode(
address account,
address account,
uint256 srcChainId,
uint256 srcChainId,
ActionType actionType,
ActionType actionType,
bytes memory actionData
bytes memory actionData
) private {
) private {
(
(
IRelayUtils.RelayParams memory relayParams,
IRelayUtils.RelayParams memory relayParams,
bytes32 referralCode
bytes32 referralCode
) = abi.decode(actionData, (IRelayUtils.RelayParams, bytes32));
) = abi.decode(actionData, (IRelayUtils.RelayParams, bytes32));
uint256 estimatedGasLimit = GasUtils.estimateSetTraderReferralCodeGasLimit(dataStore);
uint256 estimatedGasLimit = GasUtils.estimateSetTraderReferralCodeGasLimit(dataStore);
_validateGasLeft(estimatedGasLimit);
_validateGasLeft(estimatedGasLimit);
try multichainOrderRouter.setTraderReferralCode(relayParams, account, srcChainId, referralCode) {
try multichainOrderRouter.setTraderReferralCode(relayParams, account, srcChainId, referralCode) {
} catch Error(string memory reason) {
} catch Error(string memory reason) {
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
} catch (bytes memory reasonBytes) {
} catch (bytes memory reasonBytes) {
(string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
(string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
}
}
}
}
コピー
コピー済み
コピー
コピー済み
/// @dev `account` is expected to be `msg.sender` from the source chain, as
/// MultichainOrderRouter would use it to validate the signature.
function _handleRegisterCode(
address account,
uint256 srcChainId,
ActionType actionType,
bytes memory actionData
) private {
(
IRelayUtils.RelayParams memory relayParams,
bytes32 referralCode
) = abi.decode(actionData, (IRelayUtils.RelayParams, bytes32));
uint256 estimatedGasLimit = GasUtils.estimateRegisterCodeGasLimit(dataStore);
_validateGasLeft(estimatedGasLimit);
try multichainOrderRouter.registerCode(relayParams, account, srcChainId, referralCode) {
} catch Error(string memory reason) {
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
} catch (bytes memory reasonBytes) {
(string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
Text moved from lines 502-504
}
}
function _handleWithdrawal(
function _handleWithdrawal(
address account,
address account,
uint256 srcChainId,
uint256 srcChainId,
ActionType actionType,
ActionType actionType,
bytes memory actionData
bytes memory actionData
) private {
) private {
(
(
IRelayUtils.RelayParams memory relayParams,
IRelayUtils.RelayParams memory relayParams,
IRelayUtils.TransferRequests memory transferRequests,
IRelayUtils.TransferRequests memory transferRequests,
IWithdrawalUtils.CreateWithdrawalParams memory withdrawalParams
IWithdrawalUtils.CreateWithdrawalParams memory withdrawalParams
) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IWithdrawalUtils.CreateWithdrawalParams));
) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IWithdrawalUtils.CreateWithdrawalParams));
if (_areValidTransferRequests(transferRequests)) {
if (_areValidTransferRequests(transferRequests)) {
uint256 estimatedGasLimit = GasUtils.estimateCreateWithdrawalGasLimit(dataStore);
uint256 estimatedGasLimit = GasUtils.estimateCreateWithdrawalGasLimit(dataStore);
_validateGasLeft(estimatedGasLimit);
_validateGasLeft(estimatedGasLimit);
try multichainGmRouter.createWithdrawal(
try multichainGmRouter.createWithdrawal(
relayParams,
relayParams,
account,
account,
srcChainId,
srcChainId,
transferRequests,
transferRequests,
withdrawalParams
withdrawalParams
) {
) {
} catch Error(string memory reason) {
} catch Error(string memory reason) {
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
} catch (bytes memory reasonBytes) {
} catch (bytes memory reasonBytes) {
(string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
(string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
}
}
}
}
}
}
function _handleGlvWithdrawal(
function _handleGlvWithdrawal(
address account,
address account,
uint256 srcChainId,
uint256 srcChainId,
ActionType actionType,
ActionType actionType,
bytes memory actionData
bytes memory actionData
) private {
) private {
(
(
IRelayUtils.RelayParams memory relayParams,
IRelayUtils.RelayParams memory relayParams,
IRelayUtils.TransferRequests memory transferRequests,
IRelayUtils.TransferRequests memory transferRequests,
IGlvWithdrawalUtils.CreateGlvWithdrawalParams memory glvWithdrawalParams
IGlvWithdrawalUtils.CreateGlvWithdrawalParams memory glvWithdrawalParams
) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IGlvWithdrawalUtils.CreateGlvWithdrawalParams));
) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IGlvWithdrawalUtils.CreateGlvWithdrawalParams));
if (_areValidTransferRequests(transferRequests)) {
if (_areValidTransferRequests(transferRequests)) {
uint256 estimatedGasLimit = GasUtils.estimateCreateGlvWithdrawalGasLimit(dataStore);
uint256 estimatedGasLimit = GasUtils.estimateCreateGlvWithdrawalGasLimit(dataStore);
_validateGasLeft(estimatedGasLimit);
_validateGasLeft(estimatedGasLimit);
try multichainGlvRouter.createGlvWithdrawal(
try multichainGlvRouter.createGlvWithdrawal(
relayParams,
relayParams,
account,
account,
srcChainId,
srcChainId,
transferRequests,
transferRequests,
glvWithdrawalParams
glvWithdrawalParams
) {
) {
} catch Error(string memory reason) {
} catch Error(string memory reason) {
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
} catch (bytes memory reasonBytes) {
} catch (bytes memory reasonBytes) {
(string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
(string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason);
}
}
}
}
}
}
function _validateGasLeft(uint256 estimatedGasLimit) internal view {
function _validateGasLeft(uint256 estimatedGasLimit) internal view {
コピー
コピー済み
コピー
コピー済み
uint
256 gas = gasleft();
uint
if (gas < estimatedGasLimit) {
revert Errors.InsufficientGasLeft(gas, estimatedGasLimit);
Text moved to lines 498-500
}
}
/**
* @notice Withdraws tokens which have been locked in this contract due to potential errors in lzCompose (e.g. incorrect message format).
* @dev Callable through the timelock contract.
* @param token The address of the token to withdraw.
* @param receiver The address receiving the withdrawn tokens.
* @param amount The amount of tokens to withdraw.
*/
function withdrawTokens(address token, address receiver, uint256 amount) external onlyController {
if (amount == 0) {
revert Errors.EmptyWithdrawalAmount();
}
if (token == address(0)) {
TokenUtils.sendNativeToken(dataStore, receiver, amount);
} else {
TokenUtils.transfer(dataStore, token, receiver, amount);
}
}
/// @dev Accept ETH from StargatePoolNative and when unwrapping WNT
receive() external payable {}
}
保存された差分
原文
ファイルを開く
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.20; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ILayerZeroComposer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroComposer.sol"; import { MessagingFee, OFTReceipt, SendParam, MessagingReceipt } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/interfaces/IOFT.sol"; import { OFTComposeMsgCodec } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/libs/OFTComposeMsgCodec.sol"; import { IStargate } from "@stargatefinance/stg-evm-v2/src/interfaces/IStargate.sol"; import "../event/EventEmitter.sol"; import "../data/DataStore.sol"; import "../utils/Cast.sol"; import "../gas/GasUtils.sol"; import "../deposit/DepositStoreUtils.sol"; import "../glv/GlvUtils.sol"; import "./IMultichainProvider.sol"; import "./IMultichainGmRouter.sol"; import "./IMultichainGlvRouter.sol"; import "./IMultichainOrderRouter.sol"; import "./MultichainVault.sol"; import "./MultichainUtils.sol"; /** * @title LayerZeroProvider * Bridging In is done throught lzCompose (receives tokens + encoded message from a source chain) * Bridging Out is done through bridgeOut (sends tokens to a source chain) * Defines lzCompose function which: * - is called by the Stargate executor after tokens are delivered to this contract * - forwards the received tokens to MultichainVault and increases user's multichain balance * Defines bridgeOut function which: * - sends tokens to the Stargate executor for bridging out to the source chain */ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer, RoleModule { struct BridgeOutCache { uint32 dstEid; uint256 valueToSend; MessagingReceipt msgReceipt; SendParam sendParam; MessagingFee messagingFee; OFTReceipt receipt; } DataStore public immutable dataStore; EventEmitter public immutable eventEmitter; MultichainVault public immutable multichainVault; IMultichainGmRouter public immutable multichainGmRouter; IMultichainGlvRouter public immutable multichainGlvRouter; IMultichainOrderRouter public immutable multichainOrderRouter; constructor( DataStore _dataStore, RoleStore _roleStore, EventEmitter _eventEmitter, MultichainVault _multichainVault, IMultichainGmRouter _multichainGmRouter, IMultichainGlvRouter _multichainGlvRouter, IMultichainOrderRouter _multichainOrderRouter ) RoleModule(_roleStore) { dataStore = _dataStore; eventEmitter = _eventEmitter; multichainVault = _multichainVault; multichainGmRouter = _multichainGmRouter; multichainGlvRouter = _multichainGlvRouter; multichainOrderRouter = _multichainOrderRouter; } /** * @notice Called by Stargate after tokens have been delivered to this contract. * @dev Handles the receipt of bridged tokens and optionally executes a deposit action. * * @dev If a user bridges tokens with deposit data, and already has sufficient funds in their multichain balance, * it is possible for multiple bridge transactions to result in multiple deposits (i.e. double mints). * For example, if a user bridges 10 WETH and 20,000 USDC, both with deposit data, and already has enough funds, * both bridge transactions could result in a deposit. * * @dev It is recommended that the interface or frontend enforces that users only bridge amounts that would not * result in double deposits. * * @param from The address of the sender (i.e. Stargate address, not user's address). * param guid A global unique identifier for tracking the packet. * @param message Encoded message. Contains the params needed to record the deposit (account, srcChainId) * param executor The address of the Executor. * param extraData Any extra data or options to trigger on receipt. */ function lzCompose( address from, bytes32 /*guid*/, bytes calldata message, address /*executor*/, bytes calldata /*extraData*/ ) external payable { MultichainUtils.validateMultichainProvider(dataStore, from); MultichainUtils.validateMultichainEndpoint(dataStore, msg.sender); /// @dev The `account` field is user-supplied and not validated; any address may be provided by the sender // msg.sender and `from` need to be validated for `amountLD` and `token` to not be spoofable // additionally, the lzReceive, _credit and _debit functions of the OFT should be checked for any overrides // before the `amountLD` and `token` values can be trusted (address account, uint256 srcChainId, uint256 amountLD, bytes memory data) = _decodeLzComposeMsg(message); address token = IStargate(from).token(); if (token == address(0x0)) { // `from` is StargatePoolNative TokenUtils.depositAndSendWrappedNativeToken(dataStore, address(multichainVault), amountLD); // if token is ETH then we need to use WNT token = TokenUtils.wnt(dataStore); } else { // `from` is e.g. StargatePoolUSDC TokenUtils.transfer(dataStore, token, address(multichainVault), amountLD); } MultichainUtils.recordBridgeIn( dataStore, eventEmitter, multichainVault, this, token, account, srcChainId ); // note that for these functions, the remaining gas must be sufficient // otherwise the function will revert // if the action cannot be completed due to gas issues, the user would // need to manually call the function with the right amount of gas // srcChainId could be zero when returned from _decodeLzComposeMsg, skip follow up actions in this case if (srcChainId != 0 && data.length != 0) { (ActionType actionType, bytes memory actionData) = abi.decode(data, (ActionType, bytes)); // this event is emitted even though the actionType may not be valid MultichainEventUtils.emitMultichainBridgeAction(eventEmitter, address(this), account, srcChainId, uint256(actionType)); /// @dev relayParams.fee.feeToken must default to WNT when feeSwapPath is not being used /// otherwise reverts with UnexpectedRelayFeeToken if (actionType == ActionType.Deposit) { _handleDeposit(account, srcChainId, actionType, actionData); } else if (actionType == ActionType.GlvDeposit) { _handleGlvDeposit(account, srcChainId, actionType, actionData); } else if (actionType == ActionType.SetTraderReferralCode) { _handleSetTraderReferralCode(account, srcChainId, actionType, actionData); } else if (actionType == ActionType.Withdrawal) { _handleWithdrawal(account, srcChainId, actionType, actionData); } else if (actionType == ActionType.GlvWithdrawal) { _handleGlvWithdrawal(account, srcChainId, actionType, actionData); } } } /** * Bridges tokens from the current chain to a source chain using Stargate protocol * @dev Processes a cross-chain transfer of tokens from user's multichain balance * @dev Called by MultichainTransferRouter * This function: * 1. Approves tokens to be spent by Stargate * 2. Prepares the necessary parameters and quotes fees * 3. Transfers the bridging fee (in WNT) from user's multichain balance * 4. Unwraps WNT to ETH to pay for the cross-chain fee * 5. Transfers the tokens to be bridged from user's multichain balance * 6. Calls Stargate to initiate the cross-chain transfer * * @param params A struct containing: * - provider: Address of the Stargate pool * - account: User account bridging tokens * - token: Address of token being bridged * - amount: Amount of tokens to bridge * - srcChainId: Source chain ID (for multichain balance accounting) * - data: ABI-encoded destination endpoint ID (dstEid) * @return The amount of tokens bridged out (may be slightly different from params.amount after LZ precision/path limits adjustments) */ function bridgeOut(address account, uint256 srcChainId, IRelayUtils.BridgeOutParams memory params) external onlyController returns (uint256) { IStargate stargate = IStargate(params.provider); address wnt = dataStore.getAddress(Keys.WNT); if (stargate.token() == address(0x0)) { // `stargate` is StargatePoolNative if (params.token != wnt) { revert Errors.InvalidBridgeOutToken(params.token); } } else { // `stargate` is e.g. StargatePoolUSDC if (params.token != stargate.token()) { revert Errors.InvalidBridgeOutToken(params.token); } } BridgeOutCache memory cache; cache.dstEid = abi.decode(params.data, (uint32)); if (srcChainId != dataStore.getUint(Keys.eidToSrcChainId(cache.dstEid))) { revert Errors.InvalidEid(cache.dstEid); } (cache.valueToSend, cache.sendParam, cache.messagingFee, cache.receipt) = prepareSend( stargate, params.amount, params.minAmountOut, account, cache.dstEid ); // LZ/Stargate would round down the `amount` to 6 decimals precision / apply path limits params.amount = cache.receipt.amountSentLD; // transferOut bridging fee amount of wnt from user's multichain balance into this contract // for StargatePoolNative, amountSentLD is added on top of the bridging fee MultichainUtils.transferOut( dataStore, eventEmitter, multichainVault, wnt, // token account, address(this), // receiver cache.valueToSend, // bridge out fee (+ amountSentLD for native token transfers) srcChainId ); IWNT(wnt).withdraw(cache.valueToSend); // if Stagrate.token() is the ZeroAddress: // - amountSentLD was already added to valueToSend and transferred/unwrapped with the bridging fee // - approval is not needed (since native tokens are being bridged) if (stargate.token() != address(0x0)) { // `stargate` is e.g. StargatePoolUSDC // transferOut amount of tokens from user's multichain balance into this contract MultichainUtils.transferOut( dataStore, eventEmitter, multichainVault, params.token, account, address(this), // receiver params.amount, srcChainId ); IERC20(params.token).approve(params.provider, params.amount); } (cache.msgReceipt, /* OFTReceipt memory oftReceipt */) = stargate.send{ value: cache.valueToSend }( cache.sendParam, cache.messagingFee, address(this) // refundAddress ); // fee refunds are send back to this contract, converted to wrapped native token, // sent to multichainVault and user's multichain balance is increased if (cache.msgReceipt.fee.nativeFee < cache.messagingFee.nativeFee) { TokenUtils.depositAndSendWrappedNativeToken( dataStore, address(multichainVault), // receiver cache.messagingFee.nativeFee - cache.msgReceipt.fee.nativeFee // refund amount ); MultichainUtils.recordTransferIn( dataStore, eventEmitter, multichainVault, wnt, // token account, 0 // srcChainId ); } return params.amount; } function prepareSend( IStargate stargate, uint256 amount, uint256 minAmountOut, address receiver, uint32 _dstEid ) private view returns ( uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee, OFTReceipt memory receipt ) { sendParam = SendParam({ dstEid: _dstEid, to: Cast.toBytes32(receiver), amountLD: amount, minAmountLD: minAmountOut, extraOptions: bytes(""), composeMsg: bytes(""), oftCmd: "" }); (, , receipt) = stargate.quoteOFT(sendParam); sendParam.minAmountLD = receipt.amountReceivedLD; messagingFee = stargate.quoteSend(sendParam, false); valueToSend = messagingFee.nativeFee; if (stargate.token() == address(0x0)) { valueToSend += receipt.amountSentLD; } } function _decodeLzComposeMsg(bytes calldata message) private view returns (address, uint256, uint256, bytes memory) { uint256 amountLD = OFTComposeMsgCodec.amountLD(message); uint32 srcEid = OFTComposeMsgCodec.srcEid(message); uint256 srcChainId = dataStore.getUint(Keys.eidToSrcChainId(srcEid)); bytes memory composeMessage = OFTComposeMsgCodec.composeMsg(message); (address account, bytes memory data) = abi.decode(composeMessage, (address, bytes)); return (account, srcChainId, amountLD, data); } function _areValidTransferRequests(IRelayUtils.TransferRequests memory transferRequests) private pure returns (bool) { if ( transferRequests.tokens.length != transferRequests.receivers.length || transferRequests.tokens.length != transferRequests.amounts.length ) { return false; } for (uint256 i = 0; i < transferRequests.tokens.length; i++) { if ( transferRequests.tokens[i] == address(0) || transferRequests.receivers[i] == address(0) || transferRequests.amounts[i] == 0 ) { return false; } } return true; } /// @dev long/short tokens are deposited from user's multichain balance /// GM tokens are minted and transferred to user's multichain balance function _handleDeposit( address account, uint256 srcChainId, ActionType actionType, bytes memory actionData ) private { ( IRelayUtils.RelayParams memory relayParams, IRelayUtils.TransferRequests memory transferRequests, IDepositUtils.CreateDepositParams memory depositParams ) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IDepositUtils.CreateDepositParams)); if (_areValidTransferRequests(transferRequests)) { uint256 estimatedGasLimit = GasUtils.estimateCreateDepositGasLimit(dataStore); _validateGasLeft(estimatedGasLimit); try multichainGmRouter.createDeposit( relayParams, account, srcChainId, transferRequests, depositParams ) { } catch Error(string memory reason) { MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } catch (bytes memory reasonBytes) { (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes); MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } } } /// @dev long/short/GM tokens are deposited from user's multichain balance /// GLV tokens are minted and transferred to user's multichain balance function _handleGlvDeposit( address account, uint256 srcChainId, ActionType actionType, bytes memory actionData ) private { ( IRelayUtils.RelayParams memory relayParams, IRelayUtils.TransferRequests memory transferRequests, IGlvDepositUtils.CreateGlvDepositParams memory glvDepositParams ) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IGlvDepositUtils.CreateGlvDepositParams)); if (_areValidTransferRequests(transferRequests)) { uint256 estimatedGasLimit = GasUtils.estimateCreateGlvDepositGasLimit(dataStore); _validateGasLeft(estimatedGasLimit); try multichainGlvRouter.createGlvDeposit( relayParams, account, srcChainId, transferRequests, glvDepositParams ) { } catch Error(string memory reason) { MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } catch (bytes memory reasonBytes) { (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes); MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } } } /// @dev `account` is expected to be `msg.sender` from the source chain, as /// MultichainOrderRouter would use it to validate the signature. function _handleSetTraderReferralCode( address account, uint256 srcChainId, ActionType actionType, bytes memory actionData ) private { ( IRelayUtils.RelayParams memory relayParams, bytes32 referralCode ) = abi.decode(actionData, (IRelayUtils.RelayParams, bytes32)); uint256 estimatedGasLimit = GasUtils.estimateSetTraderReferralCodeGasLimit(dataStore); _validateGasLeft(estimatedGasLimit); try multichainOrderRouter.setTraderReferralCode(relayParams, account, srcChainId, referralCode) { } catch Error(string memory reason) { MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } catch (bytes memory reasonBytes) { (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes); MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } } function _handleWithdrawal( address account, uint256 srcChainId, ActionType actionType, bytes memory actionData ) private { ( IRelayUtils.RelayParams memory relayParams, IRelayUtils.TransferRequests memory transferRequests, IWithdrawalUtils.CreateWithdrawalParams memory withdrawalParams ) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IWithdrawalUtils.CreateWithdrawalParams)); if (_areValidTransferRequests(transferRequests)) { uint256 estimatedGasLimit = GasUtils.estimateCreateWithdrawalGasLimit(dataStore); _validateGasLeft(estimatedGasLimit); try multichainGmRouter.createWithdrawal( relayParams, account, srcChainId, transferRequests, withdrawalParams ) { } catch Error(string memory reason) { MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } catch (bytes memory reasonBytes) { (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes); MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } } } function _handleGlvWithdrawal( address account, uint256 srcChainId, ActionType actionType, bytes memory actionData ) private { ( IRelayUtils.RelayParams memory relayParams, IRelayUtils.TransferRequests memory transferRequests, IGlvWithdrawalUtils.CreateGlvWithdrawalParams memory glvWithdrawalParams ) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IGlvWithdrawalUtils.CreateGlvWithdrawalParams)); if (_areValidTransferRequests(transferRequests)) { uint256 estimatedGasLimit = GasUtils.estimateCreateGlvWithdrawalGasLimit(dataStore); _validateGasLeft(estimatedGasLimit); try multichainGlvRouter.createGlvWithdrawal( relayParams, account, srcChainId, transferRequests, glvWithdrawalParams ) { } catch Error(string memory reason) { MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } catch (bytes memory reasonBytes) { (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes); MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } } } function _validateGasLeft(uint256 estimatedGasLimit) internal view { uint256 gas = gasleft(); if (gas < estimatedGasLimit) { revert Errors.InsufficientGasLeft(gas, estimatedGasLimit); } } /** * @notice Withdraws tokens which have been locked in this contract due to potential errors in lzCompose (e.g. incorrect message format). * @dev Callable through the timelock contract. * @param token The address of the token to withdraw. * @param receiver The address receiving the withdrawn tokens. * @param amount The amount of tokens to withdraw. */ function withdrawTokens(address token, address receiver, uint256 amount) external onlyController { if (amount == 0) { revert Errors.EmptyWithdrawalAmount(); } if (token == address(0)) { TokenUtils.sendNativeToken(dataStore, receiver, amount); } else { TokenUtils.transfer(dataStore, token, receiver, amount); } } /// @dev Accept ETH from StargatePoolNative and when unwrapping WNT receive() external payable {} }
変更されたテキスト
ファイルを開く
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.20; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ILayerZeroComposer } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroComposer.sol"; import { MessagingFee, OFTReceipt, SendParam, MessagingReceipt } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/interfaces/IOFT.sol"; import { OFTComposeMsgCodec } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/libs/OFTComposeMsgCodec.sol"; import { IStargate } from "@stargatefinance/stg-evm-v2/src/interfaces/IStargate.sol"; import "../event/EventEmitter.sol"; import "../data/DataStore.sol"; import "../utils/Cast.sol"; import "../gas/GasUtils.sol"; import "../deposit/DepositStoreUtils.sol"; import "../glv/GlvUtils.sol"; import "./IMultichainProvider.sol"; import "./IMultichainGmRouter.sol"; import "./IMultichainGlvRouter.sol"; import "./IMultichainOrderRouter.sol"; import "./MultichainVault.sol"; import "./MultichainUtils.sol"; /** * @title LayerZeroProvider * Bridging In is done throught lzCompose (receives tokens + encoded message from a source chain) * Bridging Out is done through bridgeOut (sends tokens to a source chain) * Defines lzCompose function which: * - is called by the Stargate executor after tokens are delivered to this contract * - forwards the received tokens to MultichainVault and increases user's multichain balance * Defines bridgeOut function which: * - sends tokens to the Stargate executor for bridging out to the source chain */ contract LayerZeroProvider is IMultichainProvider, ILayerZeroComposer, RoleModule { struct BridgeOutCache { uint32 dstEid; uint256 valueToSend; MessagingReceipt msgReceipt; SendParam sendParam; MessagingFee messagingFee; OFTReceipt receipt; } DataStore public immutable dataStore; EventEmitter public immutable eventEmitter; MultichainVault public immutable multichainVault; IMultichainGmRouter public immutable multichainGmRouter; IMultichainGlvRouter public immutable multichainGlvRouter; IMultichainOrderRouter public immutable multichainOrderRouter; constructor( DataStore _dataStore, RoleStore _roleStore, EventEmitter _eventEmitter, MultichainVault _multichainVault, IMultichainGmRouter _multichainGmRouter, IMultichainGlvRouter _multichainGlvRouter, IMultichainOrderRouter _multichainOrderRouter ) RoleModule(_roleStore) { dataStore = _dataStore; eventEmitter = _eventEmitter; multichainVault = _multichainVault; multichainGmRouter = _multichainGmRouter; multichainGlvRouter = _multichainGlvRouter; multichainOrderRouter = _multichainOrderRouter; } /** * @notice Called by Stargate after tokens have been delivered to this contract. * @dev Handles the receipt of bridged tokens and optionally executes a deposit action. * * @dev If a user bridges tokens with deposit data, and already has sufficient funds in their multichain balance, * it is possible for multiple bridge transactions to result in multiple deposits (i.e. double mints). * For example, if a user bridges 10 WETH and 20,000 USDC, both with deposit data, and already has enough funds, * both bridge transactions could result in a deposit. * * @dev It is recommended that the interface or frontend enforces that users only bridge amounts that would not * result in double deposits. * * @param from The address of the sender (i.e. Stargate address, not user's address). * param guid A global unique identifier for tracking the packet. * @param message Encoded message. Contains the params needed to record the deposit (account, srcChainId) * param executor The address of the Executor. * param extraData Any extra data or options to trigger on receipt. */ function lzCompose( address from, bytes32 /*guid*/, bytes calldata message, address /*executor*/, bytes calldata /*extraData*/ ) external payable { MultichainUtils.validateMultichainProvider(dataStore, from); MultichainUtils.validateMultichainEndpoint(dataStore, msg.sender); /// @dev The `account` field is user-supplied and not validated; any address may be provided by the sender // msg.sender and `from` need to be validated for `amountLD` and `token` to not be spoofable // additionally, the lzReceive, _credit and _debit functions of the OFT should be checked for any overrides // before the `amountLD` and `token` values can be trusted (address account, uint256 srcChainId, uint256 amountLD, bytes memory data) = _decodeLzComposeMsg(message); address token = IStargate(from).token(); if (token == address(0x0)) { // `from` is StargatePoolNative TokenUtils.depositAndSendWrappedNativeToken(dataStore, address(multichainVault), amountLD); // if token is ETH then we need to use WNT token = TokenUtils.wnt(dataStore); } else { // `from` is e.g. StargatePoolUSDC TokenUtils.transfer(dataStore, token, address(multichainVault), amountLD); } MultichainUtils.recordBridgeIn( dataStore, eventEmitter, multichainVault, this, token, account, srcChainId ); // Handle native token top-up before other actions to ensure the user has the ability to // pay relay and execution fees required by subsequent actions (Deposit, Withdrawal, etc.) uint256 expectedNativeValue = 0; if (data.length != 0) { (, uint256 decodedExpectedNativeValue, ) = abi.decode(data, (ActionType, uint256, bytes)); expectedNativeValue = decodedExpectedNativeValue; } if (expectedNativeValue != 0 || msg.value != 0) { _handleNativeTopUp(account, srcChainId, expectedNativeValue); } // note that for these functions, the remaining gas must be sufficient // otherwise the function will revert // if the action cannot be completed due to gas issues, the user would // need to manually call the function with the right amount of gas // srcChainId could be zero when returned from _decodeLzComposeMsg, skip follow up actions in this case if (srcChainId != 0 && data.length != 0) { (ActionType actionType, , bytes memory actionData) = abi.decode(data, (ActionType, uint256, bytes)); // this event is emitted even though the actionType may not be valid MultichainEventUtils.emitMultichainBridgeAction(eventEmitter, address(this), account, srcChainId, uint256(actionType)); /// @dev relayParams.fee.feeToken must default to WNT when feeSwapPath is not being used /// otherwise reverts with UnexpectedRelayFeeToken if (actionType == ActionType.Deposit) { _handleDeposit(account, srcChainId, actionType, actionData); } else if (actionType == ActionType.GlvDeposit) { _handleGlvDeposit(account, srcChainId, actionType, actionData); } else if (actionType == ActionType.SetTraderReferralCode) { _handleSetTraderReferralCode(account, srcChainId, actionType, actionData); } else if (actionType == ActionType.RegisterCode) { _handleRegisterCode(account, srcChainId, actionType, actionData); } else if (actionType == ActionType.Withdrawal) { _handleWithdrawal(account, srcChainId, actionType, actionData); } else if (actionType == ActionType.GlvWithdrawal) { _handleGlvWithdrawal(account, srcChainId, actionType, actionData); } } } /** * Bridges tokens from the current chain to a source chain using Stargate protocol * @dev Processes a cross-chain transfer of tokens from user's multichain balance * @dev Called by MultichainTransferRouter * This function: * 1. Approves tokens to be spent by Stargate * 2. Prepares the necessary parameters and quotes fees * 3. Transfers the bridging fee (in WNT) from user's multichain balance * 4. Unwraps WNT to ETH to pay for the cross-chain fee * 5. Transfers the tokens to be bridged from user's multichain balance * 6. Calls Stargate to initiate the cross-chain transfer * * @param params A struct containing: * - provider: Address of the Stargate pool * - account: User account bridging tokens * - token: Address of token being bridged * - amount: Amount of tokens to bridge * - srcChainId: Source chain ID (for multichain balance accounting) * - data: ABI-encoded destination endpoint ID (dstEid) * @return The amount of tokens bridged out (may be slightly different from params.amount after LZ precision/path limits adjustments) */ function bridgeOut(address account, uint256 srcChainId, IRelayUtils.BridgeOutParams memory params) external onlyController returns (uint256) { IStargate stargate = IStargate(params.provider); address wnt = dataStore.getAddress(Keys.WNT); if (stargate.token() == address(0x0)) { // `stargate` is StargatePoolNative if (params.token != wnt) { revert Errors.InvalidBridgeOutToken(params.token); } } else { // `stargate` is e.g. StargatePoolUSDC if (params.token != stargate.token()) { revert Errors.InvalidBridgeOutToken(params.token); } } BridgeOutCache memory cache; cache.dstEid = abi.decode(params.data, (uint32)); if (srcChainId != dataStore.getUint(Keys.eidToSrcChainId(cache.dstEid))) { revert Errors.InvalidEid(cache.dstEid); } (cache.valueToSend, cache.sendParam, cache.messagingFee, cache.receipt) = prepareSend( stargate, params.amount, params.minAmountOut, account, cache.dstEid ); // LZ/Stargate would round down the `amount` to 6 decimals precision / apply path limits params.amount = cache.receipt.amountSentLD; // transferOut bridging fee amount of wnt from user's multichain balance into this contract // for StargatePoolNative, amountSentLD is added on top of the bridging fee MultichainUtils.transferOut( dataStore, eventEmitter, multichainVault, wnt, // token account, address(this), // receiver cache.valueToSend, // bridge out fee (+ amountSentLD for native token transfers) srcChainId ); IWNT(wnt).withdraw(cache.valueToSend); // if Stagrate.token() is the ZeroAddress: // - amountSentLD was already added to valueToSend and transferred/unwrapped with the bridging fee // - approval is not needed (since native tokens are being bridged) if (stargate.token() != address(0x0)) { // `stargate` is e.g. StargatePoolUSDC // transferOut amount of tokens from user's multichain balance into this contract MultichainUtils.transferOut( dataStore, eventEmitter, multichainVault, params.token, account, address(this), // receiver params.amount, srcChainId ); IERC20(params.token).approve(params.provider, params.amount); } (cache.msgReceipt, /* OFTReceipt memory oftReceipt */) = stargate.send{ value: cache.valueToSend }( cache.sendParam, cache.messagingFee, address(this) // refundAddress ); // fee refunds are send back to this contract, converted to wrapped native token, // sent to multichainVault and user's multichain balance is increased if (cache.msgReceipt.fee.nativeFee < cache.messagingFee.nativeFee) { TokenUtils.depositAndSendWrappedNativeToken( dataStore, address(multichainVault), // receiver cache.messagingFee.nativeFee - cache.msgReceipt.fee.nativeFee // refund amount ); MultichainUtils.recordTransferIn( dataStore, eventEmitter, multichainVault, wnt, // token account, 0 // srcChainId ); } return params.amount; } function prepareSend( IStargate stargate, uint256 amount, uint256 minAmountOut, address receiver, uint32 _dstEid ) private view returns ( uint256 valueToSend, SendParam memory sendParam, MessagingFee memory messagingFee, OFTReceipt memory receipt ) { sendParam = SendParam({ dstEid: _dstEid, to: Cast.toBytes32(receiver), amountLD: amount, minAmountLD: minAmountOut, extraOptions: bytes(""), composeMsg: bytes(""), oftCmd: "" }); (, , receipt) = stargate.quoteOFT(sendParam); sendParam.minAmountLD = receipt.amountReceivedLD; messagingFee = stargate.quoteSend(sendParam, false); valueToSend = messagingFee.nativeFee; if (stargate.token() == address(0x0)) { valueToSend += receipt.amountSentLD; } } function _decodeLzComposeMsg(bytes calldata message) private view returns (address, uint256, uint256, bytes memory) { uint256 amountLD = OFTComposeMsgCodec.amountLD(message); uint32 srcEid = OFTComposeMsgCodec.srcEid(message); uint256 srcChainId = dataStore.getUint(Keys.eidToSrcChainId(srcEid)); bytes memory composeMessage = OFTComposeMsgCodec.composeMsg(message); (address account, bytes memory data) = abi.decode(composeMessage, (address, bytes)); return (account, srcChainId, amountLD, data); } function _areValidTransferRequests(IRelayUtils.TransferRequests memory transferRequests) private pure returns (bool) { if ( transferRequests.tokens.length != transferRequests.receivers.length || transferRequests.tokens.length != transferRequests.amounts.length ) { return false; } for (uint256 i = 0; i < transferRequests.tokens.length; i++) { if ( transferRequests.tokens[i] == address(0) || transferRequests.receivers[i] == address(0) || transferRequests.amounts[i] == 0 ) { return false; } } return true; } function _handleNativeTopUp( address account, uint256 srcChainId, uint256 expectedNativeValue ) private { // Enforce msg.value matches expected native top-up amount // Note: This may cause bridged tokens to be locked if executor underpays, // but other executors or users can retry lzCompose with correct msg.value if (msg.value < expectedNativeValue) { revert Errors.InsufficientNativeTokenAmount(msg.value, expectedNativeValue); } // Allow receiving native tokens alongside bridged tokens to cover execution fees // msg.value represents ONLY the top-up amount specified in lzComposeOption (not the bridged tokens) // Bridged tokens are already transferred to this contract separately via Stargate's flow TokenUtils.depositAndSendWrappedNativeToken(dataStore, address(multichainVault), msg.value); MultichainUtils.recordBridgeIn( dataStore, eventEmitter, multichainVault, this, TokenUtils.wnt(dataStore), account, srcChainId ); } /// @dev long/short tokens are deposited from user's multichain balance /// GM tokens are minted and transferred to user's multichain balance function _handleDeposit( address account, uint256 srcChainId, ActionType actionType, bytes memory actionData ) private { ( IRelayUtils.RelayParams memory relayParams, IRelayUtils.TransferRequests memory transferRequests, IDepositUtils.CreateDepositParams memory depositParams ) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IDepositUtils.CreateDepositParams)); if (_areValidTransferRequests(transferRequests)) { uint256 estimatedGasLimit = GasUtils.estimateCreateDepositGasLimit(dataStore); _validateGasLeft(estimatedGasLimit); try multichainGmRouter.createDeposit( relayParams, account, srcChainId, transferRequests, depositParams ) { } catch Error(string memory reason) { MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } catch (bytes memory reasonBytes) { (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes); MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } } } /// @dev long/short/GM tokens are deposited from user's multichain balance /// GLV tokens are minted and transferred to user's multichain balance function _handleGlvDeposit( address account, uint256 srcChainId, ActionType actionType, bytes memory actionData ) private { ( IRelayUtils.RelayParams memory relayParams, IRelayUtils.TransferRequests memory transferRequests, IGlvDepositUtils.CreateGlvDepositParams memory glvDepositParams ) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IGlvDepositUtils.CreateGlvDepositParams)); if (_areValidTransferRequests(transferRequests)) { uint256 estimatedGasLimit = GasUtils.estimateCreateGlvDepositGasLimit(dataStore); _validateGasLeft(estimatedGasLimit); try multichainGlvRouter.createGlvDeposit( relayParams, account, srcChainId, transferRequests, glvDepositParams ) { } catch Error(string memory reason) { MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } catch (bytes memory reasonBytes) { (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes); MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } } } /// @dev `account` is expected to be `msg.sender` from the source chain, as /// MultichainOrderRouter would use it to validate the signature. function _handleSetTraderReferralCode( address account, uint256 srcChainId, ActionType actionType, bytes memory actionData ) private { ( IRelayUtils.RelayParams memory relayParams, bytes32 referralCode ) = abi.decode(actionData, (IRelayUtils.RelayParams, bytes32)); uint256 estimatedGasLimit = GasUtils.estimateSetTraderReferralCodeGasLimit(dataStore); _validateGasLeft(estimatedGasLimit); try multichainOrderRouter.setTraderReferralCode(relayParams, account, srcChainId, referralCode) { } catch Error(string memory reason) { MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } catch (bytes memory reasonBytes) { (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes); MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } } /// @dev `account` is expected to be `msg.sender` from the source chain, as /// MultichainOrderRouter would use it to validate the signature. function _handleRegisterCode( address account, uint256 srcChainId, ActionType actionType, bytes memory actionData ) private { ( IRelayUtils.RelayParams memory relayParams, bytes32 referralCode ) = abi.decode(actionData, (IRelayUtils.RelayParams, bytes32)); uint256 estimatedGasLimit = GasUtils.estimateRegisterCodeGasLimit(dataStore); _validateGasLeft(estimatedGasLimit); try multichainOrderRouter.registerCode(relayParams, account, srcChainId, referralCode) { } catch Error(string memory reason) { MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } catch (bytes memory reasonBytes) { (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes); MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } } function _handleWithdrawal( address account, uint256 srcChainId, ActionType actionType, bytes memory actionData ) private { ( IRelayUtils.RelayParams memory relayParams, IRelayUtils.TransferRequests memory transferRequests, IWithdrawalUtils.CreateWithdrawalParams memory withdrawalParams ) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IWithdrawalUtils.CreateWithdrawalParams)); if (_areValidTransferRequests(transferRequests)) { uint256 estimatedGasLimit = GasUtils.estimateCreateWithdrawalGasLimit(dataStore); _validateGasLeft(estimatedGasLimit); try multichainGmRouter.createWithdrawal( relayParams, account, srcChainId, transferRequests, withdrawalParams ) { } catch Error(string memory reason) { MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } catch (bytes memory reasonBytes) { (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes); MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } } } function _handleGlvWithdrawal( address account, uint256 srcChainId, ActionType actionType, bytes memory actionData ) private { ( IRelayUtils.RelayParams memory relayParams, IRelayUtils.TransferRequests memory transferRequests, IGlvWithdrawalUtils.CreateGlvWithdrawalParams memory glvWithdrawalParams ) = abi.decode(actionData, (IRelayUtils.RelayParams, IRelayUtils.TransferRequests, IGlvWithdrawalUtils.CreateGlvWithdrawalParams)); if (_areValidTransferRequests(transferRequests)) { uint256 estimatedGasLimit = GasUtils.estimateCreateGlvWithdrawalGasLimit(dataStore); _validateGasLeft(estimatedGasLimit); try multichainGlvRouter.createGlvWithdrawal( relayParams, account, srcChainId, transferRequests, glvWithdrawalParams ) { } catch Error(string memory reason) { MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } catch (bytes memory reasonBytes) { (string memory reason, /* bool hasRevertMessage */) = ErrorUtils.getRevertMessage(reasonBytes); MultichainEventUtils.emitMultichainBridgeActionFailed(eventEmitter, address(this), account, srcChainId, uint256(actionType), reason); } } } function _validateGasLeft(uint256 estimatedGasLimit) internal view { uint256 gas = gasleft(); if (gas < estimatedGasLimit) { revert Errors.InsufficientGasLeft(gas, estimatedGasLimit); } } /** * @notice Withdraws tokens which have been locked in this contract due to potential errors in lzCompose (e.g. incorrect message format). * @dev Callable through the timelock contract. * @param token The address of the token to withdraw. * @param receiver The address receiving the withdrawn tokens. * @param amount The amount of tokens to withdraw. */ function withdrawTokens(address token, address receiver, uint256 amount) external onlyController { if (amount == 0) { revert Errors.EmptyWithdrawalAmount(); } if (token == address(0)) { TokenUtils.sendNativeToken(dataStore, receiver, amount); } else { TokenUtils.transfer(dataStore, token, receiver, amount); } } /// @dev Accept ETH from StargatePoolNative and when unwrapping WNT receive() external payable {} }
違いを見つける