Diff
checker
Texto
Texto
Imágenes
Documentos
Excel
Carpetas
Legal
Enterprise
Aplicación de escritorio
Precios
Iniciar sesión
Descargar Diffchecker Desktop
Comparar texto
Encuentra la diferencia entre dos archivos de texto
Herramientas
Historial
Visualización
Vista
Dividido
Unificado
Editor live
Ocultar espacios en blanco
Ocultar sin cambios
Sin ajuste de línea
Nivel de detalle
Inteligente
Sintaxis
Ninguno
Cambiar apariencia
Procesamiento
Ignorar
Transformar texto
Ir al primer cambio
Editar entrada
Diffchecker Desktop
La forma más segura de usar Diffchecker. ¡Obtén la app de Diffchecker Desktop: tus diffs nunca salen de tu computadora!
Obtener Desktop
LayerZeroProvider Diff
Creado
hace 9 meses
El diff nunca expira
Borrar
Exportar
Compartir
Explicar
28 eliminaciones
Líneas
Total
Eliminado
Caracteres
Total
Eliminado
Para continuar usando esta función, actualice a
Diff
checker
Pro
Ver precios
526 líneas
Copiar todo
69 adiciones
Líneas
Total
Añadido
Caracteres
Total
Añadido
Para continuar usando esta función, actualice a
Diff
checker
Pro
Ver precios
566 líneas
Copiar todo
// 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();
Copiar
Copiado
Copiar
Copiado
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
);
);
Copiar
Copiado
Copiar
Copiado
// 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) {
Copiar
Copiado
Copiar
Copiado
(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);
Copiar
Copiado
Copiar
Copiado
} 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;
}
}
Copiar
Copiado
Copiar
Copiado
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);
}
}
}
}
Copiar
Copiado
Copiar
Copiado
/// @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 {
Copiar
Copiado
Copiar
Copiado
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 {}
}
Diferencias guardadas
Texto original
Abrir archivo
// 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 {} }
Texto modificado
Abrir archivo
// 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 {} }
Encontrar la diferencia