Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: update EVM Executor reference implementation #104

Merged
merged 1 commit into from
Oct 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 125 additions & 6 deletions packages/layerzero-v2/evm/messagelib/contracts/Executor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,58 @@ pragma solidity ^0.8.20;
import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import { Proxied } from "hardhat-deploy/solc_0.8/proxy/Proxied.sol";

import { ILayerZeroEndpointV2, Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { Origin } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { PacketV1Codec } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/PacketV1Codec.sol";

import { IUltraLightNode301 } from "./uln/uln301/interfaces/IUltraLightNode301.sol";
import { IExecutor } from "./interfaces/IExecutor.sol";
import { IExecutorFeeLib } from "./interfaces/IExecutorFeeLib.sol";
import { WorkerUpgradeable } from "./upgradeable/WorkerUpgradeable.sol";

interface ILayerZeroEndpointV2 {
function eid() external view returns (uint32);

function lzReceive(
Origin calldata _origin,
address _receiver,
bytes32 _guid,
bytes calldata _message,
bytes calldata _extraData
) external payable;

function lzReceiveAlert(
Origin calldata _origin,
address _receiver,
bytes32 _guid,
uint256 _gas,
uint256 _value,
bytes calldata _message,
bytes calldata _extraData,
bytes calldata _reason
) external;

function lzCompose(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
bytes calldata _message,
bytes calldata _extraData
) external payable;

function lzComposeAlert(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
uint256 _gas,
uint256 _value,
bytes calldata _message,
bytes calldata _extraData,
bytes calldata _reason
) external;
}

contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IExecutor {
using PacketV1Codec for bytes;

Expand Down Expand Up @@ -49,10 +93,11 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
for (uint256 i = 0; i < _params.length; i++) {
DstConfigParam memory param = _params[i];
dstConfig[param.dstEid] = DstConfig(
param.baseGas,
param.lzReceiveBaseGas,
param.multiplierBps,
param.floorMarginUSD,
param.nativeCap
param.nativeCap,
param.lzComposeBaseGas
);
}
emit DstConfigSet(_params);
Expand Down Expand Up @@ -83,6 +128,66 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
IUltraLightNode301(receiveUln301).commitVerification(_packet, _gasLimit);
}

function execute302(ExecutionParams calldata _executionParams) external payable onlyRole(ADMIN_ROLE) nonReentrant {
try
ILayerZeroEndpointV2(endpoint).lzReceive{ value: msg.value, gas: _executionParams.gasLimit }(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.message,
_executionParams.extraData
)
{
// do nothing
} catch (bytes memory reason) {
ILayerZeroEndpointV2(endpoint).lzReceiveAlert(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.gasLimit,
msg.value,
_executionParams.message,
_executionParams.extraData,
reason
);
}
}

function compose302(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
bytes calldata _message,
bytes calldata _extraData,
uint256 _gasLimit
) external payable onlyRole(ADMIN_ROLE) nonReentrant {
try
ILayerZeroEndpointV2(endpoint).lzCompose{ value: msg.value, gas: _gasLimit }(
_from,
_to,
_guid,
_index,
_message,
_extraData
)
{
// do nothing
} catch (bytes memory reason) {
ILayerZeroEndpointV2(endpoint).lzComposeAlert(
_from,
_to,
_guid,
_index,
_gasLimit,
msg.value,
_message,
_extraData,
reason
);
}
}

function nativeDropAndExecute302(
NativeDropParams[] calldata _nativeDropParams,
uint256 _nativeDropGasLimit,
Expand All @@ -97,14 +202,28 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
);

uint256 value = msg.value - spent;
// ignore the execution result
try
ILayerZeroEndpointV2(endpoint).lzReceive{ value: value, gas: _executionParams.gasLimit }(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.message,
_executionParams.extraData
);
)
{
// do nothing
} catch (bytes memory reason) {
ILayerZeroEndpointV2(endpoint).lzReceiveAlert(
_executionParams.origin,
_executionParams.receiver,
_executionParams.guid,
_executionParams.gasLimit,
value,
_executionParams.message,
_executionParams.extraData,
reason
);
}
}

// --- Message Lib ---
Expand All @@ -113,7 +232,7 @@ contract Executor is WorkerUpgradeable, ReentrancyGuardUpgradeable, Proxied, IEx
address _sender,
uint256 _calldataSize,
bytes calldata _options
) external onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_sender) returns (uint256 fee) {
) external onlyRole(MESSAGE_LIB_ROLE) onlyAcl(_sender) whenNotPaused returns (uint256 fee) {
IExecutorFeeLib.FeeParams memory params = IExecutorFeeLib.FeeParams(
priceFeed,
_dstEid,
Expand Down
72 changes: 30 additions & 42 deletions packages/layerzero-v2/evm/messagelib/contracts/ExecutorFeeLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
IExecutor.DstConfig calldata _dstConfig,
bytes calldata _options
) external returns (uint256 fee) {
if (_dstConfig.baseGas == 0) revert Executor_EidNotSupported(_params.dstEid);
if (_dstConfig.lzReceiveBaseGas == 0) revert Executor_EidNotSupported(_params.dstEid);

(uint256 totalDstAmount, uint256 totalGas) = _decodeExecutorOptions(
_isV1Eid(_params.dstEid),
_dstConfig.baseGas,
_dstConfig.lzReceiveBaseGas,
_dstConfig.lzComposeBaseGas,
_dstConfig.nativeCap,
_options
);
Expand All @@ -49,19 +50,10 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
uint128 nativePriceUSD
) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeOnSend(_params.dstEid, _params.calldataSize, totalGas);

fee = _applyPremiumToGas(
totalGasFee,
_dstConfig.multiplierBps,
_params.defaultMultiplierBps,
_dstConfig.floorMarginUSD,
nativePriceUSD
);
fee += _convertAndApplyPremiumToValue(
totalDstAmount,
priceRatio,
priceRatioDenominator,
_params.defaultMultiplierBps
);
uint16 multiplierBps = _dstConfig.multiplierBps == 0 ? _params.defaultMultiplierBps : _dstConfig.multiplierBps;

fee = _applyPremiumToGas(totalGasFee, multiplierBps, _dstConfig.floorMarginUSD, nativePriceUSD);
fee += _convertAndApplyPremiumToValue(totalDstAmount, priceRatio, priceRatioDenominator, multiplierBps);
}

// ================================ View ================================
Expand All @@ -70,11 +62,12 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
IExecutor.DstConfig calldata _dstConfig,
bytes calldata _options
) external view returns (uint256 fee) {
if (_dstConfig.baseGas == 0) revert Executor_EidNotSupported(_params.dstEid);
if (_dstConfig.lzReceiveBaseGas == 0) revert Executor_EidNotSupported(_params.dstEid);

(uint256 totalDstAmount, uint256 totalGas) = _decodeExecutorOptions(
_isV1Eid(_params.dstEid),
_dstConfig.baseGas,
_dstConfig.lzReceiveBaseGas,
_dstConfig.lzComposeBaseGas,
_dstConfig.nativeCap,
_options
);
Expand All @@ -86,26 +79,18 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
uint128 nativePriceUSD
) = ILayerZeroPriceFeed(_params.priceFeed).estimateFeeByEid(_params.dstEid, _params.calldataSize, totalGas);

fee = _applyPremiumToGas(
totalGasFee,
_dstConfig.multiplierBps,
_params.defaultMultiplierBps,
_dstConfig.floorMarginUSD,
nativePriceUSD
);
fee += _convertAndApplyPremiumToValue(
totalDstAmount,
priceRatio,
priceRatioDenominator,
_params.defaultMultiplierBps
);
uint16 multiplierBps = _dstConfig.multiplierBps == 0 ? _params.defaultMultiplierBps : _dstConfig.multiplierBps;

fee = _applyPremiumToGas(totalGasFee, multiplierBps, _dstConfig.floorMarginUSD, nativePriceUSD);
fee += _convertAndApplyPremiumToValue(totalDstAmount, priceRatio, priceRatioDenominator, multiplierBps);
}

// ================================ Internal ================================
// @dev decode executor options into dstAmount and totalGas
function _decodeExecutorOptions(
bool _v1Eid,
uint64 _baseGas,
uint64 _lzReceiveBaseGas,
uint64 _lzComposeBaseGas,
uint128 _nativeCap,
bytes calldata _options
) internal pure returns (uint256 dstAmount, uint256 totalGas) {
Expand All @@ -115,8 +100,9 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {

uint256 cursor = 0;
bool ordered = false;
totalGas = _baseGas;
totalGas = _lzReceiveBaseGas; // lz receive only called once

bool v1Eid = _v1Eid; // stack too deep
uint256 lzReceiveGas;
while (cursor < _options.length) {
(uint8 optionType, bytes calldata option, uint256 newCursor) = _options.nextExecutorOption(cursor);
Expand All @@ -126,7 +112,7 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
(uint128 gas, uint128 value) = ExecutorOptions.decodeLzReceiveOption(option);

// endpoint v1 does not support lzReceive with value
if (_v1Eid && value > 0) revert Executor_UnsupportedOptionType(optionType);
if (v1Eid && value > 0) revert Executor_UnsupportedOptionType(optionType);

dstAmount += value;
lzReceiveGas += gas;
Expand All @@ -135,11 +121,16 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
dstAmount += nativeDropAmount;
} else if (optionType == ExecutorOptions.OPTION_TYPE_LZCOMPOSE) {
// endpoint v1 does not support lzCompose
if (_v1Eid) revert Executor_UnsupportedOptionType(optionType);
if (v1Eid) revert Executor_UnsupportedOptionType(optionType);

(, uint128 gas, uint128 value) = ExecutorOptions.decodeLzComposeOption(option);
if (gas == 0) revert Executor_ZeroLzComposeGasProvided();

dstAmount += value;
totalGas += gas;
// lz compose can be called multiple times, based on unique index
// to simplify the quoting, we add lzComposeBaseGas for each lzComposeOption received
// if the same index has multiple compose options, the gas will be added multiple times
totalGas += gas + _lzComposeBaseGas;
} else if (optionType == ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION) {
ordered = true;
} else {
Expand All @@ -158,14 +149,11 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {

function _applyPremiumToGas(
uint256 _fee,
uint16 _bps,
uint16 _defaultBps,
uint16 _multiplierBps,
uint128 _marginUSD,
uint128 _nativePriceUSD
) internal view returns (uint256) {
uint16 multiplierBps = _bps == 0 ? _defaultBps : _bps;

uint256 feeWithMultiplier = (_fee * multiplierBps) / 10000;
uint256 feeWithMultiplier = (_fee * _multiplierBps) / 10000;

if (_nativePriceUSD == 0 || _marginUSD == 0) {
return feeWithMultiplier;
Expand All @@ -179,10 +167,10 @@ contract ExecutorFeeLib is Ownable, IExecutorFeeLib {
uint256 _value,
uint128 _ratio,
uint128 _denom,
uint16 _defaultBps
uint16 _multiplierBps
) internal pure returns (uint256 fee) {
if (_value > 0) {
fee = (((_value * _ratio) / _denom) * _defaultBps) / 10000;
fee = (((_value * _ratio) / _denom) * _multiplierBps) / 10000;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,19 @@ import { ILayerZeroExecutor } from "./ILayerZeroExecutor.sol";
interface IExecutor is IWorker, ILayerZeroExecutor {
struct DstConfigParam {
uint32 dstEid;
uint64 baseGas;
uint64 lzReceiveBaseGas;
uint64 lzComposeBaseGas;
uint16 multiplierBps;
uint128 floorMarginUSD;
uint128 nativeCap;
}

struct DstConfig {
uint64 baseGas; // for verifying / fixed calldata overhead
uint64 lzReceiveBaseGas;
uint16 multiplierBps;
uint128 floorMarginUSD; // uses priceFeed PRICE_RATIO_DENOMINATOR
uint128 nativeCap;
uint64 lzComposeBaseGas;
}

struct ExecutionParams {
Expand All @@ -40,5 +42,5 @@ interface IExecutor is IWorker, ILayerZeroExecutor {
event DstConfigSet(DstConfigParam[] params);
event NativeDropApplied(Origin origin, uint32 dstEid, address oapp, NativeDropParams[] params, bool[] success);

function dstConfig(uint32 _dstEid) external view returns (uint64, uint16, uint128, uint128);
function dstConfig(uint32 _dstEid) external view returns (uint64, uint16, uint128, uint128, uint64);
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface IExecutorFeeLib {
error Executor_UnsupportedOptionType(uint8 optionType);
error Executor_InvalidExecutorOptions(uint256 cursor);
error Executor_ZeroLzReceiveGasProvided();
error Executor_ZeroLzComposeGasProvided();
error Executor_EidNotSupported(uint32 eid);

function getFeeOnSend(
Expand Down
Loading
Loading